Create the following two files and store them in known locations.
-For example /etc/ovs-ifup and /etc/ifdown
+For example /etc/ovs-ifup and /etc/ovs-ifdown
/etc/ovs-ifup
--------------------------------------------------------------------
configuration database, ovsdb-server. Each machine on which Open
vSwitch is installed should run its own copy of ovsdb-server.
Configure it to use the database you created during step 7 of
-installation, above, and to listen on a Unix domain socket, e.g.:
-
- % ovsdb-server /usr/local/etc/ovs-vswitchd.conf.db --remote=punix:/usr/local/var/run/openvswitch/db.sock
+installation, above, to listen on a Unix domain socket, to connect to
+any managers specified in the database itself, and to use the SSL
+configuration in the database:
+
+ % ovsdb-server /usr/local/etc/ovs-vswitchd.conf.db \
+ --remote=punix:/usr/local/var/run/openvswitch/db.sock \
+ --remote=db:Open_vSwitch,managers \
+ --private-key=db:SSL,private_key \
+ --certificate=db:SSL,certificate \
+ --bootstrap-ca-cert=db:SSL,ca_cert
Then initialize the database using ovs-vsctl. This is only
necessary the first time after you create the database with
OVS_ENABLE_OPTION([-Wmissing-field-initializers])
OVS_ENABLE_OPTION([-Wno-override-init])
OVS_CONDITIONAL_CC_OPTION([-Wno-unused], [HAVE_WNO_UNUSED])
+OVS_CONDITIONAL_CC_OPTION([-Wno-unused-parameter], [HAVE_WNO_UNUSED_PARAMETER])
AC_ARG_VAR(KARCH, [Kernel Architecture String])
AC_SUBST(KARCH)
static struct sk_buff *make_writable(struct sk_buff *skb, unsigned min_headroom, gfp_t gfp)
{
- if (skb_shared(skb) || skb_cloned(skb)) {
+ if (skb_cloned(skb)) {
struct sk_buff *nskb;
unsigned headroom = max(min_headroom, skb_headroom(skb));
OVS_CB(skb)->tun_id = 0;
for (; n_actions > 0; a++, n_actions--) {
- WARN_ON_ONCE(skb_shared(skb));
if (prev_port != -1) {
do_output(dp, skb_clone(skb, gfp), prev_port);
prev_port = -1;
static struct datapath *dps[XFLOW_MAX];
static DEFINE_MUTEX(dp_mutex);
-/* Number of milliseconds between runs of the maintenance thread. */
-#define MAINT_SLEEP_MSECS 1000
+/* We limit the number of times that we pass into dp_process_received_packet()
+ * to avoid blowing out the stack in the event that we have a loop. */
+struct loop_counter {
+ int count; /* Count. */
+ bool looping; /* Loop detected? */
+};
+
+#define DP_MAX_LOOPS 5
+
+/* We use a separate counter for each CPU for both interrupt and non-interrupt
+ * context in order to keep the limit deterministic for a given packet. */
+struct percpu_loop_counters {
+ struct loop_counter counters[2];
+};
+
+static DEFINE_PER_CPU(struct percpu_loop_counters, dp_loop_counters);
static int new_dp_port(struct datapath *, struct xflow_port *, int port_no);
p->port_no = port_no;
p->dp = dp;
+ p->vport = vport;
atomic_set(&p->sflow_pool, 0);
err = vport_attach(vport, p);
return err;
}
+static void suppress_loop(struct datapath *dp, struct sw_flow_actions *actions)
+{
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: flow looped %d times, dropping\n",
+ dp_name(dp), DP_MAX_LOOPS);
+ actions->n_actions = 0;
+}
+
/* Must be called with rcu_read_lock. */
void dp_process_received_packet(struct dp_port *p, struct sk_buff *skb)
{
int stats_counter_off;
struct xflow_key key;
struct tbl_node *flow_node;
-
- WARN_ON_ONCE(skb_shared(skb));
- skb_warn_if_lro(skb);
+ struct sw_flow *flow;
+ struct sw_flow_actions *acts;
+ struct loop_counter *loop;
OVS_CB(skb)->dp_port = p;
+ /* Extract flow from 'skb' into 'key'. */
if (flow_extract(skb, p ? p->port_no : XFLOWP_NONE, &key)) {
if (dp->drop_frags) {
kfree_skb(skb);
}
}
+ /* Look up flow. */
flow_node = tbl_lookup(rcu_dereference(dp->table), &key, flow_hash(&key), flow_cmp);
- if (flow_node) {
- struct sw_flow *flow = flow_cast(flow_node);
- struct sw_flow_actions *acts = rcu_dereference(flow->sf_acts);
- flow_used(flow, skb);
- execute_actions(dp, skb, &key, acts->actions, acts->n_actions,
- GFP_ATOMIC);
- stats_counter_off = offsetof(struct dp_stats_percpu, n_hit);
- } else {
- stats_counter_off = offsetof(struct dp_stats_percpu, n_missed);
+ if (unlikely(!flow_node)) {
dp_output_control(dp, skb, _XFLOWL_MISS_NR, OVS_CB(skb)->tun_id);
+ stats_counter_off = offsetof(struct dp_stats_percpu, n_missed);
+ goto out;
}
+ flow = flow_cast(flow_node);
+ flow_used(flow, skb);
+
+ acts = rcu_dereference(flow->sf_acts);
+
+ /* Check whether we've looped too much. */
+ loop = &get_cpu_var(dp_loop_counters).counters[!!in_interrupt()];
+ if (unlikely(++loop->count > DP_MAX_LOOPS))
+ loop->looping = true;
+ if (unlikely(loop->looping)) {
+ suppress_loop(dp, acts);
+ goto out_loop;
+ }
+
+ /* Execute actions. */
+ execute_actions(dp, skb, &key, acts->actions, acts->n_actions, GFP_ATOMIC);
+ stats_counter_off = offsetof(struct dp_stats_percpu, n_hit);
+
+ /* Check whether sub-actions looped too much. */
+ if (unlikely(loop->looping))
+ suppress_loop(dp, acts);
+
+out_loop:
+ /* Decrement loop counter. */
+ if (!--loop->count)
+ loop->looping = false;
+ put_cpu_var(dp_loop_counters);
+
out:
+ /* Update datapath statistics. */
local_bh_disable();
stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id());
(*(u64 *)((u8 *)stats + stats_counter_off))++;
return ERR_PTR(error);
}
-static void get_stats(struct sw_flow *flow, struct xflow_flow_stats *stats)
+static struct timespec get_time_offset(void)
+{
+ struct timespec now_mono, now_jiffies;
+
+ ktime_get_ts(&now_mono);
+ jiffies_to_timespec(jiffies, &now_jiffies);
+ return timespec_sub(now_mono, now_jiffies);
+}
+
+static void get_stats(struct sw_flow *flow, struct xflow_flow_stats *stats,
+ struct timespec time_offset)
{
- if (flow->used.tv_sec) {
- stats->used_sec = flow->used.tv_sec;
- stats->used_nsec = flow->used.tv_nsec;
+ if (flow->used) {
+ struct timespec flow_ts, used;
+
+ jiffies_to_timespec(flow->used, &flow_ts);
+ set_normalized_timespec(&used, flow_ts.tv_sec + time_offset.tv_sec,
+ flow_ts.tv_nsec + time_offset.tv_nsec);
+
+ stats->used_sec = used.tv_sec;
+ stats->used_nsec = used.tv_nsec;
} else {
stats->used_sec = 0;
stats->used_nsec = 0;
}
+
stats->n_packets = flow->packet_count;
stats->n_bytes = flow->byte_count;
- stats->ip_tos = flow->ip_tos;
+ stats->reserved = 0;
stats->tcp_flags = flow->tcp_flags;
stats->error = 0;
}
static void clear_stats(struct sw_flow *flow)
{
- flow->used.tv_sec = flow->used.tv_nsec = 0;
+ flow->used = 0;
flow->tcp_flags = 0;
- flow->ip_tos = 0;
flow->packet_count = 0;
flow->byte_count = 0;
}
/* Fetch stats, then clear them if necessary. */
spin_lock_bh(&flow->lock);
- get_stats(flow, stats);
+ get_stats(flow, stats, get_time_offset());
if (uf->flags & XFLOWPF_ZERO_STATS)
clear_stats(flow);
spin_unlock_bh(&flow->lock);
}
static int do_answer_query(struct sw_flow *flow, u32 query_flags,
+ struct timespec time_offset,
struct xflow_flow_stats __user *ustats,
union xflow_action __user *actions,
u32 __user *n_actionsp)
u32 n_actions;
spin_lock_bh(&flow->lock);
- get_stats(flow, &stats);
+ get_stats(flow, &stats, time_offset);
if (query_flags & XFLOWFF_ZERO_TCP_FLAGS)
flow->tcp_flags = 0;
}
static int answer_query(struct sw_flow *flow, u32 query_flags,
+ struct timespec time_offset,
struct xflow_flow __user *ufp)
{
union xflow_action *actions;
if (get_user(actions, &ufp->actions))
return -EFAULT;
- return do_answer_query(flow, query_flags,
+ return do_answer_query(flow, query_flags, time_offset,
&ufp->stats, actions, &ufp->n_actions);
}
if (IS_ERR(flow))
return PTR_ERR(flow);
- error = answer_query(flow, 0, ufp);
+ error = answer_query(flow, 0, get_time_offset(), ufp);
flow_deferred_free(flow);
return error;
}
static int do_query_flows(struct datapath *dp, const struct xflow_flowvec *flowvec)
{
struct tbl *table = rcu_dereference(dp->table);
+ struct timespec time_offset;
u32 i;
+ time_offset = get_time_offset();
+
for (i = 0; i < flowvec->n_flows; i++) {
struct xflow_flow __user *ufp = &flowvec->flows[i];
struct xflow_flow uf;
if (!flow_node)
error = put_user(ENOENT, &ufp->stats.error);
else
- error = answer_query(flow_cast(flow_node), uf.flags, ufp);
+ error = answer_query(flow_cast(flow_node), uf.flags, time_offset, ufp);
if (error)
return -EFAULT;
}
struct xflow_flow __user *uflows;
u32 n_flows;
u32 listed_flows;
+ struct timespec time_offset;
};
static int list_flow(struct tbl_node *node, void *cbdata_)
if (copy_to_user(&ufp->key, &flow->key, sizeof flow->key))
return -EFAULT;
- error = answer_query(flow, 0, ufp);
+ error = answer_query(flow, 0, cbdata->time_offset, ufp);
if (error)
return error;
cbdata.uflows = flowvec->flows;
cbdata.n_flows = flowvec->n_flows;
cbdata.listed_flows = 0;
+ cbdata.time_offset = get_time_offset();
+
error = tbl_foreach(rcu_dereference(dp->table), list_flow, &cbdata);
return error ? error : cbdata.listed_flows;
}
}
static int compat_answer_query(struct sw_flow *flow, u32 query_flags,
+ struct timespec time_offset,
struct compat_xflow_flow __user *ufp)
{
compat_uptr_t actions;
if (get_user(actions, &ufp->actions))
return -EFAULT;
- return do_answer_query(flow, query_flags, &ufp->stats,
+ return do_answer_query(flow, query_flags, time_offset, &ufp->stats,
compat_ptr(actions), &ufp->n_actions);
}
if (IS_ERR(flow))
return PTR_ERR(flow);
- error = compat_answer_query(flow, 0, ufp);
+ error = compat_answer_query(flow, 0, get_time_offset(), ufp);
flow_deferred_free(flow);
return error;
}
static int compat_query_flows(struct datapath *dp, struct compat_xflow_flow *flows, u32 n_flows)
{
struct tbl *table = rcu_dereference(dp->table);
+ struct timespec time_offset;
u32 i;
+ time_offset = get_time_offset();
+
for (i = 0; i < n_flows; i++) {
struct compat_xflow_flow __user *ufp = &flows[i];
struct xflow_flow uf;
if (!flow_node)
error = put_user(ENOENT, &ufp->stats.error);
else
- error = compat_answer_query(flow_cast(flow_node), uf.flags, ufp);
+ error = compat_answer_query(flow_cast(flow_node), uf.flags, time_offset, ufp);
if (error)
return -EFAULT;
}
struct compat_xflow_flow __user *uflows;
u32 n_flows;
u32 listed_flows;
+ struct timespec time_offset;
};
static int compat_list_flow(struct tbl_node *node, void *cbdata_)
if (copy_to_user(&ufp->key, &flow->key, sizeof flow->key))
return -EFAULT;
- error = compat_answer_query(flow, 0, ufp);
+ error = compat_answer_query(flow, 0, cbdata->time_offset, ufp);
if (error)
return error;
cbdata.uflows = flows;
cbdata.n_flows = n_flows;
cbdata.listed_flows = 0;
+ cbdata.time_offset = get_time_offset();
+
error = tbl_foreach(rcu_dereference(dp->table), compat_list_flow, &cbdata);
return error ? error : cbdata.listed_flows;
}
if (is_internal_dev(dev))
vport = internal_dev_get_vport(dev);
- else {
+ else
vport = netdev_get_vport(dev);
- if (!vport)
- return NOTIFY_DONE;
- }
+ if (!vport)
+ return NOTIFY_DONE;
p = vport_get_dp_port(vport);
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
-#include <linux/spinlock.h>
-#include <linux/times.h>
#include <linux/version.h>
#include "dp_sysfs.h"
#include "vport-internal_dev.h"
#ifdef CONFIG_SYSFS
-#define to_dev(obj) container_of(obj, struct device, kobj)
/* Hack to attempt to build on more platforms. */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
struct datapath *sysfs_get_dp(struct net_device *netdev)
{
- return vport_get_dp_port(internal_dev_get_vport(netdev))->dp;
-}
+ struct vport *vport = internal_dev_get_vport(netdev);
+ struct dp_port *dp_port;
+
+ if (!vport)
+ return NULL;
+
+ dp_port = vport_get_dp_port(vport);
+ if (!dp_port)
+ return NULL;
+ return dp_port->dp;
+}
/*
* Common code for storing bridge parameters.
*/
const char *buf, size_t len,
void (*set)(struct datapath *, unsigned long))
{
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
char *endp;
unsigned long val;
+ ssize_t result = len;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (endp == buf)
return -EINVAL;
-#if 0
- spin_lock_bh(&br->lock);
- (*set)(br, val);
- spin_unlock_bh(&br->lock);
-#else
/* xxx We use a default value of 0 for all fields. If the caller is
* xxx attempting to set the value to our default, just silently
* xxx ignore the request.
*/
if (val != 0) {
- printk("%s: xxx writing dp parms not supported yet!\n",
- dp_name(dp));
+ struct datapath *dp;
+
+ rcu_read_lock();
+
+ dp = sysfs_get_dp(to_net_dev(d));
+ if (dp)
+ printk("%s: xxx writing dp parms not supported yet!\n",
+ dp_name(dp));
+ else
+ result = -ENODEV;
+
+ rcu_read_unlock();
}
-#endif
- return len;
+
+ return result;
}
static ssize_t show_forward_delay(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static void set_forward_delay(struct datapath *dp, unsigned long val)
{
-#if 0
- unsigned long delay = clock_t_to_jiffies(val);
- br->forward_delay = delay;
- if (br_is_root_bridge(br))
- br->bridge_forward_delay = delay;
-#else
printk("%s: xxx attempt to set_forward_delay()\n", dp_name(dp));
-#endif
}
static ssize_t store_forward_delay(DEVICE_PARAMS,
static ssize_t show_hello_time(DEVICE_PARAMS, char *buf)
{
-#if 0
- return sprintf(buf, "%lu\n",
- jiffies_to_clock_t(to_bridge(d)->hello_time));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static void set_hello_time(struct datapath *dp, unsigned long val)
{
-#if 0
- unsigned long t = clock_t_to_jiffies(val);
- br->hello_time = t;
- if (br_is_root_bridge(br))
- br->bridge_hello_time = t;
-#else
printk("%s: xxx attempt to set_hello_time()\n", dp_name(dp));
-#endif
}
static ssize_t store_hello_time(DEVICE_PARAMS,
static ssize_t show_max_age(DEVICE_PARAMS, char *buf)
{
-#if 0
- return sprintf(buf, "%lu\n",
- jiffies_to_clock_t(to_bridge(d)->max_age));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static void set_max_age(struct datapath *dp, unsigned long val)
{
-#if 0
- unsigned long t = clock_t_to_jiffies(val);
- br->max_age = t;
- if (br_is_root_bridge(br))
- br->bridge_max_age = t;
-#else
printk("%s: xxx attempt to set_max_age()\n", dp_name(dp));
-#endif
}
static ssize_t store_max_age(DEVICE_PARAMS,
static ssize_t show_ageing_time(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static void set_ageing_time(struct datapath *dp, unsigned long val)
{
-#if 0
- br->ageing_time = clock_t_to_jiffies(val);
-#else
printk("%s: xxx attempt to set_ageing_time()\n", dp_name(dp));
-#endif
}
static ssize_t store_ageing_time(DEVICE_PARAMS,
static ssize_t show_stp_state(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%d\n", br->stp_enabled);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
const char *buf,
size_t len)
{
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
-#if 0
- char *endp;
- unsigned long val;
+ struct datapath *dp;
+ ssize_t result = len;
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
+ rcu_read_lock();
- val = simple_strtoul(buf, &endp, 0);
- if (endp == buf)
- return -EINVAL;
+ dp = sysfs_get_dp(to_net_dev(d));
+ if (dp)
+ printk("%s: xxx attempt to set_stp_state()\n", dp_name(dp));
+ else
+ result = -ENODEV;
- rtnl_lock();
- br_stp_set_enabled(br, val);
- rtnl_unlock();
-#else
- printk("%s: xxx attempt to set_stp_state()\n", dp_name(dp));
-#endif
+ rcu_read_unlock();
- return len;
+ return result;
}
static INTERNAL_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
store_stp_state);
static ssize_t show_priority(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%d\n",
- (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static void set_priority(struct datapath *dp, unsigned long val)
{
-#if 0
- br_stp_set_bridge_priority(br, (u16) val);
-#else
printk("%s: xxx attempt to set_priority()\n", dp_name(dp));
-#endif
}
static ssize_t store_priority(DEVICE_PARAMS,
static ssize_t show_root_id(DEVICE_PARAMS, char *buf)
{
-#if 0
- return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
-#else
return sprintf(buf, "0000.010203040506\n");
-#endif
}
static INTERNAL_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf)
{
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- const unsigned char *addr = vport_get_addr(dp->ports[XFLOWP_LOCAL]->vport);
+ struct vport *vport;
+ ssize_t result;
+
+ rcu_read_lock();
+
+ vport = internal_dev_get_vport(to_net_dev(d));
+ if (vport) {
+ const unsigned char *addr;
+
+ addr = vport_get_addr(vport);
+ result = sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
+ 0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ } else
+ result = -ENODEV;
+
+ rcu_read_unlock();
- /* xxx Do we need a lock of some sort? */
- return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
- 0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ return result;
}
static INTERNAL_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
static ssize_t show_root_port(DEVICE_PARAMS, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", to_bridge(d)->root_port);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static INTERNAL_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
static ssize_t show_root_path_cost(DEVICE_PARAMS, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static INTERNAL_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
static ssize_t show_topology_change(DEVICE_PARAMS, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static INTERNAL_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
static ssize_t show_topology_change_detected(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%d\n", br->topology_change_detected);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static INTERNAL_DEVICE_ATTR(topology_change_detected, S_IRUGO,
show_topology_change_detected, NULL);
static ssize_t show_hello_timer(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static INTERNAL_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
static ssize_t show_tcn_timer(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static INTERNAL_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
static ssize_t show_topology_change_timer(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static INTERNAL_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
NULL);
static ssize_t show_gc_timer(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static INTERNAL_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
static ssize_t show_group_addr(DEVICE_PARAMS, char *buf)
{
-#if 0
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
- br->group_addr[0], br->group_addr[1],
- br->group_addr[2], br->group_addr[3],
- br->group_addr[4], br->group_addr[5]);
-#else
return sprintf(buf, "00:01:02:03:04:05\n");
-#endif
}
static ssize_t store_group_addr(DEVICE_PARAMS,
const char *buf, size_t len)
{
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
-#if 0
- unsigned new_addr[6];
- int i;
+ struct datapath *dp;
+ ssize_t result = len;
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
+ rcu_read_lock();
- if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
- &new_addr[0], &new_addr[1], &new_addr[2],
- &new_addr[3], &new_addr[4], &new_addr[5]) != 6)
- return -EINVAL;
+ dp = sysfs_get_dp(to_net_dev(d));
+ if (dp)
+ printk("%s: xxx attempt to store_group_addr()\n", dp_name(dp));
+ else
+ result = -ENODEV;
- /* Must be 01:80:c2:00:00:0X */
- for (i = 0; i < 5; i++)
- if (new_addr[i] != br_group_address[i])
- return -EINVAL;
-
- if (new_addr[5] & ~0xf)
- return -EINVAL;
+ rcu_read_unlock();
- if (new_addr[5] == 1 /* 802.3x Pause address */
- || new_addr[5] == 2 /* 802.3ad Slow protocols */
- || new_addr[5] == 3) /* 802.1X PAE address */
- return -EINVAL;
-
- spin_lock_bh(&br->lock);
- for (i = 0; i < 6; i++)
- br->group_addr[i] = new_addr[i];
- spin_unlock_bh(&br->lock);
-#else
- printk("%s: xxx attempt to store_group_addr()\n", dp_name(dp));
-#endif
- return len;
+ return result;
}
static INTERNAL_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
-#include <linux/spinlock.h>
#include "datapath.h"
#include "dp_sysfs.h"
static ssize_t show_path_cost(struct dp_port *p, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", p->path_cost);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static ssize_t store_path_cost(struct dp_port *p, unsigned long v)
{
-#if 0
- br_stp_set_path_cost(p, v);
-#endif
return 0;
}
static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
static ssize_t show_priority(struct dp_port *p, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", p->priority);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static ssize_t store_priority(struct dp_port *p, unsigned long v)
{
-#if 0
- if (v >= (1<<(16-BR_PORT_BITS)))
- return -ERANGE;
- br_stp_set_port_priority(p, v);
-#endif
return 0;
}
static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
static ssize_t show_designated_root(struct dp_port *p, char *buf)
{
-#if 0
- return br_show_bridge_id(buf, &p->designated_root);
-#else
return sprintf(buf, "0000.010203040506\n");
-#endif
}
static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL);
static ssize_t show_designated_bridge(struct dp_port *p, char *buf)
{
-#if 0
- return br_show_bridge_id(buf, &p->designated_bridge);
-#else
return sprintf(buf, "0000.060504030201\n");
-#endif
}
static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL);
static ssize_t show_designated_port(struct dp_port *p, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", p->designated_port);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL);
static ssize_t show_designated_cost(struct dp_port *p, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", p->designated_cost);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL);
static ssize_t show_port_id(struct dp_port *p, char *buf)
{
-#if 0
- return sprintf(buf, "0x%x\n", p->port_id);
-#else
return sprintf(buf, "0x%x\n", 0);
-#endif
}
static BRPORT_ATTR(port_id, S_IRUGO, show_port_id, NULL);
static ssize_t show_change_ack(struct dp_port *p, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", p->topology_change_ack);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL);
static ssize_t show_config_pending(struct dp_port *p, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", p->config_pending);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL);
static ssize_t show_port_state(struct dp_port *p, char *buf)
{
-#if 0
- return sprintf(buf, "%d\n", p->state);
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL);
static ssize_t show_message_age_timer(struct dp_port *p,
char *buf)
{
-#if 0
- return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL);
static ssize_t show_forward_delay_timer(struct dp_port *p,
char *buf)
{
-#if 0
- return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static BRPORT_ATTR(forward_delay_timer, S_IRUGO, show_forward_delay_timer, NULL);
static ssize_t show_hold_timer(struct dp_port *p,
char *buf)
{
-#if 0
- return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer));
-#else
return sprintf(buf, "%d\n", 0);
-#endif
}
static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
const char * buf, size_t count)
{
struct dp_port * p = to_brport(kobj);
-#if 0
- struct brport_attribute * brport_attr = to_brport_attr(attr);
- char *endp;
- unsigned long val;
-#endif
ssize_t ret = -EINVAL;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
-#if 0
- val = simple_strtoul(buf, &endp, 0);
- if (endp != buf) {
- rtnl_lock();
- if (p->dev && p->br && brport_attr->store) {
- spin_lock_bh(&p->br->lock);
- ret = brport_attr->store(p, val);
- spin_unlock_bh(&p->br->lock);
- if (ret == 0)
- ret = count;
- }
- rtnl_unlock();
- }
-#else
printk("%s: xxx writing port parms not supported yet!\n",
dp_name(p->dp));
-#endif
+
return ret;
}
#define TCP_FLAGS_OFFSET 13
#define TCP_FLAG_MASK 0x3f
-static inline struct ovs_tcphdr *ovs_tcp_hdr(const struct sk_buff *skb)
-{
- return (struct ovs_tcphdr *)skb_transport_header(skb);
-}
-
void flow_used(struct sw_flow *flow, struct sk_buff *skb)
{
u8 tcp_flags = 0;
- if (flow->key.dl_type == htons(ETH_P_IP) && iphdr_ok(skb)) {
- struct iphdr *nh = ip_hdr(skb);
- flow->ip_tos = nh->tos;
- if (flow->key.nw_proto == IPPROTO_TCP && tcphdr_ok(skb)) {
- u8 *tcp = (u8 *)tcp_hdr(skb);
- tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK;
- }
+ if (flow->key.dl_type == htons(ETH_P_IP) &&
+ flow->key.nw_proto == IPPROTO_TCP) {
+ u8 *tcp = (u8 *)tcp_hdr(skb);
+ tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK;
}
spin_lock_bh(&flow->lock);
- ktime_get_ts(&flow->used);
+ flow->used = jiffies;
flow->packet_count++;
flow->byte_count += skb->len;
flow->tcp_flags |= tcp_flags;
return retval;
}
-struct sw_flow *flow_cast(const struct tbl_node *node)
-{
- return container_of(node, struct sw_flow, tbl_node);
-}
-
u32 flow_hash(const struct xflow_key *key)
{
return jhash2((u32*)key, sizeof *key / sizeof(u32), hash_seed);
#include <linux/types.h>
#include <linux/rcupdate.h>
#include <linux/gfp.h>
+#include <linux/jiffies.h>
#include <linux/time.h>
#include "openvswitch/xflow.h"
struct xflow_key key;
struct sw_flow_actions *sf_acts;
- struct timespec used; /* Last used time. */
-
- u8 ip_tos; /* IP TOS value. */
-
spinlock_t lock; /* Lock for values below. */
+ unsigned long used; /* Last used time (in jiffies). */
u64 packet_count; /* Number of packets matched. */
u64 byte_count; /* Number of bytes matched. */
u8 tcp_flags; /* Union of seen TCP flags. */
int flow_extract(struct sk_buff *, u16 in_port, struct xflow_key *);
void flow_used(struct sw_flow *, struct sk_buff *);
-struct sw_flow *flow_cast(const struct tbl_node *);
u32 flow_hash(const struct xflow_key *key);
int flow_cmp(const struct tbl_node *, void *target);
void flow_free_tbl(struct tbl_node *);
int flow_init(void);
void flow_exit(void);
+static inline struct sw_flow *flow_cast(const struct tbl_node *node)
+{
+ return container_of(node, struct sw_flow, tbl_node);
+}
+
#endif /* flow.h */
/random32.c
/skbuff-openvswitch.c
/table.c
+/time.c
/tmp
/veth.c
/vport-generic.c
linux-2.6/compat-2.6/ip_output-openvswitch.c \
linux-2.6/compat-2.6/kmemdup.c \
linux-2.6/compat-2.6/random32.c \
- linux-2.6/compat-2.6/skbuff-openvswitch.c
+ linux-2.6/compat-2.6/skbuff-openvswitch.c \
+ linux-2.6/compat-2.6/time.c
openvswitch_headers += \
linux-2.6/compat-2.6/compat26.h \
linux-2.6/compat-2.6/include/asm-generic/bug.h \
#endif /* NETIF_F_LRO */
#endif /* HAVE_SKB_WARN_LRO */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
-static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev,
- unsigned int length)
-{
- struct sk_buff *skb = netdev_alloc_skb(dev, length + NET_IP_ALIGN);
-
- if (NET_IP_ALIGN && skb)
- skb_reserve(skb, NET_IP_ALIGN);
- return skb;
-}
-#endif /* kernel < 2.6.33 */
-
-
#endif
--- /dev/null
+#include <linux/time.h>
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+
+/* "set_normalized_timespec" is defined but not exported in kernels
+ * before 2.6.26. */
+
+/**
+ * set_normalized_timespec - set timespec sec and nsec parts and normalize
+ *
+ * @ts: pointer to timespec variable to be set
+ * @sec: seconds to set
+ * @nsec: nanoseconds to set
+ *
+ * Set seconds and nanoseconds field of a timespec variable and
+ * normalize to the timespec storage format
+ *
+ * Note: The tv_nsec part is always in the range of
+ * 0 <= tv_nsec < NSEC_PER_SEC
+ * For negative values only the tv_sec field is negative !
+ */
+void set_normalized_timespec(struct timespec *ts,
+ time_t sec, long nsec)
+{
+ while (nsec >= NSEC_PER_SEC) {
+ nsec -= NSEC_PER_SEC;
+ ++sec;
+ }
+ while (nsec < 0) {
+ nsec += NSEC_PER_SEC;
+ --sec;
+ }
+ ts->tv_sec = sec;
+ ts->tv_nsec = nsec;
+}
+
+#endif /* linux kernel < 2.6.26 */
#include "vport-netdev.h"
struct internal_dev {
- struct vport *vport;
+ struct vport *attached_vport, *vport;
struct net_device_stats stats;
};
/* Called with rcu_read_lock and bottom-halves disabled. */
static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
{
- struct vport *vport = internal_dev_get_vport(netdev);
+ struct internal_dev *internal_dev = internal_dev_priv(netdev);
+ struct vport *vport = rcu_dereference(internal_dev->vport);
/* We need our own clone. */
skb = skb_share_check(skb, GFP_ATOMIC);
- if (!skb) {
+ if (unlikely(!skb)) {
vport_record_error(vport, VPORT_E_RX_DROPPED);
return 0;
}
static void internal_dev_getinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
- struct dp_port *dp_port = vport_get_dp_port(internal_dev_get_vport(netdev));
+ struct vport *vport = internal_dev_get_vport(netdev);
+ struct dp_port *dp_port;
strcpy(info->driver, "openvswitch");
+
+ if (!vport)
+ return;
+
+ dp_port = vport_get_dp_port(vport);
if (dp_port)
sprintf(info->bus_info, "%d.%d", dp_port->dp->dp_idx, dp_port->port_no);
}
static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu)
{
- struct dp_port *dp_port = vport_get_dp_port(internal_dev_get_vport(netdev));
+ struct vport *vport = internal_dev_get_vport(netdev);
if (new_mtu < 68)
return -EINVAL;
- if (dp_port) {
- if (new_mtu > dp_min_mtu(dp_port->dp))
- return -EINVAL;
+ if (vport) {
+ struct dp_port *dp_port = vport_get_dp_port(vport);
+
+ if (dp_port) {
+ if (new_mtu > dp_min_mtu(dp_port->dp))
+ return -EINVAL;
+ }
}
netdev->mtu = new_mtu;
}
internal_dev = internal_dev_priv(netdev_vport->dev);
- internal_dev->vport = vport;
+ rcu_assign_pointer(internal_dev->vport, vport);
err = register_netdevice(netdev_vport->dev);
if (err)
static int internal_dev_attach(struct vport *vport)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
+ struct internal_dev *internal_dev = internal_dev_priv(netdev_vport->dev);
+ rcu_assign_pointer(internal_dev->attached_vport, internal_dev->vport);
dev_set_promiscuity(netdev_vport->dev, 1);
-
- /* It would make sense to assign dev->br_port here too, but
- * that causes packets received on internal ports to get caught
- * in netdev_frame_hook(). In turn netdev_frame_hook() can reject them
- * back to the network stack, but that's a waste of time. */
+ netif_start_queue(netdev_vport->dev);
return 0;
}
static int internal_dev_detach(struct vport *vport)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
+ struct internal_dev *internal_dev = internal_dev_priv(netdev_vport->dev);
+ netif_stop_queue(netdev_vport->dev);
dev_set_promiscuity(netdev_vport->dev, -1);
-
- /* Make sure that no packets arrive from now on, since
- * internal_dev_xmit() will try to find itself through
- * p->dp->ports[], and we're about to set that to null. */
- netif_tx_disable(netdev_vport->dev);
+ rcu_assign_pointer(internal_dev->attached_vport, NULL);
return 0;
}
struct vport *internal_dev_get_vport(struct net_device *netdev)
{
struct internal_dev *internal_dev = internal_dev_priv(netdev);
- return rcu_dereference(internal_dev->vport);
+ return rcu_dereference(internal_dev->attached_vport);
}
return 0;
}
-static void
-netdev_exit(void)
+static void netdev_exit(void)
{
br_handle_frame_hook = NULL;
}
return netdev_vport->dev->operstate;
}
-int
-netdev_get_ifindex(const struct vport *vport)
+int netdev_get_ifindex(const struct vport *vport)
{
const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
return netdev_vport->dev->ifindex;
* (No one comes after us, since we tell handle_bridge() that we took
* the packet.) */
skb = skb_share_check(skb, GFP_ATOMIC);
- if (!skb)
+ if (unlikely(!skb))
return;
+ skb_warn_if_lro(skb);
+
/* Push the Ethernet header back on. */
skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
static struct hlist_head *dev_table;
#define VPORT_HASH_BUCKETS 1024
-/* We limit the number of times that we pass through vport_send() to
- * avoid blowing out the stack in the event that we have a loop. There is
- * a separate counter for each CPU for both interrupt and non-interrupt
- * context in order to keep the limit deterministic for a given packet. */
-struct percpu_loop_counter {
- int count[2];
-};
-
-static DEFINE_PER_CPU(struct percpu_loop_counter, vport_loop_counter);
-#define VPORT_MAX_LOOPS 5
-
/* Both RTNL lock and vport_mutex need to be held when updating dev_table.
*
* If you use vport_locate and then perform some operations, you need to hold
{
ASSERT_RTNL();
- if (dp_port->vport)
- return -EBUSY;
-
if (vport_get_dp_port(vport))
return -EBUSY;
return err;
}
- dp_port->vport = vport;
rcu_assign_pointer(vport->dp_port, dp_port);
return 0;
if (!dp_port)
return -EINVAL;
- dp_port->vport = NULL;
rcu_assign_pointer(vport->dp_port, NULL);
if (vport->ops->detach)
*/
int vport_send(struct vport *vport, struct sk_buff *skb)
{
- int *loop_count;
int mtu;
int sent;
- loop_count = &get_cpu_var(vport_loop_counter).count[!!in_interrupt()];
- (*loop_count)++;
-
- if (unlikely(*loop_count > VPORT_MAX_LOOPS)) {
- if (net_ratelimit())
- printk(KERN_WARNING "%s: dropping packet that has looped more than %d times\n",
- dp_name(vport_get_dp_port(vport)->dp), VPORT_MAX_LOOPS);
- goto error;
- }
-
mtu = vport_get_mtu(vport);
if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
if (net_ratelimit())
local_bh_enable();
}
- goto out;
+ return sent;
error:
- sent = 0;
kfree_skb(skb);
vport_record_error(vport, VPORT_E_TX_DROPPED);
-out:
- (*loop_count)--;
- put_cpu_var(vport_loop_counter);
-
- return sent;
+ return 0;
}
/**
uint64_t used_sec; /* Time last used, in system monotonic time. */
uint32_t used_nsec;
uint8_t tcp_flags;
- uint8_t ip_tos;
+ uint8_t reserved;
uint16_t error; /* Used by XFLOW_FLOW_GET. */
};
lib/netdev-provider.h \
lib/netdev.c \
lib/netdev.h \
+ lib/ofp-parse.c \
+ lib/ofp-parse.h \
lib/ofp-print.c \
lib/ofp-print.h \
lib/ofp-util.c \
if HAVE_WNO_UNUSED
lib_libsflow_a_CFLAGS += -Wno-unused
endif
+if HAVE_WNO_UNUSED_PARAMETER
+lib_libsflow_a_CFLAGS += -Wno-unused-parameter
+endif
if HAVE_NETLINK
lib_libopenvswitch_a_SOURCES += \
memset(stats, '\0', sizeof(*stats));
if ((flow->dl_type == htons(ETH_TYPE_IP)) && packet->l4) {
- struct ip_header *ip = packet->l3;
- stats->ip_tos = ip->ip_tos;
if ((flow->nw_proto == IP_TYPE_TCP) && packet->l7) {
struct tcp_header *tcp = packet->l4;
stats->tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
#include "shash.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct ds;
/* Type of a JSON value. */
bool json_string_unescape(const char *in, size_t in_len, char **outp);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* json.h */
#include "flow.h"
#include "mac-learning.h"
#include "ofpbuf.h"
+#include "ofp-parse.h"
#include "ofp-print.h"
#include "ofp-util.h"
#include "openflow/openflow.h"
static void queue_tx(struct lswitch *, struct rconn *, struct ofpbuf *);
static void send_features_request(struct lswitch *, struct rconn *);
+static void send_default_flows(struct lswitch *sw, struct rconn *rconn,
+ FILE *default_flows);
static void schedule_query(struct lswitch *, long long int delay);
static bool may_learn(const struct lswitch *, uint16_t port_no);
static bool may_recv(const struct lswitch *, uint16_t port_no,
* after the given number of seconds (or never expire, if 'max_idle' is
* OFP_FLOW_PERMANENT). Otherwise, the new switch will process every packet.
*
+ * The caller may provide the file stream 'default_flows' that defines
+ * default flows that should be pushed when a switch connects. Each
+ * line is a flow entry in the format described for "add-flows" command
+ * in the Flow Syntax section of the ovs-ofct(8) man page. The caller
+ * is responsible for closing the stream.
+ *
* 'rconn' is used to send out an OpenFlow features request. */
struct lswitch *
lswitch_create(struct rconn *rconn, bool learn_macs,
- bool exact_flows, int max_idle, bool action_normal)
+ bool exact_flows, int max_idle, bool action_normal,
+ FILE *default_flows)
{
struct lswitch *sw;
size_t i;
sw->port_states[i] = P_DISABLED;
}
send_features_request(sw, rconn);
+ if (default_flows) {
+ send_default_flows(sw, rconn, default_flows);
+ }
return sw;
}
}
}
+static void
+send_default_flows(struct lswitch *sw, struct rconn *rconn,
+ FILE *default_flows)
+{
+ char line[1024];
+
+ while (fgets(line, sizeof line, default_flows)) {
+ struct ofpbuf *b;
+ struct ofp_flow_mod *ofm;
+ uint16_t priority, idle_timeout, hard_timeout;
+ uint64_t cookie;
+ struct ofp_match match;
+
+ char *comment;
+
+ /* Delete comments. */
+ comment = strchr(line, '#');
+ if (comment) {
+ *comment = '\0';
+ }
+
+ /* Drop empty lines. */
+ if (line[strspn(line, " \t\n")] == '\0') {
+ continue;
+ }
+
+ /* Parse and send. str_to_flow() will expand and reallocate the data
+ * in 'buffer', so we can't keep pointers to across the str_to_flow()
+ * call. */
+ make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &b);
+ parse_ofp_str(line, &match, b,
+ NULL, NULL, &priority, &idle_timeout, &hard_timeout,
+ &cookie);
+ ofm = b->data;
+ ofm->match = match;
+ ofm->command = htons(OFPFC_ADD);
+ ofm->cookie = htonll(cookie);
+ ofm->idle_timeout = htons(idle_timeout);
+ ofm->hard_timeout = htons(hard_timeout);
+ ofm->buffer_id = htonl(UINT32_MAX);
+ ofm->priority = htons(priority);
+
+ update_openflow_length(b);
+ queue_tx(sw, rconn, b);
+ }
+}
+
static void
queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b)
{
out_port = lswitch_choose_destination(sw, &flow);
/* Make actions. */
- memset(actions, 0, sizeof actions);
if (out_port == OFPP_NONE) {
actions_len = 0;
} else if (sw->queue == UINT32_MAX || out_port >= OFPP_MAX) {
- struct ofp_action_output *oao = (struct ofp_action_output *) actions;
- oao->type = htons(OFPAT_OUTPUT);
- oao->len = htons(sizeof *oao);
- oao->port = htons(out_port);
- actions_len = sizeof *oao;
+ struct ofp_action_output oao;
+
+ memset(&oao, 0, sizeof oao);
+ oao.type = htons(OFPAT_OUTPUT);
+ oao.len = htons(sizeof oao);
+ oao.port = htons(out_port);
+
+ memcpy(actions, &oao, sizeof oao);
+ actions_len = sizeof oao;
} else {
- struct ofp_action_enqueue *oae = (struct ofp_action_enqueue *) actions;
- oae->type = htons(OFPAT_ENQUEUE);
- oae->len = htons(sizeof *oae);
- oae->port = htons(out_port);
- oae->queue_id = htonl(sw->queue);
- actions_len = sizeof *oae;
+ struct ofp_action_enqueue oae;
+
+ memset(&oae, 0, sizeof oae);
+ oae.type = htons(OFPAT_ENQUEUE);
+ oae.len = htons(sizeof oae);
+ oae.port = htons(out_port);
+ oae.queue_id = htonl(sw->queue);
+
+ memcpy(actions, &oae, sizeof oae);
+ actions_len = sizeof oae;
}
assert(actions_len <= sizeof actions);
#include <stdbool.h>
#include <stdint.h>
+#include <stdio.h>
struct ofpbuf;
struct rconn;
struct lswitch *lswitch_create(struct rconn *, bool learn_macs,
- bool exact_flows, int max_idle,
- bool action_normal);
+ bool exact_flows, int max_idle,
+ bool action_normal, FILE *default_flows);
void lswitch_set_queue(struct lswitch *sw, uint32_t queue);
void lswitch_run(struct lswitch *, struct rconn *);
void lswitch_wait(struct lswitch *);
--- /dev/null
+/*
+ * Copyright (c) 2010 Nicira Networks.
+ *
+ * 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 "ofp-parse.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "netdev.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
+#include "openflow/openflow.h"
+#include "packets.h"
+#include "socket-util.h"
+#include "vconn.h"
+#include "vlog.h"
+
+
+VLOG_DEFINE_THIS_MODULE(ofp_parse)
+
+#define DEFAULT_IDLE_TIMEOUT 60
+
+static uint32_t
+str_to_u32(const char *str)
+{
+ char *tail;
+ uint32_t value;
+
+ errno = 0;
+ value = strtoul(str, &tail, 0);
+ if (errno == EINVAL || errno == ERANGE || *tail) {
+ ovs_fatal(0, "invalid numeric format %s", str);
+ }
+ return value;
+}
+
+static uint64_t
+str_to_u64(const char *str)
+{
+ char *tail;
+ uint64_t value;
+
+ errno = 0;
+ value = strtoull(str, &tail, 0);
+ if (errno == EINVAL || errno == ERANGE || *tail) {
+ ovs_fatal(0, "invalid numeric format %s", str);
+ }
+ return value;
+}
+
+static void
+str_to_mac(const char *str, uint8_t mac[6])
+{
+ if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
+ != ETH_ADDR_SCAN_COUNT) {
+ ovs_fatal(0, "invalid mac address %s", str);
+ }
+}
+
+static uint32_t
+str_to_ip(const char *str_, uint32_t *ip)
+{
+ char *str = xstrdup(str_);
+ char *save_ptr = NULL;
+ const char *name, *netmask;
+ struct in_addr in_addr;
+ int n_wild, retval;
+
+ name = strtok_r(str, "/", &save_ptr);
+ retval = name ? lookup_ip(name, &in_addr) : EINVAL;
+ if (retval) {
+ ovs_fatal(0, "%s: could not convert to IP address", str);
+ }
+ *ip = in_addr.s_addr;
+
+ netmask = strtok_r(NULL, "/", &save_ptr);
+ if (netmask) {
+ uint8_t o[4];
+ if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8,
+ &o[0], &o[1], &o[2], &o[3]) == 4) {
+ uint32_t nm = (o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3];
+ int i;
+
+ /* Find first 1-bit. */
+ for (i = 0; i < 32; i++) {
+ if (nm & (1u << i)) {
+ break;
+ }
+ }
+ n_wild = i;
+
+ /* Verify that the rest of the bits are 1-bits. */
+ for (; i < 32; i++) {
+ if (!(nm & (1u << i))) {
+ ovs_fatal(0, "%s: %s is not a valid netmask",
+ str, netmask);
+ }
+ }
+ } else {
+ int prefix = atoi(netmask);
+ if (prefix <= 0 || prefix > 32) {
+ ovs_fatal(0, "%s: network prefix bits not between 1 and 32",
+ str);
+ }
+ n_wild = 32 - prefix;
+ }
+ } else {
+ n_wild = 0;
+ }
+
+ free(str);
+ return n_wild;
+}
+
+static void *
+put_action(struct ofpbuf *b, size_t size, uint16_t type)
+{
+ struct ofp_action_header *ah = ofpbuf_put_zeros(b, size);
+ ah->type = htons(type);
+ ah->len = htons(size);
+ return ah;
+}
+
+static struct ofp_action_output *
+put_output_action(struct ofpbuf *b, uint16_t port)
+{
+ struct ofp_action_output *oao = put_action(b, sizeof *oao, OFPAT_OUTPUT);
+ oao->port = htons(port);
+ return oao;
+}
+
+static void
+put_enqueue_action(struct ofpbuf *b, uint16_t port, uint32_t queue)
+{
+ struct ofp_action_enqueue *oae = put_action(b, sizeof *oae, OFPAT_ENQUEUE);
+ oae->port = htons(port);
+ oae->queue_id = htonl(queue);
+}
+
+static void
+put_dl_addr_action(struct ofpbuf *b, uint16_t type, const char *addr)
+{
+ struct ofp_action_dl_addr *oada = put_action(b, sizeof *oada, type);
+ str_to_mac(addr, oada->dl_addr);
+}
+
+
+static bool
+parse_port_name(const char *name, uint16_t *port)
+{
+ struct pair {
+ const char *name;
+ uint16_t value;
+ };
+ static const struct pair pairs[] = {
+#define DEF_PAIR(NAME) {#NAME, OFPP_##NAME}
+ DEF_PAIR(IN_PORT),
+ DEF_PAIR(TABLE),
+ DEF_PAIR(NORMAL),
+ DEF_PAIR(FLOOD),
+ DEF_PAIR(ALL),
+ DEF_PAIR(CONTROLLER),
+ DEF_PAIR(LOCAL),
+ DEF_PAIR(NONE),
+#undef DEF_PAIR
+ };
+ static const int n_pairs = ARRAY_SIZE(pairs);
+ size_t i;
+
+ for (i = 0; i < n_pairs; i++) {
+ if (!strcasecmp(name, pairs[i].name)) {
+ *port = pairs[i].value;
+ return true;
+ }
+ }
+ return false;
+}
+
+static void
+str_to_action(char *str, struct ofpbuf *b)
+{
+ char *act, *arg;
+ char *saveptr = NULL;
+ bool drop = false;
+ int n_actions;
+
+ for (act = strtok_r(str, ", \t\r\n", &saveptr), n_actions = 0; act;
+ act = strtok_r(NULL, ", \t\r\n", &saveptr), n_actions++)
+ {
+ uint16_t port;
+
+ if (drop) {
+ ovs_fatal(0, "Drop actions must not be followed by other actions");
+ }
+
+ /* Arguments are separated by colons */
+ arg = strchr(act, ':');
+ if (arg) {
+ *arg = '\0';
+ arg++;
+ }
+
+ if (!strcasecmp(act, "mod_vlan_vid")) {
+ struct ofp_action_vlan_vid *va;
+ va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID);
+ va->vlan_vid = htons(str_to_u32(arg));
+ } else if (!strcasecmp(act, "mod_vlan_pcp")) {
+ struct ofp_action_vlan_pcp *va;
+ va = put_action(b, sizeof *va, OFPAT_SET_VLAN_PCP);
+ va->vlan_pcp = str_to_u32(arg);
+ } else if (!strcasecmp(act, "strip_vlan")) {
+ struct ofp_action_header *ah;
+ ah = put_action(b, sizeof *ah, OFPAT_STRIP_VLAN);
+ ah->type = htons(OFPAT_STRIP_VLAN);
+ } else if (!strcasecmp(act, "mod_dl_src")) {
+ put_dl_addr_action(b, OFPAT_SET_DL_SRC, arg);
+ } else if (!strcasecmp(act, "mod_dl_dst")) {
+ put_dl_addr_action(b, OFPAT_SET_DL_DST, arg);
+ } else if (!strcasecmp(act, "mod_nw_src")) {
+ struct ofp_action_nw_addr *na;
+ na = put_action(b, sizeof *na, OFPAT_SET_NW_SRC);
+ str_to_ip(arg, &na->nw_addr);
+ } else if (!strcasecmp(act, "mod_nw_dst")) {
+ struct ofp_action_nw_addr *na;
+ na = put_action(b, sizeof *na, OFPAT_SET_NW_DST);
+ str_to_ip(arg, &na->nw_addr);
+ } else if (!strcasecmp(act, "mod_tp_src")) {
+ struct ofp_action_tp_port *ta;
+ ta = put_action(b, sizeof *ta, OFPAT_SET_TP_SRC);
+ ta->tp_port = htons(str_to_u32(arg));
+ } else if (!strcasecmp(act, "mod_tp_dst")) {
+ struct ofp_action_tp_port *ta;
+ ta = put_action(b, sizeof *ta, OFPAT_SET_TP_DST);
+ ta->tp_port = htons(str_to_u32(arg));
+ } else if (!strcasecmp(act, "mod_nw_tos")) {
+ struct ofp_action_nw_tos *nt;
+ nt = put_action(b, sizeof *nt, OFPAT_SET_NW_TOS);
+ nt->nw_tos = str_to_u32(arg);
+ } else if (!strcasecmp(act, "resubmit")) {
+ struct nx_action_resubmit *nar;
+ nar = put_action(b, sizeof *nar, OFPAT_VENDOR);
+ nar->vendor = htonl(NX_VENDOR_ID);
+ nar->subtype = htons(NXAST_RESUBMIT);
+ nar->in_port = htons(str_to_u32(arg));
+ } else if (!strcasecmp(act, "set_tunnel")) {
+ struct nx_action_set_tunnel *nast;
+ nast = put_action(b, sizeof *nast, OFPAT_VENDOR);
+ nast->vendor = htonl(NX_VENDOR_ID);
+ nast->subtype = htons(NXAST_SET_TUNNEL);
+ nast->tun_id = htonl(str_to_u32(arg));
+ } else if (!strcasecmp(act, "output")) {
+ put_output_action(b, str_to_u32(arg));
+ } else if (!strcasecmp(act, "enqueue")) {
+ char *sp = NULL;
+ char *port = strtok_r(arg, ":q", &sp);
+ char *queue = strtok_r(NULL, "", &sp);
+ if (port == NULL || queue == NULL) {
+ ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
+ }
+ put_enqueue_action(b, str_to_u32(port), str_to_u32(queue));
+ } else if (!strcasecmp(act, "drop")) {
+ /* A drop action in OpenFlow occurs by just not setting
+ * an action. */
+ drop = true;
+ if (n_actions) {
+ ovs_fatal(0, "Drop actions must not be preceded by other "
+ "actions");
+ }
+ } else if (!strcasecmp(act, "CONTROLLER")) {
+ struct ofp_action_output *oao;
+ oao = put_output_action(b, OFPP_CONTROLLER);
+
+ /* Unless a numeric argument is specified, we send the whole
+ * packet to the controller. */
+ if (arg && (strspn(arg, "0123456789") == strlen(arg))) {
+ oao->max_len = htons(str_to_u32(arg));
+ } else {
+ oao->max_len = htons(UINT16_MAX);
+ }
+ } else if (parse_port_name(act, &port)) {
+ put_output_action(b, port);
+ } else if (strspn(act, "0123456789") == strlen(act)) {
+ put_output_action(b, str_to_u32(act));
+ } else {
+ ovs_fatal(0, "Unknown action: %s", act);
+ }
+ }
+}
+
+struct protocol {
+ const char *name;
+ uint16_t dl_type;
+ uint8_t nw_proto;
+};
+
+static bool
+parse_protocol(const char *name, const struct protocol **p_out)
+{
+ static const struct protocol protocols[] = {
+ { "ip", ETH_TYPE_IP, 0 },
+ { "arp", ETH_TYPE_ARP, 0 },
+ { "icmp", ETH_TYPE_IP, IP_TYPE_ICMP },
+ { "tcp", ETH_TYPE_IP, IP_TYPE_TCP },
+ { "udp", ETH_TYPE_IP, IP_TYPE_UDP },
+ };
+ const struct protocol *p;
+
+ for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
+ if (!strcmp(p->name, name)) {
+ *p_out = p;
+ return true;
+ }
+ }
+ *p_out = NULL;
+ return false;
+}
+
+struct field {
+ const char *name;
+ uint32_t wildcard;
+ enum { F_U8, F_U16, F_MAC, F_IP } type;
+ size_t offset, shift;
+};
+
+static bool
+parse_field(const char *name, const struct field **f_out)
+{
+#define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
+ static const struct field fields[] = {
+ { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 },
+ { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 },
+ { "dl_vlan_pcp", OFPFW_DL_VLAN_PCP, F_U8, F_OFS(dl_vlan_pcp), 0 },
+ { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src), 0 },
+ { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst), 0 },
+ { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type), 0 },
+ { "nw_src", OFPFW_NW_SRC_MASK, F_IP,
+ F_OFS(nw_src), OFPFW_NW_SRC_SHIFT },
+ { "nw_dst", OFPFW_NW_DST_MASK, F_IP,
+ F_OFS(nw_dst), OFPFW_NW_DST_SHIFT },
+ { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto), 0 },
+ { "nw_tos", OFPFW_NW_TOS, F_U8, F_OFS(nw_tos), 0 },
+ { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src), 0 },
+ { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst), 0 },
+ { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type), 0 },
+ { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code), 0 }
+ };
+ const struct field *f;
+
+ for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
+ if (!strcmp(f->name, name)) {
+ *f_out = f;
+ return true;
+ }
+ }
+ *f_out = NULL;
+ return false;
+}
+
+/* Convert 'string' (as described in the Flow Syntax section of the
+ * ovs-ofctl man page) into 'match'. The other arguments are optional
+ * and may be NULL if their value is not needed. If 'actions' is
+ * specified, an action must be in 'string' and may be expanded or
+ * reallocated. */
+void
+parse_ofp_str(char *string, struct ofp_match *match, struct ofpbuf *actions,
+ uint8_t *table_idx, uint16_t *out_port, uint16_t *priority,
+ uint16_t *idle_timeout, uint16_t *hard_timeout,
+ uint64_t *cookie)
+{
+ struct ofp_match normalized;
+ char *save_ptr = NULL;
+ char *name;
+ uint32_t wildcards;
+
+ if (table_idx) {
+ *table_idx = 0xff;
+ }
+ if (out_port) {
+ *out_port = OFPP_NONE;
+ }
+ if (priority) {
+ *priority = OFP_DEFAULT_PRIORITY;
+ }
+ if (idle_timeout) {
+ *idle_timeout = DEFAULT_IDLE_TIMEOUT;
+ }
+ if (hard_timeout) {
+ *hard_timeout = OFP_FLOW_PERMANENT;
+ }
+ if (cookie) {
+ *cookie = 0;
+ }
+ if (actions) {
+ char *act_str = strstr(string, "action");
+ if (!act_str) {
+ ovs_fatal(0, "must specify an action");
+ }
+ *act_str = '\0';
+
+ act_str = strchr(act_str + 1, '=');
+ if (!act_str) {
+ ovs_fatal(0, "must specify an action");
+ }
+
+ act_str++;
+
+ str_to_action(act_str, actions);
+ }
+ memset(match, 0, sizeof *match);
+ wildcards = OFPFW_ALL;
+ for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+ name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+ const struct protocol *p;
+
+ if (parse_protocol(name, &p)) {
+ wildcards &= ~OFPFW_DL_TYPE;
+ match->dl_type = htons(p->dl_type);
+ if (p->nw_proto) {
+ wildcards &= ~OFPFW_NW_PROTO;
+ match->nw_proto = p->nw_proto;
+ }
+ } else {
+ const struct field *f;
+ char *value;
+
+ value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+ if (!value) {
+ ovs_fatal(0, "field %s missing value", name);
+ }
+
+ if (table_idx && !strcmp(name, "table")) {
+ *table_idx = atoi(value);
+ if (*table_idx < 0 || *table_idx > 31) {
+ ovs_fatal(0, "table %s is invalid, "
+ "must be between 0 and 31", value);
+ }
+ } else if (out_port && !strcmp(name, "out_port")) {
+ *out_port = atoi(value);
+ } else if (priority && !strcmp(name, "priority")) {
+ *priority = atoi(value);
+ } else if (idle_timeout && !strcmp(name, "idle_timeout")) {
+ *idle_timeout = atoi(value);
+ } else if (hard_timeout && !strcmp(name, "hard_timeout")) {
+ *hard_timeout = atoi(value);
+ } else if (cookie && !strcmp(name, "cookie")) {
+ *cookie = str_to_u64(value);
+ } else if (!strcmp(name, "tun_id_wild")) {
+ wildcards |= NXFW_TUN_ID;
+ } else if (parse_field(name, &f)) {
+ void *data = (char *) match + f->offset;
+ if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
+ wildcards |= f->wildcard;
+ } else {
+ wildcards &= ~f->wildcard;
+ if (f->wildcard == OFPFW_IN_PORT
+ && parse_port_name(value, (uint16_t *) data)) {
+ /* Nothing to do. */
+ } else if (f->type == F_U8) {
+ *(uint8_t *) data = str_to_u32(value);
+ } else if (f->type == F_U16) {
+ *(uint16_t *) data = htons(str_to_u32(value));
+ } else if (f->type == F_MAC) {
+ str_to_mac(value, data);
+ } else if (f->type == F_IP) {
+ wildcards |= str_to_ip(value, data) << f->shift;
+ } else {
+ NOT_REACHED();
+ }
+ }
+ } else {
+ ovs_fatal(0, "unknown keyword %s", name);
+ }
+ }
+ }
+ match->wildcards = htonl(wildcards);
+
+ normalized = *match;
+ normalize_match(&normalized);
+ if (memcmp(match, &normalized, sizeof normalized)) {
+ char *old = ofp_match_to_literal_string(match);
+ char *new = ofp_match_to_literal_string(&normalized);
+ VLOG_WARN("The specified flow is not in normal form:");
+ VLOG_WARN(" as specified: %s", old);
+ VLOG_WARN("as normalized: %s", new);
+ free(old);
+ free(new);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Nicira Networks.
+ *
+ * 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.
+ */
+
+/* OpenFlow protocol string to flow parser. */
+
+#ifndef OFP_PARSE_H
+#define OFP_PARSE_H 1
+
+#include <stdint.h>
+
+struct ofp_match;
+struct ofpbuf;
+
+void parse_ofp_str(char *string, struct ofp_match *match,
+ struct ofpbuf *actions, uint8_t *table_idx,
+ uint16_t *out_port, uint16_t *priority,
+ uint16_t *idle_timeout, uint16_t *hard_timeout,
+ uint64_t *cookie);
+
+#endif /* ofp-parse.h */
return true;
}
+static void
+stream_ssl_set_private_key_file__(const char *file_name)
+{
+ if (SSL_CTX_use_PrivateKey_file(ctx, file_name, SSL_FILETYPE_PEM) == 1) {
+ private_key.read = true;
+ } else {
+ VLOG_ERR("SSL_use_PrivateKey_file: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+}
+
void
stream_ssl_set_private_key_file(const char *file_name)
{
- if (!update_ssl_config(&private_key, file_name)) {
- return;
+ if (update_ssl_config(&private_key, file_name)) {
+ stream_ssl_set_private_key_file__(file_name);
}
- if (SSL_CTX_use_PrivateKey_file(ctx, file_name, SSL_FILETYPE_PEM) != 1) {
- VLOG_ERR("SSL_use_PrivateKey_file: %s",
+}
+
+static void
+stream_ssl_set_certificate_file__(const char *file_name)
+{
+ if (SSL_CTX_use_certificate_chain_file(ctx, file_name) == 1) {
+ certificate.read = true;
+ } else {
+ VLOG_ERR("SSL_use_certificate_file: %s",
ERR_error_string(ERR_get_error(), NULL));
- return;
}
- private_key.read = true;
}
void
stream_ssl_set_certificate_file(const char *file_name)
{
- if (!update_ssl_config(&certificate, file_name)) {
- return;
+ if (update_ssl_config(&certificate, file_name)) {
+ stream_ssl_set_certificate_file__(file_name);
}
- if (SSL_CTX_use_certificate_chain_file(ctx, file_name) != 1) {
- VLOG_ERR("SSL_use_certificate_file: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return;
+}
+
+/* Sets the private key and certificate files in one operation. Use this
+ * interface, instead of calling stream_ssl_set_private_key_file() and
+ * stream_ssl_set_certificate_file() individually, in the main loop of a
+ * long-running program whose key and certificate might change at runtime.
+ *
+ * This is important because of OpenSSL's behavior. If an OpenSSL context
+ * already has a certificate, and stream_ssl_set_private_key_file() is called
+ * to install a new private key, OpenSSL will report an error because the new
+ * private key does not match the old certificate. The other order, of setting
+ * a new certificate, then setting a new private key, does work.
+ *
+ * If this were the only problem, calling stream_ssl_set_certificate_file()
+ * before stream_ssl_set_private_key_file() would fix it. But, if the private
+ * key is changed before the certificate (e.g. someone "scp"s or "mv"s the new
+ * private key in place before the certificate), then OpenSSL would reject that
+ * change, and then the change of certificate would succeed, but there would be
+ * no associated private key (because it had only changed once and therefore
+ * there was no point in re-reading it).
+ *
+ * This function avoids both problems by, whenever either the certificate or
+ * the private key file changes, re-reading both of them, in the correct order.
+ */
+void
+stream_ssl_set_key_and_cert(const char *private_key_file,
+ const char *certificate_file)
+{
+ if (update_ssl_config(&private_key, private_key_file)
+ || update_ssl_config(&certificate, certificate_file)) {
+ stream_ssl_set_certificate_file__(certificate_file);
+ stream_ssl_set_private_key_file__(private_key_file);
}
- certificate.read = true;
}
/* Reads the X509 certificate or certificates in file 'file_name'. On success,
/*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#ifdef HAVE_OPENSSL
bool stream_ssl_is_configured(void);
+
void stream_ssl_set_private_key_file(const char *file_name);
void stream_ssl_set_certificate_file(const char *file_name);
void stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap);
+
+void stream_ssl_set_key_and_cert(const char *private_key_file,
+ const char *certificate_file);
+
+
void stream_ssl_set_peer_ca_cert_file(const char *file_name);
/* Define the long options for SSL support.
/*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
tag_create_deterministic(uint32_t seed)
{
int x = seed & (N_TAG_BITS - 1);
- int y = (seed >> LOG2_N_TAG_BITS) % 31;
+ int y = (seed >> LOG2_N_TAG_BITS) % (N_TAG_BITS - 1);
y += y >= x;
return (1u << x) | (1u << y);
}
memset(set, 0, sizeof *set);
}
+static bool
+tag_is_worth_adding(const struct tag_set *set, tag_type tag)
+{
+ if (!tag) {
+ /* Nothing to add. */
+ return false;
+ } else if ((set->total & tag) != tag) {
+ /* 'set' doesn't have all the bits in 'tag', so we need to add it. */
+ return true;
+ } else {
+ /* We can drop it if some member of 'set' already includes all of the
+ * 1-bits in 'tag'. (tag_set_intersects() does a different test:
+ * whether some member of 'set' has at least two 1-bit in common with
+ * 'tag'.) */
+ int i;
+
+ for (i = 0; i < TAG_SET_SIZE; i++) {
+ if ((set->tags[i] & tag) == tag) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
/* Adds 'tag' to 'set'. */
void
tag_set_add(struct tag_set *set, tag_type tag)
{
- if (tag && (!tag_is_valid(tag) || !tag_set_intersects(set, tag))) {
+ if (tag_is_worth_adding(set, tag)) {
/* XXX We could do better by finding the set member to which we would
* add the fewest number of 1-bits. This would reduce the amount of
* ambiguity, since e.g. three 1-bits match 3 * 2 / 2 = 3 unique tags
VLOG_MODULE(netflow)
VLOG_MODULE(netlink)
VLOG_MODULE(ofctl)
+VLOG_MODULE(ofp_parse)
VLOG_MODULE(ofp_util)
VLOG_MODULE(ofproto)
VLOG_MODULE(openflowd)
struct timespec used; /* Last used time. */
long long int packet_count; /* Number of packets matched. */
long long int byte_count; /* Number of bytes matched. */
- uint8_t ip_tos; /* IP TOS value. */
uint16_t tcp_ctl; /* Bitwise-OR of seen tcp_ctl values. */
/* Actions. */
xflow_flow->stats.used_sec = flow->used.tv_sec;
xflow_flow->stats.used_nsec = flow->used.tv_nsec;
xflow_flow->stats.tcp_flags = TCP_FLAGS(flow->tcp_ctl);
- xflow_flow->stats.ip_tos = flow->ip_tos;
+ xflow_flow->stats.reserved = 0;
xflow_flow->stats.error = 0;
if (xflow_flow->n_actions > 0) {
unsigned int n = MIN(xflow_flow->n_actions, flow->n_actions);
flow->used.tv_nsec = 0;
flow->packet_count = 0;
flow->byte_count = 0;
- flow->ip_tos = 0;
flow->tcp_ctl = 0;
}
time_timespec(&flow->used);
flow->packet_count++;
flow->byte_count += packet->size;
- if (key->dl_type == htons(ETH_TYPE_IP)) {
- struct ip_header *nh = packet->l3;
- flow->ip_tos = nh->ip_tos;
-
- if (key->nw_proto == IPPROTO_TCP) {
- struct tcp_header *th = packet->l4;
- flow->tcp_ctl |= th->tcp_ctl;
- }
+ if (key->dl_type == htons(ETH_TYPE_IP) && key->nw_proto == IPPROTO_TCP) {
+ struct tcp_header *th = packet->l4;
+ flow->tcp_ctl |= th->tcp_ctl;
}
}
}
nf_rec->tcp_flags = nf_flow->tcp_flags;
nf_rec->ip_proto = expired->flow.nw_proto;
- nf_rec->ip_tos = nf_flow->ip_tos;
+ nf_rec->ip_tos = expired->flow.nw_tos;
/* Update flow tracking data. */
nf_flow->created = 0;
}
void
-netflow_flow_update_flags(struct netflow_flow *nf_flow, uint8_t ip_tos,
- uint8_t tcp_flags)
+netflow_flow_update_flags(struct netflow_flow *nf_flow, uint8_t tcp_flags)
{
- nf_flow->ip_tos = ip_tos;
nf_flow->tcp_flags |= tcp_flags;
}
uint64_t byte_count_off; /* Byte count at last time out. */
uint16_t output_iface; /* Output interface index. */
- uint8_t ip_tos; /* Last-seen IP type-of-service. */
uint8_t tcp_flags; /* Bitwise-OR of all TCP flags seen. */
};
void netflow_flow_clear(struct netflow_flow *);
void netflow_flow_update_time(struct netflow *, struct netflow_flow *,
long long int used);
-void netflow_flow_update_flags(struct netflow_flow *, uint8_t ip_tos,
- uint8_t tcp_flags);
+void netflow_flow_update_flags(struct netflow_flow *, uint8_t tcp_flags);
bool netflow_active_timeout_expired(struct netflow *, struct netflow_flow *);
#endif /* netflow.h */
/* ofproto supports two kinds of OpenFlow connections:
*
- * - "Controller connections": Connections to ordinary OpenFlow controllers.
- * ofproto maintains persistent connections to these controllers and by
- * default sends them asynchronous messages such as packet-ins.
+ * - "Primary" connections to ordinary OpenFlow controllers. ofproto
+ * maintains persistent connections to these controllers and by default
+ * sends them asynchronous messages such as packet-ins.
*
- * - "Transient connections", e.g. from ovs-ofctl. When these connections
+ * - "Service" connections, e.g. from ovs-ofctl. When these connections
* drop, it is the other side's responsibility to reconnect them if
* necessary. ofproto does not send them asynchronous messages by default.
+ *
+ * Currently, active (tcp, ssl, unix) connections are always "primary"
+ * connections and passive (ptcp, pssl, punix) connections are always "service"
+ * connections. There is no inherent reason for this, but it reflects the
+ * common case.
*/
enum ofconn_type {
- OFCONN_CONTROLLER, /* An OpenFlow controller. */
- OFCONN_TRANSIENT /* A transient connection. */
+ OFCONN_PRIMARY, /* An ordinary OpenFlow controller. */
+ OFCONN_SERVICE /* A service connection, e.g. "ovs-ofctl". */
};
+/* A listener for incoming OpenFlow "service" connections. */
+struct ofservice {
+ struct hmap_node node; /* In struct ofproto's "services" hmap. */
+ struct pvconn *pvconn; /* OpenFlow connection listener. */
+
+ /* These are not used by ofservice directly. They are settings for
+ * accepted "struct ofconn"s from the pvconn. */
+ int probe_interval; /* Max idle time before probing, in seconds. */
+ int rate_limit; /* Max packet-in rate in packets per second. */
+ int burst_limit; /* Limit on accumulating packet credits. */
+};
+
+static struct ofservice *ofservice_lookup(struct ofproto *,
+ const char *target);
+static int ofservice_create(struct ofproto *,
+ const struct ofproto_controller *);
+static void ofservice_reconfigure(struct ofservice *,
+ const struct ofproto_controller *);
+static void ofservice_destroy(struct ofproto *, struct ofservice *);
+
/* An OpenFlow connection. */
struct ofconn {
struct ofproto *ofproto; /* The ofproto that owns this connection. */
#define OFCONN_REPLY_MAX 100
struct rconn_packet_counter *reply_counter;
- /* type == OFCONN_CONTROLLER only. */
+ /* type == OFCONN_PRIMARY only. */
enum nx_role role; /* Role. */
struct hmap_node hmap_node; /* In struct ofproto's "controllers" map. */
struct discovery *discovery; /* Controller discovery object, if enabled. */
static void ofconn_wait(struct ofconn *);
static bool ofconn_receives_async_msgs(const struct ofconn *);
static char *ofconn_make_name(const struct ofproto *, const char *target);
+static void ofconn_set_rate_limit(struct ofconn *, int rate, int burst);
static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
struct rconn_packet_counter *counter);
/* OpenFlow connections. */
struct hmap controllers; /* Controller "struct ofconn"s. */
struct list all_conns; /* Contains "struct ofconn"s. */
- struct pvconn **listeners;
- size_t n_listeners;
+ enum ofproto_fail_mode fail_mode;
+
+ /* OpenFlow listeners. */
+ struct hmap services; /* Contains "struct ofservice"s. */
struct pvconn **snoops;
size_t n_snoops;
};
/* Initialize OpenFlow connections. */
list_init(&p->all_conns);
hmap_init(&p->controllers);
- p->listeners = NULL;
- p->n_listeners = 0;
+ hmap_init(&p->services);
p->snoops = NULL;
p->n_snoops = 0;
discovery = NULL;
}
- ofconn = ofconn_create(ofproto, rconn_create(5, 8), OFCONN_CONTROLLER);
+ ofconn = ofconn_create(ofproto, rconn_create(5, 8), OFCONN_PRIMARY);
ofconn->pktbuf = pktbuf_create();
ofconn->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
if (discovery) {
static void
update_controller(struct ofconn *ofconn, const struct ofproto_controller *c)
{
- struct ofproto *ofproto = ofconn->ofproto;
int probe_interval;
- int i;
ofconn->band = (is_in_band_controller(c)
? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND);
discovery_set_accept_controller_re(ofconn->discovery, c->accept_re);
}
- for (i = 0; i < N_SCHEDULERS; i++) {
- struct pinsched **s = &ofconn->schedulers[i];
-
- if (c->rate_limit > 0) {
- if (!*s) {
- *s = pinsched_create(c->rate_limit, c->burst_limit,
- ofproto->switch_status);
- } else {
- pinsched_set_limits(*s, c->rate_limit, c->burst_limit);
- }
- } else {
- pinsched_destroy(*s);
- *s = NULL;
- }
- }
+ ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit);
}
static const char *
free(addrs);
}
+static void
+update_fail_open(struct ofproto *p)
+{
+ struct ofconn *ofconn;
+
+ if (!hmap_is_empty(&p->controllers)
+ && p->fail_mode == OFPROTO_FAIL_STANDALONE) {
+ struct rconn **rconns;
+ size_t n;
+
+ if (!p->fail_open) {
+ p->fail_open = fail_open_create(p, p->switch_status);
+ }
+
+ n = 0;
+ rconns = xmalloc(hmap_count(&p->controllers) * sizeof *rconns);
+ HMAP_FOR_EACH (ofconn, struct ofconn, hmap_node, &p->controllers) {
+ rconns[n++] = ofconn->rconn;
+ }
+
+ fail_open_set_controllers(p->fail_open, rconns, n);
+ /* p->fail_open takes ownership of 'rconns'. */
+ } else {
+ fail_open_destroy(p->fail_open);
+ p->fail_open = NULL;
+ }
+}
+
void
ofproto_set_controllers(struct ofproto *p,
const struct ofproto_controller *controllers,
size_t n_controllers)
{
struct shash new_controllers;
- enum ofproto_fail_mode fail_mode;
- struct ofconn *ofconn, *next;
+ struct ofconn *ofconn, *next_ofconn;
+ struct ofservice *ofservice, *next_ofservice;
bool ss_exists;
size_t i;
+ /* Create newly configured controllers and services.
+ * Create a name to ofproto_controller mapping in 'new_controllers'. */
shash_init(&new_controllers);
for (i = 0; i < n_controllers; i++) {
const struct ofproto_controller *c = &controllers[i];
- shash_add_once(&new_controllers, c->target, &controllers[i]);
- if (!find_controller_by_target(p, c->target)) {
- add_controller(p, c);
+ if (!vconn_verify_name(c->target) || !strcmp(c->target, "discover")) {
+ if (!find_controller_by_target(p, c->target)) {
+ add_controller(p, c);
+ }
+ } else if (!pvconn_verify_name(c->target)) {
+ if (!ofservice_lookup(p, c->target) && ofservice_create(p, c)) {
+ continue;
+ }
+ } else {
+ VLOG_WARN_RL(&rl, "%s: unsupported controller \"%s\"",
+ wdp_name(p->wdp), c->target);
+ continue;
}
+
+ shash_add_once(&new_controllers, c->target, &controllers[i]);
}
- fail_mode = OFPROTO_FAIL_STANDALONE;
+ /* Delete controllers that are no longer configured.
+ * Update configuration of all now-existing controllers. */
ss_exists = false;
- HMAP_FOR_EACH_SAFE (ofconn, next, struct ofconn, hmap_node,
+ HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, hmap_node,
&p->controllers) {
struct ofproto_controller *c;
if (ofconn->ss) {
ss_exists = true;
}
- if (c->fail == OFPROTO_FAIL_SECURE) {
- fail_mode = OFPROTO_FAIL_SECURE;
- }
}
}
- shash_destroy(&new_controllers);
- update_in_band_remotes(p);
-
- if (!hmap_is_empty(&p->controllers)
- && fail_mode == OFPROTO_FAIL_STANDALONE) {
- struct rconn **rconns;
- size_t n;
+ /* Delete services that are no longer configured.
+ * Update configuration of all now-existing services. */
+ HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, struct ofservice, node,
+ &p->services) {
+ struct ofproto_controller *c;
- if (!p->fail_open) {
- p->fail_open = fail_open_create(p, p->switch_status);
+ c = shash_find_data(&new_controllers,
+ pvconn_get_name(ofservice->pvconn));
+ if (!c) {
+ ofservice_destroy(p, ofservice);
+ } else {
+ ofservice_reconfigure(ofservice, c);
}
+ }
- n = 0;
- rconns = xmalloc(hmap_count(&p->controllers) * sizeof *rconns);
- HMAP_FOR_EACH (ofconn, struct ofconn, hmap_node, &p->controllers) {
- rconns[n++] = ofconn->rconn;
- }
+ shash_destroy(&new_controllers);
- fail_open_set_controllers(p->fail_open, rconns, n);
- /* p->fail_open takes ownership of 'rconns'. */
- } else {
- fail_open_destroy(p->fail_open);
- p->fail_open = NULL;
- }
+ update_in_band_remotes(p);
+ update_fail_open(p);
if (!hmap_is_empty(&p->controllers) && !ss_exists) {
ofconn = CONTAINER_OF(hmap_first(&p->controllers),
}
}
+void
+ofproto_set_fail_mode(struct ofproto *p, enum ofproto_fail_mode fail_mode)
+{
+ p->fail_mode = fail_mode;
+ update_fail_open(p);
+}
+
/* Drops the connections between 'ofproto' and all of its controllers, forcing
* them to reconnect. */
void
return retval;
}
-int
-ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners)
-{
- return set_pvconns(&ofproto->listeners, &ofproto->n_listeners, listeners);
-}
-
int
ofproto_set_snoops(struct ofproto *ofproto, const struct svec *snoops)
{
}
bool
-ofproto_has_controller(const struct ofproto *ofproto)
+ofproto_has_primary_controller(const struct ofproto *ofproto)
{
return !hmap_is_empty(&ofproto->controllers);
}
-void
-ofproto_get_listeners(const struct ofproto *ofproto, struct svec *listeners)
+enum ofproto_fail_mode
+ofproto_get_fail_mode(const struct ofproto *p)
{
- size_t i;
-
- for (i = 0; i < ofproto->n_listeners; i++) {
- svec_add(listeners, pvconn_get_name(ofproto->listeners[i]));
- }
+ return p->fail_mode;
}
void
void
ofproto_destroy(struct ofproto *p)
{
+ struct ofservice *ofservice, *next_ofservice;
struct ofconn *ofconn, *next_ofconn;
size_t i;
netflow_destroy(p->netflow);
ofproto_sflow_destroy(p->sflow);
- for (i = 0; i < p->n_listeners; i++) {
- pvconn_close(p->listeners[i]);
+ HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, struct ofservice, node,
+ &p->services) {
+ ofservice_destroy(p, ofservice);
}
- free(p->listeners);
+ hmap_destroy(&p->services);
for (i = 0; i < p->n_snoops; i++) {
pvconn_close(p->snoops[i]);
/* Pick a controller for monitoring. */
best = NULL;
LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) {
- if (ofconn->type == OFCONN_CONTROLLER
+ if (ofconn->type == OFCONN_PRIMARY
&& (!best || snoop_preference(ofconn) > snoop_preference(best))) {
best = ofconn;
}
ofproto_run1(struct ofproto *p)
{
struct ofconn *ofconn, *next_ofconn;
+ struct ofservice *ofservice;
int i;
for (i = 0; i < 50; i++) {
fail_open_run(p->fail_open);
}
- for (i = 0; i < p->n_listeners; i++) {
+ HMAP_FOR_EACH (ofservice, struct ofservice, node, &p->services) {
struct vconn *vconn;
int retval;
- retval = pvconn_accept(p->listeners[i], OFP_VERSION, &vconn);
+ retval = pvconn_accept(ofservice->pvconn, OFP_VERSION, &vconn);
if (!retval) {
+ struct ofconn *ofconn;
struct rconn *rconn;
char *name;
- rconn = rconn_create(60, 0);
+ rconn = rconn_create(ofservice->probe_interval, 0);
name = ofconn_make_name(p, vconn_get_name(vconn));
rconn_connect_unreliably(rconn, vconn, name);
free(name);
- ofconn_create(p, rconn, OFCONN_TRANSIENT);
+ ofconn = ofconn_create(p, rconn, OFCONN_SERVICE);
+ ofconn_set_rate_limit(ofconn, ofservice->rate_limit,
+ ofservice->burst_limit);
} else if (retval != EAGAIN) {
VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
}
void
ofproto_wait(struct ofproto *p)
{
+ struct ofservice *ofservice;
struct ofconn *ofconn;
size_t i;
if (p->sflow) {
ofproto_sflow_wait(p->sflow);
}
- for (i = 0; i < p->n_listeners; i++) {
- pvconn_wait(p->listeners[i]);
+ HMAP_FOR_EACH (ofservice, struct ofservice, node, &p->services) {
+ pvconn_wait(ofservice->pvconn);
}
for (i = 0; i < p->n_snoops; i++) {
pvconn_wait(p->snoops[i]);
static void
ofconn_destroy(struct ofconn *ofconn)
{
- if (ofconn->type == OFCONN_CONTROLLER) {
+ if (ofconn->type == OFCONN_PRIMARY) {
hmap_remove(&ofconn->ofproto->controllers, &ofconn->hmap_node);
}
discovery_destroy(ofconn->discovery);
static bool
ofconn_receives_async_msgs(const struct ofconn *ofconn)
{
- if (ofconn->type == OFCONN_CONTROLLER) {
- /* Ordinary controllers always get asynchronous messages unless they
+ if (ofconn->type == OFCONN_PRIMARY) {
+ /* Primary controllers always get asynchronous messages unless they
* have configured themselves as "slaves". */
return ofconn->role != NX_ROLE_SLAVE;
} else {
- /* Transient connections don't get asynchronous messages unless they
- * have explicitly asked for them by setting a nonzero miss send
- * length. */
+ /* Service connections don't get asynchronous messages unless they have
+ * explicitly asked for them by setting a nonzero miss send length. */
return ofconn->miss_send_len > 0;
}
}
{
return xasprintf("%s<->%s", wdp_base_name(ofproto->wdp), target);
}
+
+static void
+ofconn_set_rate_limit(struct ofconn *ofconn, int rate, int burst)
+{
+ int i;
+
+ for (i = 0; i < N_SCHEDULERS; i++) {
+ struct pinsched **s = &ofconn->schedulers[i];
+
+ if (rate > 0) {
+ if (!*s) {
+ *s = pinsched_create(rate, burst,
+ ofconn->ofproto->switch_status);
+ } else {
+ pinsched_set_limits(*s, rate, burst);
+ }
+ } else {
+ pinsched_destroy(*s);
+ *s = NULL;
+ }
+ }
+}
+\f
+static void
+ofservice_reconfigure(struct ofservice *ofservice,
+ const struct ofproto_controller *c)
+{
+ ofservice->probe_interval = c->probe_interval;
+ ofservice->rate_limit = c->rate_limit;
+ ofservice->burst_limit = c->burst_limit;
+}
+
+/* Creates a new ofservice in 'ofproto'. Returns 0 if successful, otherwise a
+ * positive errno value. */
+static int
+ofservice_create(struct ofproto *ofproto, const struct ofproto_controller *c)
+{
+ struct ofservice *ofservice;
+ struct pvconn *pvconn;
+ int error;
+
+ error = pvconn_open(c->target, &pvconn);
+ if (error) {
+ return error;
+ }
+
+ ofservice = xzalloc(sizeof *ofservice);
+ hmap_insert(&ofproto->services, &ofservice->node,
+ hash_string(c->target, 0));
+ ofservice->pvconn = pvconn;
+
+ ofservice_reconfigure(ofservice, c);
+
+ return 0;
+}
+
+static void
+ofservice_destroy(struct ofproto *ofproto, struct ofservice *ofservice)
+{
+ hmap_remove(&ofproto->services, &ofservice->node);
+ pvconn_close(ofservice->pvconn);
+ free(ofservice);
+}
+
+/* Finds and returns the ofservice within 'ofproto' that has the given
+ * 'target', or a null pointer if none exists. */
+static struct ofservice *
+ofservice_lookup(struct ofproto *ofproto, const char *target)
+{
+ struct ofservice *ofservice;
+
+ HMAP_FOR_EACH_WITH_HASH (ofservice, struct ofservice, node,
+ hash_string(target, 0), &ofproto->services) {
+ if (!strcmp(pvconn_get_name(ofservice->pvconn), target)) {
+ return ofservice;
+ }
+ }
+ return NULL;
+}
\f
static bool
rule_has_out_port(const struct wdp_rule *rule, uint16_t out_port)
}
flags = ntohs(osc->flags);
- if (ofconn->type == OFCONN_CONTROLLER && ofconn->role != NX_ROLE_SLAVE) {
+ if (ofconn->type == OFCONN_PRIMARY && ofconn->role != NX_ROLE_SLAVE) {
switch (flags & OFPC_FRAG_MASK) {
case OFPC_FRAG_NORMAL:
wdp_set_drop_frags(p->wdp, false);
static int
reject_slave_controller(struct ofconn *ofconn, const struct ofp_header *oh)
{
- if (ofconn->type == OFCONN_CONTROLLER && ofconn->role == NX_ROLE_SLAVE) {
+ if (ofconn->type == OFCONN_PRIMARY && ofconn->role == NX_ROLE_SLAVE) {
static struct vlog_rate_limit perm_rl = VLOG_RATE_LIMIT_INIT(1, 5);
char *type_name;
}
nrr = (struct nx_role_request *) msg;
- if (ofconn->type != OFCONN_CONTROLLER) {
+ if (ofconn->type != OFCONN_PRIMARY) {
VLOG_WARN_RL(&rl, "ignoring role request on non-controller "
"connection");
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
char *target; /* e.g. "tcp:127.0.0.1" */
int max_backoff; /* Maximum reconnection backoff, in seconds. */
int probe_interval; /* Max idle time before probing, in seconds. */
- enum ofproto_fail_mode fail; /* Controller failure handling mode. */
enum ofproto_band band; /* In-band or out-of-band? */
/* Discovery options. */
void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
void ofproto_set_controllers(struct ofproto *,
const struct ofproto_controller *, size_t n);
+void ofproto_set_fail_mode(struct ofproto *, enum ofproto_fail_mode fail_mode);
void ofproto_reconnect_controllers(struct ofproto *);
void ofproto_set_extra_in_band_remotes(struct ofproto *,
const struct sockaddr_in *, size_t n);
const char *mfr_desc, const char *hw_desc,
const char *sw_desc, const char *serial_desc,
const char *dp_desc);
-int ofproto_set_listeners(struct ofproto *, const struct svec *listeners);
int ofproto_set_snoops(struct ofproto *, const struct svec *snoops);
int ofproto_set_netflow(struct ofproto *,
const struct netflow_options *nf_options);
/* Configuration querying. */
uint64_t ofproto_get_datapath_id(const struct ofproto *);
-bool ofproto_has_controller(const struct ofproto *);
+bool ofproto_has_primary_controller(const struct ofproto *);
+enum ofproto_fail_mode ofproto_get_fail_mode(const struct ofproto *);
void ofproto_get_listeners(const struct ofproto *, struct svec *);
void ofproto_get_snoops(const struct ofproto *, struct svec *);
void ofproto_get_all_flows(struct ofproto *p, struct ds *);
bool (*normal_cb)(const flow_t *, const struct ofpbuf *packet,
struct xflow_actions *, tag_type *,
uint16_t *nf_output_iface, void *aux);
- void (*account_flow_cb)(const flow_t *, const union xflow_action *,
- size_t n_actions, unsigned long long int n_bytes,
- void *aux);
+ void (*account_flow_cb)(const flow_t *, tag_type tags,
+ const union xflow_action *, size_t n_actions,
+ unsigned long long int n_bytes, void *aux);
void (*account_checkpoint_cb)(void *aux);
};
void ofproto_revalidate(struct ofproto *, tag_type);
{
const struct ofproto *ofproto = ofproto_;
uint64_t datapath_id;
- struct svec listeners;
- size_t i;
datapath_id = ofproto_get_datapath_id(ofproto);
if (datapath_id) {
status_reply_put(sr, "datapath-id=%016"PRIx64, datapath_id);
}
-
- svec_init(&listeners);
- ofproto_get_listeners(ofproto, &listeners);
- for (i = 0; i < listeners.n; i++) {
- status_reply_put(sr, "management%zu=%s", i, listeners.names[i]);
- }
- svec_destroy(&listeners);
}
static void
wx_rule_update_time(wx, rule, stats);
rule->packet_count += stats->n_packets;
rule->byte_count += stats->n_bytes;
- /* XXX netflow_flow_update_flags(&rule->nf_flow, stats->ip_tos,
- stats->tcp_flags); */
+ /* XXX netflow_flow_update_flags(&rule->nf_flow, stats->tcp_flags); */
}
}
used = xflow_flow_stats_to_msec(&xflow_flow->stats);
if (used > stats->used) {
stats->used = used;
- if (xflow_flow->key.dl_type == htons(ETH_TYPE_IP)
- && xflow_flow->key.nw_proto == IP_TYPE_TCP) {
- stats->ip_tos = xflow_flow->stats.ip_tos;
- }
}
stats->tcp_flags |= xflow_flow->stats.tcp_flags;
}
#if HAVE_OPENSSL
/* Configure SSL. */
- stream_ssl_set_private_key_file(query_db_string(db, private_key_file));
- stream_ssl_set_certificate_file(query_db_string(db, certificate_file));
+ stream_ssl_set_key_and_cert(query_db_string(db, private_key_file),
+ query_db_string(db, certificate_file));
stream_ssl_set_ca_cert_file(query_db_string(db, ca_cert_file),
bootstrap_ca_cert);
#endif
AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
[add-br br0],
- [set-controller tcp:1.2.3.4],
- [get-controller],
- [get-controller default],
[get-controller br0],
-
[set-controller br0 tcp:4.5.6.7],
- [get-controller],
- [get-controller default],
- [get-controller br0],
-
- [del-controller],
- [get-controller],
- [get-controller default],
- [get-controller br0],
-
- [set-controller default tcp:8.9.10.11],
- [get-controller],
- [get-controller default],
- [get-controller br0],
-
- [del-controller default],
- [get-controller],
- [get-controller default],
[get-controller br0],
[del-controller br0],
- [get-controller],
- [get-controller default],
- [get-controller br0],
-
- [set-controller default tcp:1.2.3.4 tcp:4.5.6.7],
- [get-controller],
- [get-controller default],
[get-controller br0],
[set-controller br0 tcp:8.9.10.11 tcp:5.4.3.2],
- [get-controller],
- [get-controller default],
[get-controller br0])], [0], [
-tcp:1.2.3.4
-tcp:1.2.3.4
-tcp:1.2.3.4
-tcp:1.2.3.4
-tcp:1.2.3.4
tcp:4.5.6.7
-tcp:4.5.6.7
-
-tcp:8.9.10.11
-tcp:8.9.10.11
-tcp:4.5.6.7
-
-
-
-tcp:4.5.6.7
-
-
-
-
-
-tcp:1.2.3.4\ntcp:4.5.6.7
-tcp:1.2.3.4\ntcp:4.5.6.7
-tcp:1.2.3.4\ntcp:4.5.6.7
-
-tcp:1.2.3.4\ntcp:4.5.6.7
-tcp:1.2.3.4\ntcp:4.5.6.7
tcp:5.4.3.2\ntcp:8.9.10.11
], [], [OVS_VSCTL_CLEANUP])
OVS_VSCTL_CLEANUP
datapath_id : []
datapath_type : ""
external_ids : {}
+fail_mode : []
flood_vlans : []
mirrors : []
name : "br0"
add_id_to_interface : false
engine_id : []
engine_type : []
+external_ids : {}
targets : ["1.2.3.4:567"]
]], [ignore], [test ! -e pid || kill `cat pid`])
AT_CHECK([RUN_OVS_VSCTL([list interx x])],
datapath_id : []
datapath_type : ""
external_ids : {}
+fail_mode : []
flood_vlans : []
mirrors : []
name : "br0"
.IP
This option may be useful for debugging quality of service setups.
.
+.IP "\fB\-\-with\-flows \fIfile\fR"
+When a switch connects, push the flow entries as described in
+\fIfile\fR. Each line in \fIfile\fR is a flow entry in the format
+described for the \fBadd\-flows\fR command in the \fBFlow Syntax\fR
+section of the \fBovs\-ofctl\fR(8) man page.
+.
.SS "Public Key Infrastructure Options"
.so lib/ssl.man
.so lib/ssl-peer-ca-cert.man
.
.BR ovs\-openflowd (8),
.BR ovs\-appctl (8),
+.BR ovs\-ofctl (8),
.BR ovs\-dpctl (8)
#include <limits.h>
#include <signal.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include "command-line.h"
/* -q, --queue: OpenFlow queue to use, or the default queue if UINT32_MAX. */
static uint32_t queue_id = UINT32_MAX;
+/* --with-flows: File with flows to send to switch, or null to not load
+ * any default flows. */
+static FILE *flow_file = NULL;
+
/* --unixctl: Name of unixctl socket, or null to use the default. */
static char *unixctl_path = NULL;
{
sw->rconn = rconn_create(60, 0);
rconn_connect_unreliably(sw->rconn, vconn, NULL);
+
+ /* If it was set, rewind 'flow_file' to the beginning, since a
+ * previous call to lswitch_create() will leave the stream at the
+ * end. */
+ if (flow_file) {
+ rewind(flow_file);
+ }
sw->lswitch = lswitch_create(sw->rconn, learn_macs, exact_flows,
set_up_flows ? max_idle : -1,
- action_normal);
+ action_normal, flow_file);
+
lswitch_set_queue(sw->lswitch, queue_id);
}
OPT_MAX_IDLE = UCHAR_MAX + 1,
OPT_PEER_CA_CERT,
OPT_MUTE,
+ OPT_WITH_FLOWS,
OPT_UNIXCTL,
VLOG_OPTION_ENUMS
};
{"max-idle", required_argument, 0, OPT_MAX_IDLE},
{"mute", no_argument, 0, OPT_MUTE},
{"queue", required_argument, 0, 'q'},
+ {"with-flows", required_argument, 0, OPT_WITH_FLOWS},
{"unixctl", required_argument, 0, OPT_UNIXCTL},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
queue_id = atoi(optarg);
break;
+ case OPT_WITH_FLOWS:
+ flow_file = fopen(optarg, "r");
+ if (flow_file == NULL) {
+ ovs_fatal(errno, "%s: open", optarg);
+ }
+ break;
+
case OPT_UNIXCTL:
unixctl_path = optarg;
break;
" -N, --normal use OFPAT_NORMAL action\n"
" -w, --wildcard use wildcards, not exact-match rules\n"
" -q, --queue=QUEUE OpenFlow queue ID to use for output\n"
+ " --with-flows FILE use the flows from FILE\n"
" --unixctl=SOCKET override default control socket name\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
.IP \fBoutput\fR:\fIport\fR
Outputs the packet on the port specified by \fIport\fR.
.
+.IP \fBenqueue\fR:\fIport\fB:\fIqueue\fR
+Enqueues the packet on the specified \fIqueue\fR within port
+\fIport\fR. The number of supported queues depends on the switch;
+some OpenFlow implementations do not support queuing at all.
+.
.IP \fBnormal\fR
Subjects the packet to the device's normal L2/L3 processing. (This
action is not implemented by all OpenFlow switches.)
*/
#include <config.h>
-#include <arpa/inet.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
-#include <sys/socket.h>
#include <net/if.h>
-#include <netinet/in.h>
#include <signal.h>
-#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dynamic-string.h"
#include "netdev.h"
#include "netlink.h"
-#include "xflow-util.h"
+#include "ofp-parse.h"
#include "ofp-print.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
-#include "packets.h"
#include "random.h"
-#include "socket-util.h"
#include "stream-ssl.h"
#include "timeval.h"
#include "util.h"
#include "vconn.h"
#include "vlog.h"
#include "xfif.h"
+#include "xflow-util.h"
#include "xtoxll.h"
VLOG_DEFINE_THIS_MODULE(ofctl)
-#define DEFAULT_IDLE_TIMEOUT 60
#define MOD_PORT_CMD_UP "up"
#define MOD_PORT_CMD_DOWN "down"
dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
}
-
-static uint32_t
-str_to_u32(const char *str)
-{
- char *tail;
- uint32_t value;
-
- errno = 0;
- value = strtoul(str, &tail, 0);
- if (errno == EINVAL || errno == ERANGE || *tail) {
- ovs_fatal(0, "invalid numeric format %s", str);
- }
- return value;
-}
-
-static uint64_t
-str_to_u64(const char *str)
-{
- char *tail;
- uint64_t value;
-
- errno = 0;
- value = strtoull(str, &tail, 0);
- if (errno == EINVAL || errno == ERANGE || *tail) {
- ovs_fatal(0, "invalid numeric format %s", str);
- }
- return value;
-}
-
-static void
-str_to_mac(const char *str, uint8_t mac[6])
-{
- if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
- != ETH_ADDR_SCAN_COUNT) {
- ovs_fatal(0, "invalid mac address %s", str);
- }
-}
-
-static uint32_t
-str_to_ip(const char *str_, uint32_t *ip)
-{
- char *str = xstrdup(str_);
- char *save_ptr = NULL;
- const char *name, *netmask;
- struct in_addr in_addr;
- int n_wild, retval;
-
- name = strtok_r(str, "/", &save_ptr);
- retval = name ? lookup_ip(name, &in_addr) : EINVAL;
- if (retval) {
- ovs_fatal(0, "%s: could not convert to IP address", str);
- }
- *ip = in_addr.s_addr;
-
- netmask = strtok_r(NULL, "/", &save_ptr);
- if (netmask) {
- uint8_t o[4];
- if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8,
- &o[0], &o[1], &o[2], &o[3]) == 4) {
- uint32_t nm = (o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3];
- int i;
-
- /* Find first 1-bit. */
- for (i = 0; i < 32; i++) {
- if (nm & (1u << i)) {
- break;
- }
- }
- n_wild = i;
-
- /* Verify that the rest of the bits are 1-bits. */
- for (; i < 32; i++) {
- if (!(nm & (1u << i))) {
- ovs_fatal(0, "%s: %s is not a valid netmask",
- str, netmask);
- }
- }
- } else {
- int prefix = atoi(netmask);
- if (prefix <= 0 || prefix > 32) {
- ovs_fatal(0, "%s: network prefix bits not between 1 and 32",
- str);
- }
- n_wild = 32 - prefix;
- }
- } else {
- n_wild = 0;
- }
-
- free(str);
- return n_wild;
-}
-
static uint16_t
str_to_port_no(const char *vconn_name, const char *str)
{
return port_idx;
}
-static void *
-put_action(struct ofpbuf *b, size_t size, uint16_t type)
-{
- struct ofp_action_header *ah = ofpbuf_put_zeros(b, size);
- ah->type = htons(type);
- ah->len = htons(size);
- return ah;
-}
-
-static struct ofp_action_output *
-put_output_action(struct ofpbuf *b, uint16_t port)
-{
- struct ofp_action_output *oao = put_action(b, sizeof *oao, OFPAT_OUTPUT);
- oao->port = htons(port);
- return oao;
-}
-
-static void
-put_dl_addr_action(struct ofpbuf *b, uint16_t type, const char *addr)
-{
- struct ofp_action_dl_addr *oada = put_action(b, sizeof *oada, type);
- str_to_mac(addr, oada->dl_addr);
-}
-
-
-static bool
-parse_port_name(const char *name, uint16_t *port)
-{
- struct pair {
- const char *name;
- uint16_t value;
- };
- static const struct pair pairs[] = {
-#define DEF_PAIR(NAME) {#NAME, OFPP_##NAME}
- DEF_PAIR(IN_PORT),
- DEF_PAIR(TABLE),
- DEF_PAIR(NORMAL),
- DEF_PAIR(FLOOD),
- DEF_PAIR(ALL),
- DEF_PAIR(CONTROLLER),
- DEF_PAIR(LOCAL),
- DEF_PAIR(NONE),
-#undef DEF_PAIR
- };
- static const int n_pairs = ARRAY_SIZE(pairs);
- size_t i;
-
- for (i = 0; i < n_pairs; i++) {
- if (!strcasecmp(name, pairs[i].name)) {
- *port = pairs[i].value;
- return true;
- }
- }
- return false;
-}
-
-static void
-str_to_action(char *str, struct ofpbuf *b)
-{
- char *act, *arg;
- char *saveptr = NULL;
- bool drop = false;
- int n_actions;
-
- for (act = strtok_r(str, ", \t\r\n", &saveptr), n_actions = 0; act;
- act = strtok_r(NULL, ", \t\r\n", &saveptr), n_actions++)
- {
- uint16_t port;
-
- if (drop) {
- ovs_fatal(0, "Drop actions must not be followed by other actions");
- }
-
- /* Arguments are separated by colons */
- arg = strchr(act, ':');
- if (arg) {
- *arg = '\0';
- arg++;
- }
-
- if (!strcasecmp(act, "mod_vlan_vid")) {
- struct ofp_action_vlan_vid *va;
- va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID);
- va->vlan_vid = htons(str_to_u32(arg));
- } else if (!strcasecmp(act, "mod_vlan_pcp")) {
- struct ofp_action_vlan_pcp *va;
- va = put_action(b, sizeof *va, OFPAT_SET_VLAN_PCP);
- va->vlan_pcp = str_to_u32(arg);
- } else if (!strcasecmp(act, "strip_vlan")) {
- struct ofp_action_header *ah;
- ah = put_action(b, sizeof *ah, OFPAT_STRIP_VLAN);
- ah->type = htons(OFPAT_STRIP_VLAN);
- } else if (!strcasecmp(act, "mod_dl_src")) {
- put_dl_addr_action(b, OFPAT_SET_DL_SRC, arg);
- } else if (!strcasecmp(act, "mod_dl_dst")) {
- put_dl_addr_action(b, OFPAT_SET_DL_DST, arg);
- } else if (!strcasecmp(act, "mod_nw_src")) {
- struct ofp_action_nw_addr *na;
- na = put_action(b, sizeof *na, OFPAT_SET_NW_SRC);
- str_to_ip(arg, &na->nw_addr);
- } else if (!strcasecmp(act, "mod_nw_dst")) {
- struct ofp_action_nw_addr *na;
- na = put_action(b, sizeof *na, OFPAT_SET_NW_DST);
- str_to_ip(arg, &na->nw_addr);
- } else if (!strcasecmp(act, "mod_tp_src")) {
- struct ofp_action_tp_port *ta;
- ta = put_action(b, sizeof *ta, OFPAT_SET_TP_SRC);
- ta->tp_port = htons(str_to_u32(arg));
- } else if (!strcasecmp(act, "mod_tp_dst")) {
- struct ofp_action_tp_port *ta;
- ta = put_action(b, sizeof *ta, OFPAT_SET_TP_DST);
- ta->tp_port = htons(str_to_u32(arg));
- } else if (!strcasecmp(act, "mod_nw_tos")) {
- struct ofp_action_nw_tos *nt;
- nt = put_action(b, sizeof *nt, OFPAT_SET_NW_TOS);
- nt->nw_tos = str_to_u32(arg);
- } else if (!strcasecmp(act, "resubmit")) {
- struct nx_action_resubmit *nar;
- nar = put_action(b, sizeof *nar, OFPAT_VENDOR);
- nar->vendor = htonl(NX_VENDOR_ID);
- nar->subtype = htons(NXAST_RESUBMIT);
- nar->in_port = htons(str_to_u32(arg));
- } else if (!strcasecmp(act, "set_tunnel")) {
- struct nx_action_set_tunnel *nast;
- nast = put_action(b, sizeof *nast, OFPAT_VENDOR);
- nast->vendor = htonl(NX_VENDOR_ID);
- nast->subtype = htons(NXAST_SET_TUNNEL);
- nast->tun_id = htonl(str_to_u32(arg));
- } else if (!strcasecmp(act, "output")) {
- put_output_action(b, str_to_u32(arg));
- } else if (!strcasecmp(act, "drop")) {
- /* A drop action in OpenFlow occurs by just not setting
- * an action. */
- drop = true;
- if (n_actions) {
- ovs_fatal(0, "Drop actions must not be preceded by other "
- "actions");
- }
- } else if (!strcasecmp(act, "CONTROLLER")) {
- struct ofp_action_output *oao;
- oao = put_output_action(b, OFPP_CONTROLLER);
-
- /* Unless a numeric argument is specified, we send the whole
- * packet to the controller. */
- if (arg && (strspn(arg, "0123456789") == strlen(arg))) {
- oao->max_len = htons(str_to_u32(arg));
- } else {
- oao->max_len = htons(UINT16_MAX);
- }
- } else if (parse_port_name(act, &port)) {
- put_output_action(b, port);
- } else if (strspn(act, "0123456789") == strlen(act)) {
- put_output_action(b, str_to_u32(act));
- } else {
- ovs_fatal(0, "Unknown action: %s", act);
- }
- }
-}
-
-struct protocol {
- const char *name;
- uint16_t dl_type;
- uint8_t nw_proto;
-};
-
-static bool
-parse_protocol(const char *name, const struct protocol **p_out)
-{
- static const struct protocol protocols[] = {
- { "ip", ETH_TYPE_IP, 0 },
- { "arp", ETH_TYPE_ARP, 0 },
- { "icmp", ETH_TYPE_IP, IP_TYPE_ICMP },
- { "tcp", ETH_TYPE_IP, IP_TYPE_TCP },
- { "udp", ETH_TYPE_IP, IP_TYPE_UDP },
- };
- const struct protocol *p;
-
- for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
- if (!strcmp(p->name, name)) {
- *p_out = p;
- return true;
- }
- }
- *p_out = NULL;
- return false;
-}
-
-struct field {
- const char *name;
- uint32_t wildcard;
- enum { F_U8, F_U16, F_MAC, F_IP } type;
- size_t offset, shift;
-};
-
-static bool
-parse_field(const char *name, const struct field **f_out)
-{
-#define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
- static const struct field fields[] = {
- { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 },
- { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 },
- { "dl_vlan_pcp", OFPFW_DL_VLAN_PCP, F_U8, F_OFS(dl_vlan_pcp), 0 },
- { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src), 0 },
- { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst), 0 },
- { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type), 0 },
- { "nw_src", OFPFW_NW_SRC_MASK, F_IP,
- F_OFS(nw_src), OFPFW_NW_SRC_SHIFT },
- { "nw_dst", OFPFW_NW_DST_MASK, F_IP,
- F_OFS(nw_dst), OFPFW_NW_DST_SHIFT },
- { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto), 0 },
- { "nw_tos", OFPFW_NW_TOS, F_U8, F_OFS(nw_tos), 0 },
- { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src), 0 },
- { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst), 0 },
- { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type), 0 },
- { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code), 0 }
- };
- const struct field *f;
-
- for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
- if (!strcmp(f->name, name)) {
- *f_out = f;
- return true;
- }
- }
- *f_out = NULL;
- return false;
-}
-
-static void
-str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions,
- uint8_t *table_idx, uint16_t *out_port, uint16_t *priority,
- uint16_t *idle_timeout, uint16_t *hard_timeout,
- uint64_t *cookie)
-{
- struct ofp_match normalized;
- char *save_ptr = NULL;
- char *name;
- uint32_t wildcards;
-
- if (table_idx) {
- *table_idx = 0xff;
- }
- if (out_port) {
- *out_port = OFPP_NONE;
- }
- if (priority) {
- *priority = OFP_DEFAULT_PRIORITY;
- }
- if (idle_timeout) {
- *idle_timeout = DEFAULT_IDLE_TIMEOUT;
- }
- if (hard_timeout) {
- *hard_timeout = OFP_FLOW_PERMANENT;
- }
- if (cookie) {
- *cookie = 0;
- }
- if (actions) {
- char *act_str = strstr(string, "action");
- if (!act_str) {
- ovs_fatal(0, "must specify an action");
- }
- *act_str = '\0';
-
- act_str = strchr(act_str + 1, '=');
- if (!act_str) {
- ovs_fatal(0, "must specify an action");
- }
-
- act_str++;
-
- str_to_action(act_str, actions);
- }
- memset(match, 0, sizeof *match);
- wildcards = OFPFW_ALL;
- for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
- name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
- const struct protocol *p;
-
- if (parse_protocol(name, &p)) {
- wildcards &= ~OFPFW_DL_TYPE;
- match->dl_type = htons(p->dl_type);
- if (p->nw_proto) {
- wildcards &= ~OFPFW_NW_PROTO;
- match->nw_proto = p->nw_proto;
- }
- } else {
- const struct field *f;
- char *value;
-
- value = strtok_r(NULL, ", \t\r\n", &save_ptr);
- if (!value) {
- ovs_fatal(0, "field %s missing value", name);
- }
-
- if (table_idx && !strcmp(name, "table")) {
- *table_idx = atoi(value);
- if (*table_idx < 0 || *table_idx > 31) {
- ovs_fatal(0, "table %s is invalid, must be between 0 and 31", value);
- }
- } else if (out_port && !strcmp(name, "out_port")) {
- *out_port = atoi(value);
- } else if (priority && !strcmp(name, "priority")) {
- *priority = atoi(value);
- } else if (idle_timeout && !strcmp(name, "idle_timeout")) {
- *idle_timeout = atoi(value);
- } else if (hard_timeout && !strcmp(name, "hard_timeout")) {
- *hard_timeout = atoi(value);
- } else if (cookie && !strcmp(name, "cookie")) {
- *cookie = str_to_u64(value);
- } else if (!strcmp(name, "tun_id_wild")) {
- wildcards |= NXFW_TUN_ID;
- } else if (parse_field(name, &f)) {
- void *data = (char *) match + f->offset;
- if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
- wildcards |= f->wildcard;
- } else {
- wildcards &= ~f->wildcard;
- if (f->wildcard == OFPFW_IN_PORT
- && parse_port_name(value, (uint16_t *) data)) {
- /* Nothing to do. */
- } else if (f->type == F_U8) {
- *(uint8_t *) data = str_to_u32(value);
- } else if (f->type == F_U16) {
- *(uint16_t *) data = htons(str_to_u32(value));
- } else if (f->type == F_MAC) {
- str_to_mac(value, data);
- } else if (f->type == F_IP) {
- wildcards |= str_to_ip(value, data) << f->shift;
- } else {
- NOT_REACHED();
- }
- }
- } else {
- ovs_fatal(0, "unknown keyword %s", name);
- }
- }
- }
- match->wildcards = htonl(wildcards);
-
- normalized = *match;
- normalize_match(&normalized);
- if (memcmp(match, &normalized, sizeof normalized)) {
- char *old = ofp_match_to_literal_string(match);
- char *new = ofp_match_to_literal_string(&normalized);
- VLOG_WARN("The specified flow is not in normal form:");
- VLOG_WARN(" as specified: %s", old);
- VLOG_WARN("as normalized: %s", new);
- free(old);
- free(new);
- }
-}
-
static void
do_dump_flows(int argc, char *argv[])
{
struct ofpbuf *request;
req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
- str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
- &req->table_id, &out_port, NULL, NULL, NULL, NULL);
+ parse_ofp_str(argc > 2 ? argv[2] : "", &req->match, NULL,
+ &req->table_id, &out_port, NULL, NULL, NULL, NULL);
memset(&req->pad, 0, sizeof req->pad);
req->out_port = htons(out_port);
uint16_t out_port;
req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
- str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
- &req->table_id, &out_port, NULL, NULL, NULL, NULL);
+ parse_ofp_str(argc > 2 ? argv[2] : "", &req->match, NULL,
+ &req->table_id, &out_port, NULL, NULL, NULL, NULL);
memset(&req->pad, 0, sizeof req->pad);
req->out_port = htons(out_port);
struct ofp_match match;
uint8_t table_idx;
- /* Parse and send. str_to_flow() will expand and reallocate the data in
- * 'buffer', so we can't keep pointers to across the str_to_flow() call. */
+ /* Parse and send. parse_ofp_str() will expand and reallocate the
+ * data in 'buffer', so we can't keep pointers to across the
+ * parse_ofp_str() call. */
make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
- str_to_flow(argv[2], &match, buffer,
- &table_idx, NULL, &priority, &idle_timeout, &hard_timeout,
- &cookie);
+ parse_ofp_str(argv[2], &match, buffer,
+ &table_idx, NULL, &priority, &idle_timeout, &hard_timeout,
+ &cookie);
ofm = buffer->data;
ofm->match = match;
ofm->command = htons(OFPFC_ADD);
continue;
}
- /* Parse and send. str_to_flow() will expand and reallocate the data
- * in 'buffer', so we can't keep pointers to across the str_to_flow()
- * call. */
+ /* Parse and send. parse_ofp_str() will expand and reallocate
+ * the data in 'buffer', so we can't keep pointers to across the
+ * parse_ofp_str() call. */
make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
- str_to_flow(line, &match, buffer,
- &table_idx, NULL, &priority, &idle_timeout, &hard_timeout,
- &cookie);
+ parse_ofp_str(line, &match, buffer, &table_idx, NULL, &priority,
+ &idle_timeout, &hard_timeout, &cookie);
ofm = buffer->data;
ofm->match = match;
ofm->command = htons(OFPFC_ADD);
struct ofp_match match;
uint8_t table_idx;
- /* Parse and send. str_to_flow() will expand and reallocate the data in
- * 'buffer', so we can't keep pointers to across the str_to_flow() call. */
+ /* Parse and send. parse_ofp_str() will expand and reallocate the
+ * data in 'buffer', so we can't keep pointers to across the
+ * parse_ofp_str() call. */
make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
- str_to_flow(argv[2], &match, buffer,
- &table_idx, NULL, &priority, &idle_timeout, &hard_timeout,
- &cookie);
+ parse_ofp_str(argv[2], &match, buffer,
+ &table_idx, NULL, &priority, &idle_timeout, &hard_timeout,
+ &cookie);
ofm = buffer->data;
ofm->match = match;
if (strict) {
/* Parse and send. */
ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
- str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL,
- &out_port, &priority, NULL, NULL, NULL);
+ parse_ofp_str(argc > 2 ? argv[2] : "", &ofm->match, NULL, &table_idx,
+ &out_port, &priority, NULL, NULL, NULL);
if (strict) {
ofm->command = htons(OFPFC_DELETE_STRICT);
} else {
.so lib/xfif.man
.
.PP
-The optional \fIcontroller\fR arguments specify how to connect to
-the OpenFlow controller. It takes one of the following forms:
+The optional \fIcontroller\fR arguments specify how to connect to the
+OpenFlow controller or controllers. Each takes one of the following
+forms:
.
.so lib/vconn-active.man
.
.PP
+When multiple controllers are configured, \fBovs\-openflowd\fR
+connects to all of them simultaneously. OpenFlow 1.0 does not specify
+how multiple controllers coordinate in interacting with a single
+switch, so more than one controller should be specified only if the
+controllers are themselves designed to coordinate with each other.
+(The Nicira-defined \fBNXT_ROLE\fR OpenFlow vendor extension may be
+useful for this.)
+.PP
If no \fIcontroller\fR is specified, \fBovs\-openflowd\fR attempts to
discover the location of a controller automatically (see below).
.
/* Controller configuration. */
struct ofproto_controller *controllers;
size_t n_controllers;
+ enum ofproto_fail_mode fail_mode;
/* Datapath. */
uint64_t datapath_id; /* Datapath ID. */
const char *dp_desc; /* Datapath description. */
/* Related vconns and network devices. */
- struct svec listeners; /* Listen for management connections. */
struct svec snoops; /* Listen for controller snooping conns. */
/* Failure behavior. */
}
ofproto_set_desc(ofproto, s.mfr_desc, s.hw_desc, s.sw_desc,
s.serial_desc, s.dp_desc);
- if (!s.listeners.n) {
- svec_add_nocopy(&s.listeners, xasprintf("punix:%s/%s.mgmt",
- ovs_rundir, s.dp_name));
- } else if (s.listeners.n == 1 && !strcmp(s.listeners.names[0], "none")) {
- svec_clear(&s.listeners);
- }
- error = ofproto_set_listeners(ofproto, &s.listeners);
- if (error) {
- ovs_fatal(error, "failed to configure management connections");
- }
error = ofproto_set_snoops(ofproto, &s.snoops);
if (error) {
ovs_fatal(error,
ovs_fatal(error, "failed to configure STP");
}
ofproto_set_controllers(ofproto, s.controllers, s.n_controllers);
+ ofproto_set_fail_mode(ofproto, s.fail_mode);
daemonize_complete();
};
char *short_options = long_options_to_short_options(long_options);
struct ofproto_controller controller_opts;
+ struct svec controllers;
+ int i;
/* Set defaults that we can figure out before parsing options. */
controller_opts.target = NULL;
controller_opts.max_backoff = 8;
controller_opts.probe_interval = 5;
- controller_opts.fail = OFPROTO_FAIL_STANDALONE;
controller_opts.band = OFPROTO_IN_BAND;
controller_opts.accept_re = NULL;
controller_opts.update_resolv_conf = true;
controller_opts.rate_limit = 0;
controller_opts.burst_limit = 0;
+ s->fail_mode = OFPROTO_FAIL_STANDALONE;
s->datapath_id = 0;
s->mfr_desc = NULL;
s->hw_desc = NULL;
s->sw_desc = NULL;
s->serial_desc = NULL;
s->dp_desc = NULL;
- svec_init(&s->listeners);
+ svec_init(&controllers);
svec_init(&s->snoops);
s->max_idle = 0;
s->enable_stp = false;
case OPT_FAIL_MODE:
if (!strcmp(optarg, "open") || !strcmp(optarg, "standalone")) {
- controller_opts.fail = OFPROTO_FAIL_STANDALONE;
+ s->fail_mode = OFPROTO_FAIL_STANDALONE;
} else if (!strcmp(optarg, "closed")
|| !strcmp(optarg, "secure")) {
- controller_opts.fail = OFPROTO_FAIL_SECURE;
+ s->fail_mode = OFPROTO_FAIL_SECURE;
} else {
ovs_fatal(0, "--fail argument must be \"standalone\" "
"or \"secure\"");
break;
case 'l':
- svec_add(&s->listeners, optarg);
+ svec_add(&controllers, optarg);
break;
case OPT_SNOOP:
argc -= optind;
argv += optind;
- if (argc < 1 || argc > 2) {
- ovs_fatal(0, "need one or two non-option arguments; "
+ if (argc < 1) {
+ ovs_fatal(0, "need at least one non-option arguments; "
"use --help for usage");
}
/* Local vconns. */
xf_parse_name(argv[0], &s->dp_name, &s->dp_type);
- /* Controllers. */
- s->n_controllers = argc > 1 ? argc - 1 : 1;
+ /* Figure out controller names. */
+ if (!controllers.n) {
+ svec_add_nocopy(&controllers,
+ xasprintf("punix:%s/%s.mgmt", ovs_rundir, s->dp_name));
+ }
+ for (i = 1; i < argc; i++) {
+ svec_add(&controllers, argv[i]);
+ }
+ if (argc < 2) {
+ svec_add(&controllers, "discover");
+ }
+
+ /* Set up controllers. */
+ s->n_controllers = controllers.n;
s->controllers = xmalloc(s->n_controllers * sizeof *s->controllers);
if (argc > 1) {
size_t i;
for (i = 0; i < s->n_controllers; i++) {
s->controllers[i] = controller_opts;
- s->controllers[i].target = argv[i + 1];
+ s->controllers[i].target = controllers.names[i];
}
- } else {
- s->controllers[0] = controller_opts;
- s->controllers[0].target = "discover";
}
/* Sanity check. */
exit 1
fi
if test "$keytype" != rsa && test "$keytype" != dsa; then
- echo "$0: argument to -k or --key must be rsa or dsa"
+ echo "$0: argument to -k or --key must be rsa or dsa" >&2
exit 1
fi
if test "$bits" -lt 1024; then
- echo "$0: argument to -B or --bits must be at least 1024"
+ echo "$0: argument to -B or --bits must be at least 1024" >&2
exit 1
fi
if test -z "$dsaparam"; then
*) $log="$PWD/$log" ;;
esac
+logdir=$(dirname "$log")
+if test ! -d "$logdir"; then
+ mkdir -p -m755 "$logdir" 2>/dev/null || true
+ if test ! -d "$logdir"; then
+ echo "$0: log directory $logdir does not exist and cannot be created" >&2
+ exit 1
+ fi
+fi
+
if test "$command" = "init"; then
if test -e "$pkidir" && test "$force" != "yes"; then
echo "$0: $pkidir already exists and --force not specified" >&2
????*)
;;
*)
- echo "Prefix $arg1 is too short (less than 4 hex digits)"
+ echo "Prefix $arg1 is too short (less than 4 hex digits)" >&2
exit 0
;;
esac
fingerprint=$(cd "$pkidir/${type}ca/incoming" && echo "$1"*-req.pem | sed 's/-req\.pem$//')
case $fingerprint in
"${1}*")
- echo "No certificate requests matching $1"
+ echo "No certificate requests matching $1" >&2
exit 1
;;
*" "*)
- echo "$1 matches more than one certificate request:"
+ echo "$1 matches more than one certificate request:" >&2
echo $fingerprint | sed 's/ /\
-/g'
+/g' >&2
exit 1
;;
*)
CN = Open vSwitch certificate for $arg1
EOF
if test $keytype = rsa; then
- newkey=rsa:$bits
+ (umask 077 && openssl genrsa -out "$1-privkey.pem" $bits) 1>&3 2>&3 \
+ || exit $?
else
must_exist "$dsaparam"
- newkey=dsa:$dsaparam
+ (umask 077 && openssl gendsa -out "$1-privkey.pem" "$dsaparam") \
+ 1>&3 2>&3 || exit $?
fi
- openssl req -config "$TMP/req.cnf" -text -nodes \
- -newkey $newkey -keyout "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3
+ openssl req -config "$TMP/req.cnf" -new -text \
+ -key "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3
}
sign_request() {
must_exist "$arg1-privkey.pem"
must_not_exist "$arg1-cert.pem"
- openssl x509 -in "$arg1-req.pem" -out "$arg1-cert.pem" \
- -signkey "$arg1-privkey.pem" -req -text 2>&3
+ # Create both the private key and certificate with restricted permissions.
+ (umask 077 && \
+ openssl x509 -in "$arg1-req.pem" -out "$arg1-cert.pem.tmp" \
+ -signkey "$arg1-privkey.pem" -req -text) 2>&3 || exit $?
+
+ # Reset the permissions on the certificate to the user's default.
+ cat "$arg1-cert.pem.tmp" > "$arg1-cert.pem"
+ rm -f "$arg1-cert.pem.tmp"
elif test "$command" = ls; then
check_type "$arg2"
locally, or it can be configured to connect a given bridge to one or
more external OpenFlow controllers, such as NOX.
.
-For each of these commands, a \fIbridge\fR of \fBdefault\fR applies
-the configuration as the default for any bridge that has not been
-explicitly configured. Otherwise, \fIbridge\fR must name a bridge,
-and the settings apply only to that bridge. (Omitting \fIbridge\fR
-entirely usually has the same effect as specifying \fBdefault\fR.)
-.
-.IP "\fBget\-controller\fR [\fIbridge\fR]"
+.IP "\fBget\-controller\fR \fIbridge\fR"
Prints the configured controller target.
.
-.IP "\fBdel\-controller\fR [\fIbridge\fR]"
+.IP "\fBdel\-controller\fR \fIbridge\fR"
Deletes the configured controller target.
.
-.IP "\fBset\-controller\fR [\fIbridge\fR] \fItarget\fR\&..."
-Sets the configured controller target or targets. If more than one
-\fItarget\fR is specified, then \fIbridge\fR may not be omitted. Each
-\fItarget\fR may use any of the following forms:
+.IP "\fBset\-controller\fR \fIbridge\fR \fItarget\fR\&..."
+Sets the configured controller target or targets. Each \fItarget\fR may
+use any of the following forms:
.
.RS
.so lib/vconn-active.man
If this option is set to \fBsecure\fR, \fBovs\-vswitchd\fR will not
set up flows on its own when the controller connection fails.
.
-.IP "\fBget\-fail\-mode\fR [\fIbridge\fR]"
+.IP "\fBget\-fail\-mode\fR \fIbridge\fR"
Prints the configured failure mode.
.
-.IP "\fBdel\-fail\-mode\fR [\fIbridge\fR]"
+.IP "\fBdel\-fail\-mode\fR \fIbridge\fR"
Deletes the configured failure mode.
.
-.IP "\fBset\-fail\-mode\fR [\fIbridge\fR] \fBstandalone\fR|\fBsecure\fR"
+.IP "\fBset\-fail\-mode\fR \fIbridge\fR \fBstandalone\fR|\fBsecure\fR"
Sets the configured failure mode.
.
.SS "SSL Configuration"
identified by mirror name.
.IP "\fBController\fR"
Configuration for an OpenFlow controller. A controller attached to a
-particular bridge may be identified by the bridge's name. The default
-controller controller for an Open vSwitch may be identified by
-specifying \fB.\fR as the record name.
+particular bridge may be identified by the bridge's name.
.IP "\fBNetFlow\fR"
A NetFlow configuration attached to a bridge. Records may be
identified by bridge name.
iface-to-br IFACE print name of bridge that contains IFACE\n\
\n\
Controller commands:\n\
- get-controller [BRIDGE] print the controller for BRIDGE\n\
- del-controller [BRIDGE] delete the controller for BRIDGE\n\
- set-controller [BRIDGE] TARGET set the controller for BRIDGE to TARGET\n\
- get-fail-mode [BRIDGE] print the fail-mode for BRIDGE\n\
- del-fail-mode [BRIDGE] delete the fail-mode for BRIDGE\n\
- set-fail-mode [BRIDGE] MODE set the fail-mode for BRIDGE to MODE\n\
+ get-controller BRIDGE print the controller for BRIDGE\n\
+ del-controller BRIDGE delete the controller for BRIDGE\n\
+ set-controller BRIDGE TARGET set the controller for BRIDGE to TARGET\n\
+ get-fail-mode BRIDGE print the fail-mode for BRIDGE\n\
+ del-fail-mode BRIDGE delete the fail-mode for BRIDGE\n\
+ set-fail-mode BRIDGE MODE set the fail-mode for BRIDGE to MODE\n\
\n\
SSL commands:\n\
get-ssl print the SSL configuration\n\
struct ovsrec_bridge *br_cfg;
char *name;
struct ovsrec_controller **ctrl;
+ char *fail_mode;
size_t n_ctrl;
struct vsctl_bridge *parent;
int vlan;
struct shash bridges;
struct shash ports;
struct shash ifaces;
- struct ovsrec_controller **ctrl;
- size_t n_ctrl;
};
static char *
if (parent) {
br->ctrl = parent->br_cfg->controller;
br->n_ctrl = parent->br_cfg->n_controller;
+ br->fail_mode = parent->br_cfg->fail_mode;
} else {
br->ctrl = br_cfg->controller;
br->n_ctrl = br_cfg->n_controller;
+ br->fail_mode = br_cfg->fail_mode;
}
shash_add(&b->bridges, br->name, br);
return br;
shash_init(&info->ports);
shash_init(&info->ifaces);
- info->ctrl = ovs->controller;
- info->n_ctrl = ovs->n_controller;
-
shash_init(&bridges);
shash_init(&ports);
for (i = 0; i < ovs->n_bridges; i++) {
/* Reset the Open_vSwitch table. */
ovsrec_open_vswitch_set_managers(ctx->ovs, NULL, 0);
- ovsrec_open_vswitch_set_controller(ctx->ovs, NULL, 0);
ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL);
OVSREC_BRIDGE_FOR_EACH (br, idl) {
free_info(&info);
}
-/* Print targets of the 'n_controllers' in 'controllers' on the output for
- * 'ctx'. */
static void
-print_controllers(struct vsctl_context *ctx,
- struct ovsrec_controller **controllers,
- size_t n_controllers)
+cmd_get_controller(struct vsctl_context *ctx)
{
- /* Print the targets in sorted order for reproducibility. */
+ struct vsctl_info info;
+ struct vsctl_bridge *br;
struct svec targets;
size_t i;
+ get_info(ctx->ovs, &info);
+ br = find_bridge(&info, ctx->argv[1], true);
+
+ /* Print the targets in sorted order for reproducibility. */
svec_init(&targets);
- for (i = 0; i < n_controllers; i++) {
- svec_add(&targets, controllers[i]->target);
+ for (i = 0; i < br->n_ctrl; i++) {
+ svec_add(&targets, br->ctrl[i]->target);
}
svec_sort(&targets);
ds_put_format(&ctx->output, "%s\n", targets.names[i]);
}
svec_destroy(&targets);
-}
-
-static void
-cmd_get_controller(struct vsctl_context *ctx)
-{
- struct vsctl_info info;
-
- get_info(ctx->ovs, &info);
-
- if (ctx->argc == 1 || !strcmp(ctx->argv[1], "default")) {
- print_controllers(ctx, info.ctrl, info.n_ctrl);
- } else {
- struct vsctl_bridge *br = find_bridge(&info, ctx->argv[1], true);
- if (br->n_ctrl) {
- print_controllers(ctx, br->ctrl, br->n_ctrl);
- } else {
- print_controllers(ctx, info.ctrl, info.n_ctrl);
- }
- }
free_info(&info);
}
cmd_del_controller(struct vsctl_context *ctx)
{
struct vsctl_info info;
+ struct vsctl_bridge *br;
get_info(ctx->ovs, &info);
+ br = find_real_bridge(&info, ctx->argv[1], true);
- if (ctx->argc == 1 || !strcmp(ctx->argv[1], "default")) {
- if (info.n_ctrl) {
- delete_controllers(info.ctrl, info.n_ctrl);
- ovsrec_open_vswitch_set_controller(ctx->ovs, NULL, 0);
- }
- } else {
- struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
- if (br->ctrl) {
- delete_controllers(br->ctrl, br->n_ctrl);
- ovsrec_bridge_set_controller(br->br_cfg, NULL, 0);
- }
+ if (br->ctrl) {
+ delete_controllers(br->ctrl, br->n_ctrl);
+ ovsrec_bridge_set_controller(br->br_cfg, NULL, 0);
}
free_info(&info);
return controllers;
}
-static void
-set_default_controllers(struct vsctl_context *ctx, char *targets[], size_t n)
-{
- struct ovsrec_controller **controllers;
-
- delete_controllers(ctx->ovs->controller, ctx->ovs->n_controller);
-
- controllers = insert_controllers(ctx->txn, targets, n);
- ovsrec_open_vswitch_set_controller(ctx->ovs, controllers, n);
- free(controllers);
-}
-
static void
cmd_set_controller(struct vsctl_context *ctx)
{
struct vsctl_info info;
+ struct vsctl_bridge *br;
+ struct ovsrec_controller **controllers;
+ size_t n;
get_info(ctx->ovs, &info);
+ br = find_real_bridge(&info, ctx->argv[1], true);
- if (ctx->argc == 2) {
- /* Set one controller in the "Open_vSwitch" table. */
- set_default_controllers(ctx, &ctx->argv[1], 1);
- } else if (!strcmp(ctx->argv[1], "default")) {
- /* Set one or more controllers in the "Open_vSwitch" table. */
- set_default_controllers(ctx, &ctx->argv[2], ctx->argc - 2);
- } else {
- /* Set one or more controllers for a particular bridge. */
- struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
- struct ovsrec_controller **controllers;
- size_t n;
-
- delete_controllers(br->ctrl, br->n_ctrl);
+ delete_controllers(br->ctrl, br->n_ctrl);
- n = ctx->argc - 2;
- controllers = insert_controllers(ctx->txn, &ctx->argv[2], n);
- ovsrec_bridge_set_controller(br->br_cfg, controllers, n);
- free(controllers);
- }
+ n = ctx->argc - 2;
+ controllers = insert_controllers(ctx->txn, &ctx->argv[2], n);
+ ovsrec_bridge_set_controller(br->br_cfg, controllers, n);
+ free(controllers);
free_info(&info);
}
-static const char *
-get_fail_mode(struct ovsrec_controller **controllers, size_t n_controllers)
-{
- const char *fail_mode;
- size_t i;
-
- fail_mode = NULL;
- for (i = 0; i < n_controllers; i++) {
- const char *s = controllers[i]->fail_mode;
- if (s) {
- if (!strcmp(s, "secure")) {
- return s;
- } else {
- fail_mode = s;
- }
- }
- }
-
- return fail_mode;
-}
-
static void
cmd_get_fail_mode(struct vsctl_context *ctx)
{
struct vsctl_info info;
- const char *fail_mode = NULL;
+ struct vsctl_bridge *br;
get_info(ctx->ovs, &info);
+ br = find_bridge(&info, ctx->argv[1], true);
- if (ctx->argc == 1 || !strcmp(ctx->argv[1], "default")) {
- /* Return the fail-mode from the "Open_vSwitch" table */
- fail_mode = get_fail_mode(info.ctrl, info.n_ctrl);
- } else {
- /* Return the fail-mode for a particular bridge. */
- struct vsctl_bridge *br = find_bridge(&info, ctx->argv[1], true);
-
- /* If no controller is defined for the requested bridge, fallback to
- * the "Open_vSwitch" table's controller. */
- fail_mode = (br->n_ctrl
- ? get_fail_mode(br->ctrl, br->n_ctrl)
- : get_fail_mode(info.ctrl, info.n_ctrl));
- }
-
- if (fail_mode && strlen(fail_mode)) {
- ds_put_format(&ctx->output, "%s\n", fail_mode);
+ if (br->fail_mode && strlen(br->fail_mode)) {
+ ds_put_format(&ctx->output, "%s\n", br->fail_mode);
}
free_info(&info);
}
-static void
-set_fail_mode(struct ovsrec_controller **controllers, size_t n_controllers,
- const char *fail_mode)
-{
- size_t i;
-
- for (i = 0; i < n_controllers; i++) {
- ovsrec_controller_set_fail_mode(controllers[i], fail_mode);
- }
-}
-
static void
cmd_del_fail_mode(struct vsctl_context *ctx)
{
struct vsctl_info info;
+ struct vsctl_bridge *br;
get_info(ctx->ovs, &info);
+ br = find_real_bridge(&info, ctx->argv[1], true);
- if (ctx->argc == 1 || !strcmp(ctx->argv[1], "default")) {
- set_fail_mode(info.ctrl, info.n_ctrl, NULL);
- } else {
- struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
-
- set_fail_mode(br->ctrl, br->n_ctrl, NULL);
- }
+ ovsrec_bridge_set_fail_mode(br->br_cfg, NULL);
free_info(&info);
}
cmd_set_fail_mode(struct vsctl_context *ctx)
{
struct vsctl_info info;
- const char *bridge;
- const char *fail_mode;
+ struct vsctl_bridge *br;
+ const char *fail_mode = ctx->argv[2];
get_info(ctx->ovs, &info);
-
- if (ctx->argc == 2) {
- bridge = "default";
- fail_mode = ctx->argv[1];
- } else {
- bridge = ctx->argv[1];
- fail_mode = ctx->argv[2];
- }
+ br = find_real_bridge(&info, ctx->argv[1], true);
if (strcmp(fail_mode, "standalone") && strcmp(fail_mode, "secure")) {
vsctl_fatal("fail-mode must be \"standalone\" or \"secure\"");
}
- if (!strcmp(bridge, "default")) {
- /* Set the fail-mode in the "Open_vSwitch" table. */
- if (!info.ctrl) {
- vsctl_fatal("no controller declared");
- }
- set_fail_mode(info.ctrl, info.n_ctrl, fail_mode);
- } else {
- struct vsctl_bridge *br = find_real_bridge(&info, bridge, true);
-
- if (!br->ctrl) {
- vsctl_fatal("no controller declared for %s", br->name);
- }
- set_fail_mode(br->ctrl, br->n_ctrl, fail_mode);
- }
+ ovsrec_bridge_set_fail_mode(br->br_cfg, fail_mode);
free_info(&info);
}
{&ovsrec_table_controller,
{{&ovsrec_table_bridge,
&ovsrec_bridge_col_name,
- &ovsrec_bridge_col_controller},
- {&ovsrec_table_open_vswitch,
- NULL,
- &ovsrec_open_vswitch_col_controller}}},
+ &ovsrec_bridge_col_controller}}},
{&ovsrec_table_interface,
{{&ovsrec_table_interface, &ovsrec_interface_col_name, NULL},
{"iface-to-br", 1, 1, cmd_iface_to_br, NULL, ""},
/* Controller commands. */
- {"get-controller", 0, 1, cmd_get_controller, NULL, ""},
- {"del-controller", 0, 1, cmd_del_controller, NULL, ""},
+ {"get-controller", 1, 1, cmd_get_controller, NULL, ""},
+ {"del-controller", 1, 1, cmd_del_controller, NULL, ""},
{"set-controller", 1, INT_MAX, cmd_set_controller, NULL, ""},
- {"get-fail-mode", 0, 1, cmd_get_fail_mode, NULL, ""},
- {"del-fail-mode", 0, 1, cmd_del_fail_mode, NULL, ""},
- {"set-fail-mode", 1, 2, cmd_set_fail_mode, NULL, ""},
+ {"get-fail-mode", 1, 1, cmd_get_fail_mode, NULL, ""},
+ {"del-fail-mode", 1, 1, cmd_del_fail_mode, NULL, ""},
+ {"set-fail-mode", 2, 2, cmd_set_fail_mode, NULL, ""},
/* SSL commands. */
{"get-ssl", 0, 0, cmd_get_ssl, NULL, ""},
static unixctl_cb_func bridge_unixctl_dump_flows;
static unixctl_cb_func bridge_unixctl_reconnect;
static int bridge_run_one(struct bridge *);
-static size_t bridge_get_controllers(const struct ovsrec_open_vswitch *ovs_cfg,
- const struct bridge *br,
+static size_t bridge_get_controllers(const struct bridge *br,
struct ovsrec_controller ***controllersp);
-static void bridge_reconfigure_one(const struct ovsrec_open_vswitch *,
- struct bridge *);
-static void bridge_reconfigure_remotes(const struct ovsrec_open_vswitch *,
- struct bridge *,
+static void bridge_reconfigure_one(struct bridge *);
+static void bridge_reconfigure_remotes(struct bridge *,
const struct sockaddr_in *managers,
size_t n_managers);
static void bridge_get_all_ifaces(const struct bridge *, struct shash *ifaces);
svec_destroy(&xfif_types);
}
-#ifdef HAVE_OPENSSL
-static void
-bridge_configure_ssl(const struct ovsrec_ssl *ssl)
-{
- /* XXX SSL should be configurable on a per-bridge basis. */
- if (ssl) {
- stream_ssl_set_private_key_file(ssl->private_key);
- stream_ssl_set_certificate_file(ssl->certificate);
- stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert);
- }
-}
-#endif
-
/* Attempt to create the network device 'iface_name' through the netdev
* library. */
static int
shash_destroy(&old_br);
shash_destroy(&new_br);
-#ifdef HAVE_OPENSSL
- /* Configure SSL. */
- bridge_configure_ssl(ovs_cfg->ssl);
-#endif
-
/* Reconfigure all bridges. */
LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
- bridge_reconfigure_one(ovs_cfg, br);
+ bridge_reconfigure_one(br);
}
/* Add and delete ports on all datapaths.
oso.agent_device = sflow_cfg->agent;
oso.control_ip = NULL;
- n_controllers = bridge_get_controllers(ovs_cfg, br, &controllers);
+ n_controllers = bridge_get_controllers(br, &controllers);
for (i = 0; i < n_controllers; i++) {
if (controllers[i]->local_ip) {
oso.control_ip = controllers[i]->local_ip;
* yet; when a controller is configured, resetting the datapath ID will
* immediately disconnect from the controller, so it's better to set
* the datapath ID before the controller. */
- bridge_reconfigure_remotes(ovs_cfg, br, managers, n_managers);
+ bridge_reconfigure_remotes(br, managers, n_managers);
}
LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
for (i = 0; i < br->n_ports; i++) {
void
bridge_run(void)
{
+ const struct ovsrec_open_vswitch *cfg;
+
bool datapath_destroyed;
+ bool database_changed;
struct bridge *br;
/* Let each bridge do the work that it needs to do. */
}
/* (Re)configure if necessary. */
- if (ovsdb_idl_run(idl) || datapath_destroyed) {
- const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl);
+ database_changed = ovsdb_idl_run(idl);
+ cfg = ovsrec_open_vswitch_first(idl);
+ if (database_changed || datapath_destroyed) {
if (cfg) {
struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(idl);
}
}
+#ifdef HAVE_OPENSSL
+ /* Re-configure SSL. We do this on every trip through the main loop,
+ * instead of just when the database changes, because the contents of the
+ * key and certificate files can change without the database changing. */
+ if (cfg && cfg->ssl) {
+ const struct ovsrec_ssl *ssl = cfg->ssl;
+
+ stream_ssl_set_key_and_cert(ssl->private_key, ssl->certificate);
+ stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert);
+ }
+#endif
+
/* Refresh interface stats if necessary. */
if (time_msec() >= iface_stats_timer) {
struct ovsdb_idl_txn *txn;
LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
ofproto_wait(br->ofproto);
- if (ofproto_has_controller(br->ofproto)) {
+ if (ofproto_has_primary_controller(br->ofproto)) {
continue;
}
}
static size_t
-bridge_get_controllers(const struct ovsrec_open_vswitch *ovs_cfg,
- const struct bridge *br,
+bridge_get_controllers(const struct bridge *br,
struct ovsrec_controller ***controllersp)
{
struct ovsrec_controller **controllers;
size_t n_controllers;
- if (br->cfg->n_controller) {
- controllers = br->cfg->controller;
- n_controllers = br->cfg->n_controller;
- } else {
- controllers = ovs_cfg->controller;
- n_controllers = ovs_cfg->n_controller;
- }
+ controllers = br->cfg->controller;
+ n_controllers = br->cfg->n_controller;
if (n_controllers == 1 && !strcmp(controllers[0]->target, "none")) {
controllers = NULL;
}
static void
-bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg,
- struct bridge *br)
+bridge_reconfigure_one(struct bridge *br)
{
struct shash old_ports, new_ports;
- struct svec listeners, old_listeners;
struct svec snoops, old_snoops;
struct shash_node *node;
+ enum ofproto_fail_mode fail_mode;
size_t i;
/* Collect old ports. */
* user didn't specify one.
*
* XXX perhaps we should synthesize a port ourselves in this case. */
- if (bridge_get_controllers(ovs_cfg, br, NULL)) {
+ if (bridge_get_controllers(br, NULL)) {
char local_name[IF_NAMESIZE];
int error;
shash_destroy(&old_ports);
shash_destroy(&new_ports);
+ /* Set the fail-mode */
+ fail_mode = !br->cfg->fail_mode
+ || !strcmp(br->cfg->fail_mode, "standalone")
+ ? OFPROTO_FAIL_STANDALONE
+ : OFPROTO_FAIL_SECURE;
+ if (ofproto_get_fail_mode(br->ofproto) != fail_mode
+ && !ofproto_has_primary_controller(br->ofproto)) {
+ ofproto_flush_flows(br->ofproto);
+ }
+ ofproto_set_fail_mode(br->ofproto, fail_mode);
+
/* Delete all flows if we're switching from connected to standalone or vice
* versa. (XXX Should we delete all flows if we are switching from one
* controller to another?) */
- /* Configure OpenFlow management listener. */
- svec_init(&listeners);
- svec_add_nocopy(&listeners, xasprintf("punix:%s/%s.mgmt",
- ovs_rundir, br->name));
- svec_init(&old_listeners);
- ofproto_get_listeners(br->ofproto, &old_listeners);
- if (!svec_equal(&listeners, &old_listeners)) {
- ofproto_set_listeners(br->ofproto, &listeners);
- }
- svec_destroy(&listeners);
- svec_destroy(&old_listeners);
-
/* Configure OpenFlow controller connection snooping. */
svec_init(&snoops);
svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop",
mirror_reconfigure(br);
}
+/* Initializes 'oc' appropriately as a management service controller for
+ * 'br'.
+ *
+ * The caller must free oc->target when it is no longer needed. */
+static void
+bridge_ofproto_controller_for_mgmt(const struct bridge *br,
+ struct ofproto_controller *oc)
+{
+ oc->target = xasprintf("punix:%s/%s.mgmt", ovs_rundir, br->name);
+ oc->max_backoff = 0;
+ oc->probe_interval = 60;
+ oc->band = OFPROTO_OUT_OF_BAND;
+ oc->accept_re = NULL;
+ oc->update_resolv_conf = false;
+ oc->rate_limit = 0;
+ oc->burst_limit = 0;
+}
+
+/* Converts ovsrec_controller 'c' into an ofproto_controller in 'oc'. */
+static void
+bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c,
+ struct ofproto_controller *oc)
+{
+ oc->target = c->target;
+ oc->max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8;
+ oc->probe_interval = c->inactivity_probe ? *c->inactivity_probe / 1000 : 5;
+ oc->band = (!c->connection_mode || !strcmp(c->connection_mode, "in-band")
+ ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND);
+ oc->accept_re = c->discover_accept_regex;
+ oc->update_resolv_conf = c->discover_update_resolv_conf;
+ oc->rate_limit = c->controller_rate_limit ? *c->controller_rate_limit : 0;
+ oc->burst_limit = (c->controller_burst_limit
+ ? *c->controller_burst_limit : 0);
+}
+
+/* Configures the IP stack for 'br''s local interface properly according to the
+ * configuration in 'c'. */
static void
-bridge_reconfigure_remotes(const struct ovsrec_open_vswitch *ovs_cfg,
- struct bridge *br,
+bridge_configure_local_iface_netdev(struct bridge *br,
+ struct ovsrec_controller *c)
+{
+ struct netdev *netdev;
+ struct in_addr mask, gateway;
+
+ struct iface *local_iface;
+ struct in_addr ip;
+
+ /* Controller discovery does its own TCP/IP configuration later. */
+ if (strcmp(c->target, "discover")) {
+ return;
+ }
+
+ /* If there's no local interface or no IP address, give up. */
+ local_iface = bridge_get_local_iface(br);
+ if (!local_iface || !c->local_ip || !inet_aton(c->local_ip, &ip)) {
+ return;
+ }
+
+ /* Bring up the local interface. */
+ netdev = local_iface->netdev;
+ netdev_turn_flags_on(netdev, NETDEV_UP, true);
+
+ /* Configure the IP address and netmask. */
+ if (!c->local_netmask
+ || !inet_aton(c->local_netmask, &mask)
+ || !mask.s_addr) {
+ mask.s_addr = guess_netmask(ip.s_addr);
+ }
+ if (!netdev_set_in4(netdev, ip, mask)) {
+ VLOG_INFO("bridge %s: configured IP address "IP_FMT", netmask "IP_FMT,
+ br->name, IP_ARGS(&ip.s_addr), IP_ARGS(&mask.s_addr));
+ }
+
+ /* Configure the default gateway. */
+ if (c->local_gateway
+ && inet_aton(c->local_gateway, &gateway)
+ && gateway.s_addr) {
+ if (!netdev_add_router(netdev, gateway)) {
+ VLOG_INFO("bridge %s: configured gateway "IP_FMT,
+ br->name, IP_ARGS(&gateway.s_addr));
+ }
+ }
+}
+
+static void
+bridge_reconfigure_remotes(struct bridge *br,
const struct sockaddr_in *managers,
size_t n_managers)
{
struct ovsrec_controller **controllers;
size_t n_controllers;
+ bool had_primary;
+
+ struct ofproto_controller *ocs;
+ size_t n_ocs;
+ size_t i;
ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
+ had_primary = ofproto_has_primary_controller(br->ofproto);
+
+ n_controllers = bridge_get_controllers(br, &controllers);
+
+ ocs = xmalloc((n_controllers + 1) * sizeof *ocs);
+ n_ocs = 0;
+
+ bridge_ofproto_controller_for_mgmt(br, &ocs[n_ocs++]);
+ for (i = 0; i < n_controllers; i++) {
+ struct ovsrec_controller *c = controllers[i];
+
+ if (!strncmp(c->target, "punix:", 6)
+ || !strncmp(c->target, "unix:", 5)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+ /* Prevent remote ovsdb-server users from accessing arbitrary Unix
+ * domain sockets and overwriting arbitrary local files. */
+ VLOG_ERR_RL(&rl, "%s: not adding Unix domain socket controller "
+ "\"%s\" due to possibility for remote exploit",
+ br->name, c->target);
+ continue;
+ }
+
+ bridge_configure_local_iface_netdev(br, c);
+ bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs++]);
+ }
+
+ ofproto_set_controllers(br->ofproto, ocs, n_ocs);
+ free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
+ free(ocs);
- n_controllers = bridge_get_controllers(ovs_cfg, br, &controllers);
- if (ofproto_has_controller(br->ofproto) != (n_controllers != 0)) {
+ if (had_primary != ofproto_has_primary_controller(br->ofproto)) {
ofproto_flush_flows(br->ofproto);
}
- if (!n_controllers) {
+ /* If there are no controllers and the bridge is in standalone
+ * mode, set up a flow that matches every packet and directs
+ * them to OFPP_NORMAL (which goes to us). Otherwise, the
+ * switch is in secure mode and we won't pass any traffic until
+ * a controller has been defined and it tells us to do so. */
+ if (!n_controllers
+ && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
union ofp_action action;
flow_t flow;
- /* Clear out controllers. */
- ofproto_set_controllers(br->ofproto, NULL, 0);
-
- /* Set up a flow that matches every packet and directs them to
- * OFPP_NORMAL (which goes to us). */
memset(&action, 0, sizeof action);
action.type = htons(OFPAT_OUTPUT);
action.output.len = htons(sizeof action);
memset(&flow, 0, sizeof flow);
flow.wildcards = OVSFW_ALL;
ofproto_add_flow(br->ofproto, &flow, &action, 1, 0);
- } else {
- struct ofproto_controller *ocs;
- size_t i;
-
- ocs = xmalloc(n_controllers * sizeof *ocs);
- for (i = 0; i < n_controllers; i++) {
- struct ovsrec_controller *c = controllers[i];
- struct ofproto_controller *oc = &ocs[i];
-
- if (strcmp(c->target, "discover")) {
- struct iface *local_iface;
- struct in_addr ip;
-
- local_iface = bridge_get_local_iface(br);
- if (local_iface && c->local_ip
- && inet_aton(c->local_ip, &ip)) {
- struct netdev *netdev = local_iface->netdev;
- struct in_addr mask, gateway;
-
- if (!c->local_netmask
- || !inet_aton(c->local_netmask, &mask)) {
- mask.s_addr = 0;
- }
- if (!c->local_gateway
- || !inet_aton(c->local_gateway, &gateway)) {
- gateway.s_addr = 0;
- }
-
- netdev_turn_flags_on(netdev, NETDEV_UP, true);
- if (!mask.s_addr) {
- mask.s_addr = guess_netmask(ip.s_addr);
- }
- if (!netdev_set_in4(netdev, ip, mask)) {
- VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
- "netmask "IP_FMT,
- br->name, IP_ARGS(&ip.s_addr),
- IP_ARGS(&mask.s_addr));
- }
-
- if (gateway.s_addr) {
- if (!netdev_add_router(netdev, gateway)) {
- VLOG_INFO("bridge %s: configured gateway "IP_FMT,
- br->name, IP_ARGS(&gateway.s_addr));
- }
- }
- }
- }
-
- oc->target = c->target;
- oc->max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8;
- oc->probe_interval = (c->inactivity_probe
- ? *c->inactivity_probe / 1000 : 5);
- oc->fail = (!c->fail_mode
- || !strcmp(c->fail_mode, "standalone")
- || !strcmp(c->fail_mode, "open")
- ? OFPROTO_FAIL_STANDALONE
- : OFPROTO_FAIL_SECURE);
- oc->band = (!c->connection_mode
- || !strcmp(c->connection_mode, "in-band")
- ? OFPROTO_IN_BAND
- : OFPROTO_OUT_OF_BAND);
- oc->accept_re = c->discover_accept_regex;
- oc->update_resolv_conf = c->discover_update_resolv_conf;
- oc->rate_limit = (c->controller_rate_limit
- ? *c->controller_rate_limit : 0);
- oc->burst_limit = (c->controller_burst_limit
- ? *c->controller_burst_limit : 0);
- }
- ofproto_set_controllers(br->ofproto, ocs, n_controllers);
- free(ocs);
}
}
struct bridge *br = br_;
COVERAGE_INC(bridge_process_flow);
+
return process_flow(br, flow, packet, actions, tags, nf_output_iface);
}
static void
-bridge_account_flow_ofhook_cb(const flow_t *flow,
+bridge_account_flow_ofhook_cb(const flow_t *flow, tag_type tags,
const union xflow_action *actions,
size_t n_actions, unsigned long long int n_bytes,
void *br_)
struct bridge *br = br_;
const union xflow_action *a;
struct port *in_port;
- tag_type tags = 0;
+ tag_type dummy = 0;
int vlan;
- /* Feed information from the active flows back into the learning table
- * to ensure that table is always in sync with what is actually flowing
- * through the datapath. */
- if (is_admissible(br, flow, false, &tags, &vlan, &in_port)) {
+ /* Feed information from the active flows back into the learning table to
+ * ensure that table is always in sync with what is actually flowing
+ * through the datapath.
+ *
+ * We test that 'tags' is nonzero to ensure that only flows that include an
+ * OFPP_NORMAL action are used for learning. This works because
+ * bridge_normal_ofhook_cb() always sets a nonzero tag value. */
+ if (tags && is_admissible(br, flow, false, &dummy, &vlan, &in_port)) {
update_learning_table(br, flow, vlan, in_port);
}
+ /* Account for bond slave utilization. */
if (!br->has_bonded_ports) {
return;
}
-
for (a = actions; a < &actions[n_actions]; a++) {
if (a->type == XFLOWAT_OUTPUT) {
struct port *out_port = port_from_xf_ifidx(br, a->output.port);
"type": {"key": {"type": "uuid",
"refTable": "Bridge"},
"min": 0, "max": "unlimited"}},
- "controller": {
- "type": {"key": {"type": "uuid",
- "refTable": "Controller"},
- "min": 0, "max": "unlimited"}},
"managers": {
"type": {"key": "string", "min": 0, "max": "unlimited"}},
"ssl": {
"type": {"key": {"type": "uuid",
"refTable": "Controller"},
"min": 0, "max": "unlimited"}},
+ "fail_mode": {
+ "type": {"key": {"type": "string",
+ "enum": ["set", ["standalone", "secure"]]},
+ "min": 0, "max": 1}},
"other_config": {
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
"external_ids": {
"ephemeral": true},
"statistics": {
"type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"},
- "ephemeral": true}}},
+ "ephemeral": true},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}}},
"QoS": {
"columns": {
"type": {
"min": 0, "max": "unlimited"}},
"other_config": {
"type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}}},
"Queue": {
"columns": {
"other_config": {
"type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}}},
"Mirror": {
"columns": {
"type": {"key": {"type": "integer",
"minInteger": 1,
"maxInteger": 4095},
- "min": 0, "max": 1}}}},
+ "min": 0, "max": 1}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}}},
"NetFlow": {
"columns": {
"targets": {
"type": "boolean"},
"active_timeout": {
"type": {"key": {"type": "integer",
- "minInteger": -1}}}}},
+ "minInteger": -1}}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}}},
"sFlow": {
"columns": {
"targets": {
"header": {
"type": {"key": "integer", "min": 0, "max": 1}},
"agent": {
- "type": {"key": "string", "min": 0, "max": 1}}}},
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}}},
"Controller": {
"columns": {
"target": {
"min": 0, "max": 1}},
"inactivity_probe": {
"type": {"key": "integer", "min": 0, "max": 1}},
- "fail_mode": {
- "type": {"key": {"type": "string",
- "enum": ["set", ["standalone", "secure"]]},
- "min": 0, "max": 1}},
"discover_accept_regex": {
"type": {"key": "string", "min": 0, "max": 1}},
"discover_update_resolv_conf": {
"controller_burst_limit": {
"type": {"key": {"type": "integer",
"minInteger": 25},
- "min": 0, "max": 1}}}},
+ "min": 0, "max": 1}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}}},
"SSL": {
"columns": {
"private_key": {
"ca_cert": {
"type": "string"},
"bootstrap_ca_cert": {
- "type": "boolean"}},
+ "type": "boolean"},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}},
"maxRows": 1}}}
Set of bridges managed by the daemon.
</column>
- <column name="controller">
- Default OpenFlow <ref table="Controller"/> set used by bridges. May be
- overridden on a per-bridge basis by the <ref table="Bridge"
- column="controller"/> column in <ref table="Bridge"/>.
- </column>
-
<column name="managers">
Remote database clients to which the Open vSwitch's database server
should connect or to which it should listen.
</column>
<column name="external_ids">
- Key-value pairs that identify this Open vSwitch's role in
- external systems. The currently defined key-value pairs are:
+ Key-value pairs for use by external frameworks that integrate
+ with Open vSwitch, rather than by Open vSwitch itself. System
+ integrators should either use the Open vSwitch development
+ mailing list to coordinate on common key-value definitions, or
+ choose key names that are likely to be unique. The currently
+ defined common key-value pairs are:
<dl>
<dt><code>system-uuid</code></dt>
<dd>A universally unique identifier for the Open vSwitch's
<group title="OpenFlow Configuration">
<column name="controller">
- OpenFlow controller set. If unset, defaults to the set of
- controllers specified by <ref column="controller"
- table="Open_vSwitch"/> in the <ref table="Open_vSwitch"/>
- table. If the default is also unset, then no OpenFlow
- controllers will be used.
+ OpenFlow controller set. If unset, then no OpenFlow controllers
+ will be used.
+ </column>
+
+ <column name="fail_mode">
+ <p>When a controller is configured, it is, ordinarily, responsible
+ for setting up all flows on the switch. Thus, if the connection to
+ the controller fails, no new network connections can be set up.
+ If the connection to the controller stays down long enough,
+ no packets can pass through the switch at all. This setting
+ determines the switch's response to such a situation. It may be set
+ to one of the following:
+ <dl>
+ <dt><code>standalone</code></dt>
+ <dd>If no message is received from the controller for three
+ times the inactivity probe interval
+ (see <ref column="inactivity_probe"/>), then Open vSwitch
+ will take over responsibility for setting up flows. In
+ this mode, Open vSwitch causes the bridge to act like an
+ ordinary MAC-learning switch. Open vSwitch will continue
+ to retry connecting to the controller in the background
+ and, when the connection succeeds, it will discontinue its
+ standalone behavior.</dd>
+ <dt><code>secure</code></dt>
+ <dd>Open vSwitch will not set up flows on its own when the
+ controller connection fails or when no controllers are
+ defined. The bridge will continue to retry connecting to
+ any defined controllers forever.</dd>
+ </dl>
+ </p>
+ <p>If this value is unset, the default is implementation-specific.</p>
+ <p>When more than one controller is configured,
+ <ref column="fail_mode"/> is considered only when none of the
+ configured controllers can be contacted.</p>
</column>
<column name="datapath_id">
</column>
<column name="external_ids">
- Key-value pairs that identify this bridge's role in external systems.
- The currently defined key-value pairs are:
+ Key-value pairs for use by external frameworks that integrate
+ with Open vSwitch, rather than by Open vSwitch itself. System
+ integrators should either use the Open vSwitch development
+ mailing list to coordinate on common key-value definitions, or
+ choose key names that are likely to be unique. The currently
+ defined common key-value pairs are:
<dl>
<dt><code>network-uuids</code></dt>
<dd>Semicolon-delimited set of universally unique identifier(s) for
</column>
<column name="external_ids">
- Key-value pairs that identify this port's role in external systems. No
- key-value pairs native to <ref table="Port"/> are currently defined.
- For fake bridges (see the <ref column="fake_bridge"/> column), external
- IDs for the fake bridge are defined here by prefixing a
- <ref table="Bridge"/> <ref table="Bridge" column="external_ids"/> key
- with <code>fake-bridge-</code>,
- e.g. <code>fake-bridge-network-uuids</code>.
+ <p>
+ Key-value pairs for use by external frameworks that integrate with
+ Open vSwitch, rather than by Open vSwitch itself. System integrators
+ should either use the Open vSwitch development mailing list to
+ coordinate on common key-value definitions, or choose key names that
+ are likely to be unique.
+ </p>
+ <p>
+ No key-value pairs native to <ref table="Port"/> are currently
+ defined. For fake bridges (see the <ref column="fake_bridge"/>
+ column), external IDs for the fake bridge are defined here by
+ prefixing a <ref table="Bridge"/> <ref table="Bridge"
+ column="external_ids"/> key with <code>fake-bridge-</code>,
+ e.g. <code>fake-bridge-network-uuids</code>.
+ </p>
</column>
<column name="other_config">
<group title="Other Features">
<column name="external_ids">
- <p>Key-value pairs that identify this interface's role in external
- systems. All of the currently defined key-value pairs specifically
+ <p>
+ Key-value pairs for use by external frameworks that integrate
+ with Open vSwitch, rather than by Open vSwitch itself. System
+ integrators should either use the Open vSwitch development
+ mailing list to coordinate on common key-value definitions, or
+ choose key names that are likely to be unique.
+ </p>
+ <p>
+ All of the currently defined key-value pairs specifically
apply to an interface that represents a virtual Ethernet interface
connected to a virtual machine. These key-value pairs should not be
present for other types of interfaces. Keys whose names end
in <code>-uuid</code> have values that uniquely identify the entity
in question. For a Citrix XenServer hypervisor, these values are
UUIDs in RFC 4122 format. Other hypervisors may use other
- formats.</p>
+ formats.
+ </p>
<p>The currently defined key-value pairs are:</p>
<dl>
<dt><code>vif-uuid</code></dt>
Mbps.</dd>
</dl>
</column>
+
+ <column name="external_ids">
+ Key-value pairs for use by external frameworks that integrate with Open
+ vSwitch, rather than by Open vSwitch itself. System integrators should
+ either use the Open vSwitch development mailing list to coordinate on
+ common key-value definitions, or choose key names that are likely to be
+ unique. No common key-value pairs are currently defined.
+ </column>
</table>
<table name="Queue" title="QoS output queue.">
values are unimportant; only relative ordering matters.</dd>
</dl>
</column>
+
+ <column name="external_ids">
+ Key-value pairs for use by external frameworks that integrate with Open
+ vSwitch, rather than by Open vSwitch itself. System integrators should
+ either use the Open vSwitch development mailing list to coordinate on
+ common key-value definitions, or choose key names that are likely to be
+ unique. No common key-value pairs are currently defined.
+ </column>
</table>
<table name="Mirror" title="Port mirroring (SPAN/RSPAN).">
in the appropriate <ref table="Bridge"/> table or tables.</p>
</column>
</group>
+
+ <group title="Other Features">
+ <column name="external_ids">
+ Key-value pairs for use by external frameworks that integrate with Open
+ vSwitch, rather than by Open vSwitch itself. System integrators should
+ either use the Open vSwitch development mailing list to coordinate on
+ common key-value definitions, or choose key names that are likely to be
+ unique. No common key-value pairs are currently defined.
+ </column>
+ </group>
</table>
<table name="Controller" title="OpenFlow controller configuration.">
<p>An OpenFlow controller.</p>
- <p>Open vSwitch permits a bridge to have any number of OpenFlow
- controllers. When multiple controllers are configured, Open vSwitch
- connects to all of them simultaneously. OpenFlow 1.0 does not specify
- how multiple controllers coordinate in interacting with a single switch,
- so more than one controller should be specified only if the controllers
- are themselves designed to coordinate with each other.</p>
+ <p>
+ Open vSwitch supports two kinds of OpenFlow controllers:
+ </p>
+
+ <dl>
+ <dt>Primary controllers</dt>
+ <dd>
+ <p>
+ This is the kind of controller envisioned by the OpenFlow 1.0
+ specification. Usually, a primary controller implements a network
+ policy by taking charge of the switch's flow table.
+ </p>
+
+ <p>
+ Open vSwitch initiates and maintains persistent connections to
+ primary controllers, retrying the connection each time it fails or
+ drops. The <ref table="Bridge" column="fail_mode"/> column in the
+ <ref table="Bridge"/> table applies to primary controllers.
+ </p>
+
+ <p>
+ Open vSwitch permits a bridge to have any number of primary
+ controllers. When multiple controllers are configured, Open
+ vSwitch connects to all of them simultaneously. Because
+ OpenFlow 1.0 does not specify how multiple controllers
+ coordinate in interacting with a single switch, more than
+ one primary controller should be specified only if the
+ controllers are themselves designed to coordinate with each
+ other. (The Nicira-defined <code>NXT_ROLE</code> OpenFlow
+ vendor extension may be useful for this.)
+ </p>
+ </dd>
+ <dt>Service controllers</dt>
+ <dd>
+ <p>
+ These kinds of OpenFlow controller connections are intended for
+ occasional support and maintenance use, e.g. with
+ <code>ovs-ofctl</code>. Usually a service controller connects only
+ briefly to inspect or modify some of a switch's state.
+ </p>
+
+ <p>
+ Open vSwitch listens for incoming connections from service
+ controllers. The service controllers initiate and, if necessary,
+ maintain the connections from their end. The <ref table="Bridge"
+ column="fail_mode"/> column in the <ref table="Bridge"/> table does
+ not apply to service controllers.
+ </p>
+
+ <p>
+ Open vSwitch supports configuring any number of service controllers.
+ </p>
+ </dd>
+ </dl>
+
+ <p>
+ The <ref column="target"/> determines the type of controller.
+ </p>
<group title="Core Features">
<column name="target">
- <p>Connection method for controller.
- The following connection methods are currently
- supported:</p>
+ <p>Connection method for controller.</p>
+ <p>
+ The following connection methods are currently supported for primary
+ controllers:
+ </p>
<dl>
<dt><code>ssl:<var>ip</var></code>[<code>:<var>port</var></code>]</dt>
<dd>
<p>The specified SSL <var>port</var> (default: 6633) on the host at
- the given <var>ip</var>, which must be expressed as an IP address
- (not a DNS name). The <ref table="Open_vSwitch" column="ssl"/>
- column in the <ref table="Open_vSwitch"/> must point to a valid
- SSL configuration when this form is used.</p>
+ the given <var>ip</var>, which must be expressed as an IP address
+ (not a DNS name). The <ref table="Open_vSwitch" column="ssl"/>
+ column in the <ref table="Open_vSwitch"/> table must point to a
+ valid SSL configuration when this form is used.</p>
<p>SSL support is an optional feature that is not always built as
part of Open vSwitch.</p>
</dd>
used only for bootstrapping the OpenFlow PKI at initial switch
setup; <code>ovs-vswitchd</code> does not use it at all.</p>
</dd>
- <dt><code>none</code></dt>
- <dd>Disables the controller.</dd>
+ </dl>
+ <p>
+ The following connection methods are currently supported for service
+ controllers:
+ </p>
+ <dl>
+ <dt><code>pssl:</code>[<var>port</var>][<code>:<var>ip</var></code>]</dt>
+ <dd>
+ <p>
+ Listens for SSL connections on the specified TCP <var>port</var>
+ (default: 6633). If <var>ip</var>, which must be expressed as an
+ IP address (not a DNS name), is specified, then connections are
+ restricted to the specified local IP address.
+ </p>
+ <p>
+ The <ref table="Open_vSwitch" column="ssl"/> column in the <ref
+ table="Open_vSwitch"/> table must point to a valid SSL
+ configuration when this form is used.
+ </p>
+ <p>SSL support is an optional feature that is not always built as
+ part of Open vSwitch.</p>
+ </dd>
+ <dt><code>ptcp:</code>[<var>port</var>][<code>:<var>ip</var></code>]</dt>
+ <dd>
+ Listens for connections on the specified TCP <var>port</var>
+ (default: 6633). If <var>ip</var>, which must be expressed as an
+ IP address (not a DNS name), is specified, then connections are
+ restricted to the specified local IP address.
+ </dd>
</dl>
<p>When multiple controllers are configured for a single bridge, the
<ref column="target"/> values must be unique. Duplicate
assumes the connection has been broken and attempts to reconnect.
Default is implementation-specific.
</column>
-
- <column name="fail_mode">
- <p>When a controller is configured, it is, ordinarily, responsible
- for setting up all flows on the switch. Thus, if the connection to
- the controller fails, no new network connections can be set up.
- If the connection to the controller stays down long enough,
- no packets can pass through the switch at all. This setting
- determines the switch's response to such a situation. It may be set
- to one of the following:
- <dl>
- <dt><code>standalone</code></dt>
- <dd>If no message is received from the controller for three
- times the inactivity probe interval
- (see <ref column="inactivity_probe"/>), then Open vSwitch
- will take over responsibility for setting up flows. In
- this mode, Open vSwitch causes the bridge to act like an
- ordinary MAC-learning switch. Open vSwitch will continue
- to retry connecting to the controller in the background
- and, when the connection succeeds, it will discontinue its
- standalone behavior.</dd>
- <dt><code>secure</code></dt>
- <dd>Open vSwitch will not set up flows on its own when the
- controller connection fails. It will continue retry
- connecting to the controller forever.</dd>
- </dl>
- </p>
- <p>If this value is unset, the default is implementation-specific.</p>
- <p>When more than one controller is configured,
- <ref column="fail_mode"/> is considered only when none of the
- configured controllers can be contacted. At that point, the bridge
- enters secure mode if any of the controllers'
- <ref column="fail_mode"/> is set to <code>secure</code>. Otherwise,
- it enters standalone mode if at least one <ref column="fail_mode"/>
- is set to <code>standalone</code>. If none of the
- <ref column="fail_mode"/> values are set, the default is
- implementation-defined.</p>
- </column>
</group>
<group title="OpenFlow Rate Limiting">
this network has no gateway.
</column>
</group>
+
+ <group title="Other Features">
+ <column name="external_ids">
+ Key-value pairs for use by external frameworks that integrate with Open
+ vSwitch, rather than by Open vSwitch itself. System integrators should
+ either use the Open vSwitch development mailing list to coordinate on
+ common key-value definitions, or choose key names that are likely to be
+ unique. No common key-value pairs are currently defined.
+ </column>
+ </group>
</table>
<table name="NetFlow">
disambiguate the traffic.</p>
<p>When this option is enabled, a maximum of 508 ports are supported.</p>
</column>
+
+ <column name="external_ids">
+ Key-value pairs for use by external frameworks that integrate with Open
+ vSwitch, rather than by Open vSwitch itself. System integrators should
+ either use the Open vSwitch development mailing list to coordinate on
+ common key-value definitions, or choose key names that are likely to be
+ unique. No common key-value pairs are currently defined.
+ </column>
</table>
<table name="SSL">
SSL connection to a man-in-the-middle attack obtaining the initial
CA certificate.</em> It may still be useful for bootstrapping.
</column>
+
+ <column name="external_ids">
+ Key-value pairs for use by external frameworks that integrate with Open
+ vSwitch, rather than by Open vSwitch itself. System integrators should
+ either use the Open vSwitch development mailing list to coordinate on
+ common key-value definitions, or choose key names that are likely to be
+ unique. No common key-value pairs are currently defined.
+ </column>
</table>
<table name="sFlow">
sFlow targets in the form
<code><var>ip</var>:<var>port</var></code>.
</column>
+
+ <column name="external_ids">
+ Key-value pairs for use by external frameworks that integrate with Open
+ vSwitch, rather than by Open vSwitch itself. System integrators should
+ either use the Open vSwitch development mailing list to coordinate on
+ common key-value definitions, or choose key names that are likely to be
+ unique. No common key-value pairs are currently defined.
+ </column>
</table>
<table name="Capability">
restart)
restart
;;
+ reload|force-reload)
+ # Nothing to do--ovs-vswitchd and ovsdb-server keep their configuration
+ # up-to-date all the time.
+ ;;
strace-vswitchd)
shift
strace -p $(cat "$VSWITCHD_PIDFILE") "$@"
/usr/sbin/ovs-brcompatd -V
;;
help)
- printf "openvswitch [start|stop|restart|unload|status|version]\n"
+ printf "openvswitch [start|stop|restart|reload|force-reload|status|version]\n"
;;
*)
printf "Unknown command: $1\n"