Merge "master" into "wdp".
authorBen Pfaff <blp@nicira.com>
Mon, 9 Aug 2010 17:44:39 +0000 (10:44 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 9 Aug 2010 17:44:39 +0000 (10:44 -0700)
51 files changed:
INSTALL.KVM
INSTALL.Linux
configure.ac
datapath/actions.c
datapath/datapath.c
datapath/dp_notify.c
datapath/dp_sysfs_dp.c
datapath/dp_sysfs_if.c
datapath/flow.c
datapath/flow.h
datapath/linux-2.6/.gitignore
datapath/linux-2.6/Modules.mk
datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
datapath/linux-2.6/compat-2.6/time.c [new file with mode: 0644]
datapath/vport-internal_dev.c
datapath/vport-netdev.c
datapath/vport.c
include/openvswitch/xflow.h
lib/automake.mk
lib/flow.c
lib/json.h
lib/learning-switch.c
lib/learning-switch.h
lib/ofp-parse.c [new file with mode: 0644]
lib/ofp-parse.h [new file with mode: 0644]
lib/stream-ssl.c
lib/stream-ssl.h
lib/tag.c
lib/vlog-modules.def
lib/xfif-netdev.c
ofproto/netflow.c
ofproto/netflow.h
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/status.c
ofproto/wdp-xflow.c
ovsdb/ovsdb-server.c
tests/ovs-vsctl.at
utilities/ovs-controller.8.in
utilities/ovs-controller.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-openflowd.8.in
utilities/ovs-openflowd.c
utilities/ovs-pki.in
utilities/ovs-vsctl.8.in
utilities/ovs-vsctl.c
vswitchd/bridge.c
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml
xenserver/etc_init.d_openvswitch

index 8e723b3..6e61f50 100644 (file)
@@ -23,7 +23,7 @@ guide.
 
 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
 --------------------------------------------------------------------
index 7cfba40..3088f8c 100644 (file)
@@ -238,9 +238,16 @@ Before starting ovs-vswitchd itself, you need to start its
 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
index dad2fb9..04ffe6e 100644 (file)
@@ -86,6 +86,7 @@ OVS_ENABLE_OPTION([-Wmissing-prototypes])
 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)
index 80b419c..7d39cd7 100644 (file)
@@ -26,7 +26,7 @@
 
 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));
 
@@ -413,7 +413,6 @@ int execute_actions(struct datapath *dp, struct sk_buff *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;
index d814902..bb2486e 100644 (file)
@@ -69,8 +69,22 @@ EXPORT_SYMBOL(dp_ioctl_hook);
 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);
 
@@ -380,6 +394,7 @@ static int new_dp_port(struct datapath *dp, struct xflow_port *xflow_port, int p
 
        p->port_no = port_no;
        p->dp = dp;
+       p->vport = vport;
        atomic_set(&p->sflow_pool, 0);
 
        err = vport_attach(vport, p);
@@ -513,6 +528,14 @@ out:
        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)
 {
@@ -521,12 +544,13 @@ 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);
@@ -535,20 +559,44 @@ void dp_process_received_packet(struct dp_port *p, struct sk_buff *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))++;
@@ -904,27 +952,43 @@ error:
        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;
 }
@@ -1020,7 +1084,7 @@ static int do_put_flow(struct datapath *dp, struct xflow_flow_put *uf,
 
                /* 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);
@@ -1057,6 +1121,7 @@ static int put_flow(struct datapath *dp, struct xflow_flow_put __user *ufp)
 }
 
 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)
@@ -1066,7 +1131,7 @@ static int do_answer_query(struct sw_flow *flow, u32 query_flags,
        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;
 
@@ -1090,6 +1155,7 @@ static int do_answer_query(struct sw_flow *flow, u32 query_flags,
 }
 
 static int answer_query(struct sw_flow *flow, u32 query_flags,
+                       struct timespec time_offset,
                        struct xflow_flow __user *ufp)
 {
        union xflow_action *actions;
@@ -1097,7 +1163,7 @@ static int answer_query(struct sw_flow *flow, u32 query_flags,
        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);
 }
 
@@ -1135,7 +1201,7 @@ static int del_flow(struct datapath *dp, struct xflow_flow __user *ufp)
        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;
 }
@@ -1143,8 +1209,11 @@ static int del_flow(struct datapath *dp, struct xflow_flow __user *ufp)
 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;
@@ -1158,7 +1227,7 @@ static int do_query_flows(struct datapath *dp, const struct xflow_flowvec *flowv
                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;
        }
@@ -1169,6 +1238,7 @@ struct list_flows_cbdata {
        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_)
@@ -1180,7 +1250,7 @@ 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;
 
@@ -1200,6 +1270,8 @@ static int do_list_flows(struct datapath *dp, const struct xflow_flowvec *flowve
        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;
 }
@@ -1808,6 +1880,7 @@ static int compat_put_flow(struct datapath *dp, struct compat_xflow_flow_put __u
 }
 
 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;
@@ -1815,7 +1888,7 @@ static int compat_answer_query(struct sw_flow *flow, u32 query_flags,
        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);
 }
 
@@ -1832,7 +1905,7 @@ static int compat_del_flow(struct datapath *dp, struct compat_xflow_flow __user
        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;
 }
@@ -1840,8 +1913,11 @@ static int compat_del_flow(struct datapath *dp, struct compat_xflow_flow __user
 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;
@@ -1855,7 +1931,7 @@ static int compat_query_flows(struct datapath *dp, struct compat_xflow_flow *flo
                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;
        }
@@ -1866,6 +1942,7 @@ struct compat_list_flows_cbdata {
        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_)
@@ -1877,7 +1954,7 @@ 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;
 
@@ -1897,6 +1974,8 @@ static int compat_list_flows(struct datapath *dp, struct compat_xflow_flow *flow
        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;
 }
index 166582e..64867dd 100644 (file)
@@ -24,12 +24,11 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
 
        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);
 
index 5e1362c..5fd4967 100644 (file)
@@ -20,8 +20,6 @@
 #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"
@@ -29,7 +27,6 @@
 #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.
  */
@@ -56,9 +62,9 @@ static ssize_t store_bridge_parm(DEVICE_PARAMS,
                                 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;
@@ -67,44 +73,37 @@ static ssize_t store_bridge_parm(DEVICE_PARAMS,
        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,
@@ -117,24 +116,12 @@ static INTERNAL_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
 
 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,
@@ -148,24 +135,12 @@ static INTERNAL_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
 
 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,
@@ -177,21 +152,12 @@ static INTERNAL_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_
 
 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,
@@ -204,12 +170,7 @@ static INTERNAL_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
 
 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
 }
 
 
@@ -217,48 +178,32 @@ static ssize_t store_stp_state(DEVICE_PARAMS,
                               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,
@@ -270,162 +215,105 @@ static INTERNAL_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_pr
 
 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,
index 6414968..4d18395 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/netdevice.h>
 #include <linux/if_bridge.h>
 #include <linux/rtnetlink.h>
-#include <linux/spinlock.h>
 
 #include "datapath.h"
 #include "dp_sysfs.h"
@@ -42,17 +41,10 @@ struct brport_attribute brport_attr_##_name = {             \
 
 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,
@@ -60,19 +52,10 @@ 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,
@@ -80,51 +63,31 @@ 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);
 
@@ -137,64 +100,40 @@ static BRPORT_ATTR(port_no, S_IRUGO, show_port_no, 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);
 
@@ -233,33 +172,14 @@ static ssize_t brport_store(struct kobject * kobj,
                            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;
 }
 
index 455a229..f8bc0d6 100644 (file)
@@ -92,26 +92,18 @@ static inline int icmphdr_ok(struct sk_buff *skb)
 #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;
@@ -326,11 +318,6 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct xflow_key *key)
        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);
index c42ae1b..835d2cc 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/rcupdate.h>
 #include <linux/gfp.h>
+#include <linux/jiffies.h>
 #include <linux/time.h>
 
 #include "openvswitch/xflow.h"
@@ -34,11 +35,8 @@ struct sw_flow {
        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. */
@@ -52,7 +50,6 @@ void flow_deferred_free_acts(struct sw_flow_actions *);
 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 *);
@@ -60,4 +57,9 @@ 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 */
index 4b5658b..bd99f24 100644 (file)
@@ -25,6 +25,7 @@
 /random32.c
 /skbuff-openvswitch.c
 /table.c
+/time.c
 /tmp
 /veth.c
 /vport-generic.c
index baa6f53..7f4cae6 100644 (file)
@@ -5,7 +5,8 @@ openvswitch_sources += \
        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 \
index 15acea9..01a22d8 100644 (file)
@@ -228,17 +228,4 @@ static inline bool skb_warn_if_lro(const struct sk_buff *skb)
 #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
diff --git a/datapath/linux-2.6/compat-2.6/time.c b/datapath/linux-2.6/compat-2.6/time.c
new file mode 100644 (file)
index 0000000..b07ee26
--- /dev/null
@@ -0,0 +1,39 @@
+#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 */
index 67bffcf..f37d20a 100644 (file)
@@ -19,7 +19,7 @@
 #include "vport-netdev.h"
 
 struct internal_dev {
-       struct vport *vport;
+       struct vport *attached_vport, *vport;
        struct net_device_stats stats;
 };
 
@@ -70,11 +70,12 @@ static int internal_dev_mac_addr(struct net_device *dev, void *p)
 /* 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;
        }
@@ -102,9 +103,15 @@ static int internal_dev_stop(struct net_device *netdev)
 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);
 }
@@ -122,14 +129,18 @@ static struct ethtool_ops internal_dev_ethtool_ops = {
 
 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;
@@ -211,7 +222,7 @@ static struct vport *internal_dev_create(const char *name,
        }
 
        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)
@@ -240,13 +251,11 @@ static int internal_dev_destroy(struct vport *vport)
 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;
 }
@@ -254,13 +263,11 @@ static int internal_dev_attach(struct vport *vport)
 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;
 }
@@ -322,5 +329,5 @@ int is_internal_vport(const struct vport *vport)
 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);
 }
index d5de215..7bf56f5 100644 (file)
@@ -59,8 +59,7 @@ static int netdev_init(void)
        return 0;
 }
 
-static void
-netdev_exit(void)
+static void netdev_exit(void)
 {
        br_handle_frame_hook = NULL;
 }
@@ -224,8 +223,7 @@ unsigned char netdev_get_operstate(const struct vport *vport)
        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;
@@ -253,9 +251,11 @@ static void netdev_port_receive(struct net_bridge_port *p, struct sk_buff *skb)
         * (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);
index edf9af0..349297c 100644 (file)
@@ -35,17 +35,6 @@ static int n_vport_types;
 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
@@ -798,9 +787,6 @@ int vport_attach(struct vport *vport, struct dp_port *dp_port)
 {
        ASSERT_RTNL();
 
-       if (dp_port->vport)
-               return -EBUSY;
-
        if (vport_get_dp_port(vport))
                return -EBUSY;
 
@@ -812,7 +798,6 @@ int vport_attach(struct vport *vport, struct dp_port *dp_port)
                        return err;
        }
 
-       dp_port->vport = vport;
        rcu_assign_pointer(vport->dp_port, dp_port);
 
        return 0;
@@ -836,7 +821,6 @@ int vport_detach(struct vport *vport)
        if (!dp_port)
                return -EINVAL;
 
-       dp_port->vport = NULL;
        rcu_assign_pointer(vport->dp_port, NULL);
 
        if (vport->ops->detach)
@@ -1243,20 +1227,9 @@ static inline unsigned packet_length(const struct sk_buff *skb)
  */
 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())
@@ -1279,17 +1252,12 @@ int vport_send(struct vport *vport, struct sk_buff *skb)
                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;
 }
 
 /**
index 7367bbf..a705927 100644 (file)
@@ -211,7 +211,7 @@ struct xflow_flow_stats {
     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. */
 };
 
index 8c4ec29..ae889a1 100644 (file)
@@ -61,6 +61,8 @@ lib_libopenvswitch_a_SOURCES = \
        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 \
@@ -167,6 +169,9 @@ lib_libsflow_a_CFLAGS = $(AM_CFLAGS)
 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 += \
index c4bf1ad..4f33bd2 100644 (file)
@@ -231,8 +231,6 @@ flow_extract_stats(const flow_t *flow, struct ofpbuf *packet,
     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);
index 41257b0..5c32c06 100644 (file)
 
 #include "shash.h"
 
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
 struct ds;
 
 /* Type of a JSON value. */
@@ -128,4 +132,8 @@ void json_to_ds(const struct json *, int flags, struct ds *);
 
 bool json_string_unescape(const char *in, size_t in_len, char **outp);
 
+#ifdef  __cplusplus
+}
+#endif
+
 #endif /* json.h */
index f411a05..d89ff12 100644 (file)
@@ -26,6 +26,7 @@
 #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"
@@ -84,6 +85,8 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
 
 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,
@@ -107,10 +110,17 @@ static packet_handler_func process_stats_reply;
  * 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;
@@ -140,6 +150,9 @@ lswitch_create(struct rconn *rconn, bool learn_macs,
         sw->port_states[i] = P_DISABLED;
     }
     send_features_request(sw, rconn);
+    if (default_flows) {
+        send_default_flows(sw, rconn, default_flows);
+    }
     return sw;
 }
 
@@ -359,6 +372,53 @@ send_features_request(struct lswitch *sw, struct rconn *rconn)
     }
 }
 
+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)
 {
@@ -473,22 +533,29 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
     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);
 
index a2ac53b..3b414a5 100644 (file)
 
 #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 *);
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
new file mode 100644 (file)
index 0000000..bcd608b
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * 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);
+    }
+}
diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
new file mode 100644 (file)
index 0000000..ea28e7b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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 */
index 851e7a7..34dcc1f 100644 (file)
@@ -975,32 +975,75 @@ update_ssl_config(struct ssl_config_file *config, const char *file_name)
     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,
index dd2a16e..ba6e422 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
index 955cf30..0fd0de1 100644 (file)
--- a/lib/tag.c
+++ b/lib/tag.c
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -49,7 +49,7 @@ tag_type
 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);
 }
@@ -61,11 +61,36 @@ tag_set_init(struct tag_set *set)
     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
index 2513f93..8f3872e 100644 (file)
@@ -44,6 +44,7 @@ VLOG_MODULE(netdev_vport)
 VLOG_MODULE(netflow)
 VLOG_MODULE(netlink)
 VLOG_MODULE(ofctl)
+VLOG_MODULE(ofp_parse)
 VLOG_MODULE(ofp_util)
 VLOG_MODULE(ofproto)
 VLOG_MODULE(openflowd)
index 318a979..fcb0aba 100644 (file)
@@ -102,7 +102,6 @@ struct xf_netdev_flow {
     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. */
@@ -682,7 +681,7 @@ answer_flow_query(struct xf_netdev_flow *flow, uint32_t query_flags,
         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);
@@ -827,7 +826,6 @@ clear_stats(struct xf_netdev_flow *flow)
     flow->used.tv_nsec = 0;
     flow->packet_count = 0;
     flow->byte_count = 0;
-    flow->ip_tos = 0;
     flow->tcp_ctl = 0;
 }
 
@@ -1007,14 +1005,9 @@ xf_netdev_flow_used(struct xf_netdev_flow *flow,
     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;
     }
 }
 
index dd14a8b..3d913af 100644 (file)
@@ -170,7 +170,7 @@ netflow_expire(struct netflow *nf, struct netflow_flow *nf_flow,
     }
     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;
@@ -271,10 +271,8 @@ netflow_flow_update_time(struct netflow *nf, struct netflow_flow *nf_flow,
 }
 
 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;
 }
 
index f3099a1..f6fad62 100644 (file)
@@ -52,7 +52,6 @@ struct netflow_flow {
     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. */
 };
 
@@ -66,8 +65,7 @@ void netflow_run(struct netflow *);
 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 */
index ed1f314..cb0874a 100644 (file)
@@ -101,19 +101,44 @@ static void delete_flow(struct ofproto *, struct wdp_rule *, uint8_t reason);
 
 /* 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. */
@@ -134,7 +159,7 @@ struct ofconn {
 #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. */
@@ -159,6 +184,7 @@ static void ofconn_run(struct ofconn *, struct ofproto *);
 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);
@@ -196,8 +222,10 @@ struct ofproto {
     /* 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;
 };
@@ -274,8 +302,7 @@ ofproto_create(const char *datapath, const char *datapath_type,
     /* 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;
 
@@ -333,7 +360,7 @@ add_controller(struct ofproto *ofproto, const struct ofproto_controller *c)
         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) {
@@ -354,9 +381,7 @@ add_controller(struct ofproto *ofproto, const struct ofproto_controller *c)
 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);
@@ -372,21 +397,7 @@ update_controller(struct ofconn *ofconn, const struct ofproto_controller *c)
         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 *
@@ -468,30 +479,72 @@ update_in_band_remotes(struct ofproto *ofproto)
     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;
 
@@ -503,36 +556,28 @@ ofproto_set_controllers(struct ofproto *p,
             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),
@@ -542,6 +587,13 @@ ofproto_set_controllers(struct ofproto *p,
     }
 }
 
+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
@@ -684,12 +736,6 @@ set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp,
     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)
 {
@@ -748,19 +794,15 @@ ofproto_get_datapath_id(const struct ofproto *ofproto)
 }
 
 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
@@ -776,6 +818,7 @@ ofproto_get_snoops(const struct ofproto *ofproto, struct svec *snoops)
 void
 ofproto_destroy(struct ofproto *p)
 {
+    struct ofservice *ofservice, *next_ofservice;
     struct ofconn *ofconn, *next_ofconn;
     size_t i;
 
@@ -805,10 +848,11 @@ ofproto_destroy(struct ofproto *p)
     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]);
@@ -863,7 +907,7 @@ add_snooper(struct ofproto *ofproto, struct vconn *vconn)
     /* 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;
         }
@@ -905,6 +949,7 @@ int
 ofproto_run1(struct ofproto *p)
 {
     struct ofconn *ofconn, *next_ofconn;
+    struct ofservice *ofservice;
     int i;
 
     for (i = 0; i < 50; i++) {
@@ -948,21 +993,24 @@ ofproto_run1(struct ofproto *p)
         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));
         }
@@ -999,6 +1047,7 @@ ofproto_run2(struct ofproto *p OVS_UNUSED, bool revalidate_all OVS_UNUSED)
 void
 ofproto_wait(struct ofproto *p)
 {
+    struct ofservice *ofservice;
     struct ofconn *ofconn;
     size_t i;
 
@@ -1017,8 +1066,8 @@ ofproto_wait(struct ofproto *p)
     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]);
@@ -1120,7 +1169,7 @@ ofconn_create(struct ofproto *p, struct rconn *rconn, enum ofconn_type type)
 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);
@@ -1206,14 +1255,13 @@ ofconn_wait(struct ofconn *ofconn)
 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;
     }
 }
@@ -1229,6 +1277,85 @@ ofconn_make_name(const struct ofproto *ofproto, const char *target)
 {
     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)
@@ -1360,7 +1487,7 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
     }
     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);
@@ -1388,7 +1515,7 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
 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;
 
@@ -2384,7 +2511,7 @@ handle_role_request(struct ofproto *ofproto,
     }
     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);
index 2925ebc..620ed8b 100644 (file)
@@ -67,7 +67,6 @@ struct ofproto_controller {
     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. */
@@ -99,6 +98,7 @@ bool ofproto_is_alive(const struct ofproto *);
 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);
@@ -106,7 +106,6 @@ void ofproto_set_desc(struct ofproto *,
                       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);
@@ -115,7 +114,8 @@ int ofproto_set_stp(struct ofproto *, bool enable_stp);
 
 /* 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 *);
@@ -139,9 +139,9 @@ struct ofhooks {
     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);
index a8f522d..644e3a4 100644 (file)
@@ -132,20 +132,11 @@ config_status_cb(struct status_reply *sr, void *ofproto_)
 {
     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
index d1e7c0c..b2208a4 100644 (file)
@@ -231,8 +231,7 @@ wx_rule_update_stats(struct wx *wx, struct wx_rule *rule,
         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); */
     }
 }
 
@@ -1680,10 +1679,6 @@ query_stats(struct wx *wx, struct wx_rule *rule, struct wdp_flow_stats *stats)
             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;
         }
index 4ca9c2d..27db070 100644 (file)
@@ -283,8 +283,8 @@ reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
 
 #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
index 061a4f4..56fc1ce 100644 (file)
@@ -360,76 +360,21 @@ OVS_VSCTL_SETUP
 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
@@ -577,6 +522,7 @@ controller          : []
 datapath_id         : []
 datapath_type       : ""
 external_ids        : {}
+fail_mode           : []
 flood_vlans         : []
 mirrors             : []
 name                : "br0"
@@ -638,6 +584,7 @@ active_timeout      : 0
 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])], 
@@ -773,6 +720,7 @@ controller          : []
 datapath_id         : []
 datapath_type       : ""
 external_ids        : {}
+fail_mode           : []
 flood_vlans         : []
 mirrors             : []
 name                : "br0"
index ab63677..c5954dd 100644 (file)
@@ -100,6 +100,12 @@ instead use that specific queue.
 .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
@@ -133,4 +139,5 @@ connection and set the controller, e.g.:
 .
 .BR ovs\-openflowd (8),
 .BR ovs\-appctl (8),
+.BR ovs\-ofctl (8),
 .BR ovs\-dpctl (8)
index 92b84ae..42431bb 100644 (file)
@@ -21,6 +21,7 @@
 #include <limits.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 
 #include "command-line.h"
@@ -70,6 +71,10 @@ static bool mute = false;
 /* -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;
 
@@ -213,9 +218,17 @@ new_switch(struct switch_ *sw, struct vconn *vconn)
 {
     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);
 }
 
@@ -248,6 +261,7 @@ parse_options(int argc, char *argv[])
         OPT_MAX_IDLE = UCHAR_MAX + 1,
         OPT_PEER_CA_CERT,
         OPT_MUTE,
+        OPT_WITH_FLOWS,
         OPT_UNIXCTL,
         VLOG_OPTION_ENUMS
     };
@@ -259,6 +273,7 @@ parse_options(int argc, char *argv[])
         {"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'},
@@ -318,6 +333,13 @@ parse_options(int argc, char *argv[])
             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;
@@ -367,6 +389,7 @@ usage(void)
            "  -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");
index a3466da..d8f1a0a 100644 (file)
@@ -378,6 +378,11 @@ of the following keywords:
 .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.)
index 598f652..2848ade 100644 (file)
  */
 
 #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"
@@ -398,99 +392,6 @@ do_dump_tables(int argc OVS_UNUSED, char *argv[])
     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)
 {
@@ -533,359 +434,6 @@ 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[])
 {
@@ -894,8 +442,8 @@ 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);
 
@@ -910,8 +458,8 @@ do_dump_aggregate(int argc, char *argv[])
     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);
 
@@ -944,12 +492,13 @@ do_add_flow(int argc OVS_UNUSED, char *argv[])
     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);
@@ -1003,13 +552,12 @@ do_add_flows(int argc OVS_UNUSED, char *argv[])
             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);
@@ -1048,12 +596,13 @@ do_mod_flows(int argc OVS_UNUSED, char *argv[])
     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) {
@@ -1087,8 +636,8 @@ static void do_del_flows(int argc, char *argv[])
 
     /* 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 {
index d96d823..4cfd8f2 100644 (file)
@@ -19,12 +19,21 @@ to relay.  It takes one of the following forms:
 .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).
 .
index d21585e..e5d30d3 100644 (file)
@@ -54,6 +54,7 @@ struct ofsettings {
     /* Controller configuration. */
     struct ofproto_controller *controllers;
     size_t n_controllers;
+    enum ofproto_fail_mode fail_mode;
 
     /* Datapath. */
     uint64_t datapath_id;       /* Datapath ID. */
@@ -69,7 +70,6 @@ struct ofsettings {
     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. */
@@ -140,16 +140,6 @@ main(int argc, char *argv[])
     }
     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,
@@ -166,6 +156,7 @@ main(int argc, char *argv[])
         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();
 
@@ -261,24 +252,26 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
     };
     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;
@@ -330,10 +323,10 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
 
         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\"");
@@ -407,7 +400,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
             break;
 
         case 'l':
-            svec_add(&s->listeners, optarg);
+            svec_add(&controllers, optarg);
             break;
 
         case OPT_SNOOP:
@@ -450,8 +443,8 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
 
     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");
     }
 
@@ -470,19 +463,28 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
     /* 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. */
index 5c8c4bb..1f52869 100755 (executable)
@@ -161,11 +161,11 @@ if test -z "$command"; then
     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
@@ -176,6 +176,15 @@ case $log in
     *) $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
@@ -316,7 +325,7 @@ resolve_prefix() {
         ????*)
             ;;
         *)
-            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
@@ -324,13 +333,13 @@ resolve_prefix() {
     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
             ;;
         *)
@@ -451,13 +460,15 @@ OU = Open vSwitch certifier
 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() {
@@ -515,8 +526,14 @@ elif test "$command" = self-sign; then
     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"
 
index 3137a84..ea8a8aa 100644 (file)
@@ -310,22 +310,15 @@ output.
 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
@@ -352,13 +345,13 @@ it discontinues its standalone behavior.
 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"
@@ -450,9 +443,7 @@ A port mirroring configuration attached to a bridge.  Records may be
 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.
index 76e896a..d68e474 100644 (file)
@@ -440,12 +440,12 @@ Interface commands (a bond consists of multiple interfaces):\n\
   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\
@@ -512,6 +512,7 @@ struct vsctl_bridge {
     struct ovsrec_bridge *br_cfg;
     char *name;
     struct ovsrec_controller **ctrl;
+    char *fail_mode;
     size_t n_ctrl;
     struct vsctl_bridge *parent;
     int vlan;
@@ -531,8 +532,6 @@ struct vsctl_info {
     struct shash bridges;
     struct shash ports;
     struct shash ifaces;
-    struct ovsrec_controller **ctrl;
-    size_t n_ctrl;
 };
 
 static char *
@@ -572,9 +571,11 @@ add_bridge(struct vsctl_info *b,
     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;
@@ -630,9 +631,6 @@ get_info(const struct ovsrec_open_vswitch *ovs, struct vsctl_info *info)
     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++) {
@@ -885,7 +883,6 @@ cmd_emer_reset(struct vsctl_context *ctx)
 
     /* 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) {
@@ -1541,20 +1538,21 @@ cmd_iface_to_br(struct vsctl_context *ctx)
     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);
@@ -1562,25 +1560,6 @@ print_controllers(struct vsctl_context *ctx,
         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);
 }
@@ -1600,20 +1579,14 @@ static void
 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);
@@ -1634,123 +1607,53 @@ insert_controllers(struct ovsdb_idl_txn *txn, char *targets[], size_t n)
     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);
 }
@@ -1759,37 +1662,17 @@ static void
 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);
 }
@@ -1860,10 +1743,7 @@ static const struct vsctl_table_class tables[] = {
     {&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},
@@ -2966,12 +2846,12 @@ static const struct vsctl_command_syntax all_commands[] = {
     {"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, ""},
index 2bc263d..e66313f 100644 (file)
@@ -199,13 +199,10 @@ static struct bridge *bridge_lookup(const char *name);
 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);
@@ -347,19 +344,6 @@ bridge_configure_once(const struct ovsrec_open_vswitch *cfg)
     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
@@ -598,14 +582,9 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     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.
@@ -798,7 +777,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
             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;
@@ -821,7 +800,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
          * 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++) {
@@ -1108,7 +1087,10 @@ iface_refresh_stats(struct iface *iface)
 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. */
@@ -1124,8 +1106,9 @@ bridge_run(void)
     }
 
     /* (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);
 
@@ -1144,6 +1127,18 @@ bridge_run(void)
         }
     }
 
+#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;
@@ -1176,7 +1171,7 @@ bridge_wait(void)
 
     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;
         }
 
@@ -1385,20 +1380,14 @@ bridge_run_one(struct bridge *br)
 }
 
 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;
@@ -1412,13 +1401,12 @@ bridge_get_controllers(const struct ovsrec_open_vswitch *ovs_cfg,
 }
 
 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. */
@@ -1441,7 +1429,7 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg,
      * 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;
 
@@ -1487,22 +1475,21 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg,
     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",
@@ -1518,31 +1505,149 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg,
     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);
@@ -1550,76 +1655,6 @@ bridge_reconfigure_remotes(const struct ovsrec_open_vswitch *ovs_cfg,
         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);
     }
 }
 
@@ -2459,11 +2494,12 @@ bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
     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_)
@@ -2471,20 +2507,24 @@ bridge_account_flow_ofhook_cb(const flow_t *flow,
     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);
index b38463b..7f5863f 100644 (file)
@@ -6,10 +6,6 @@
          "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}}}
index b93a8db..d29e145 100644 (file)
         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">
index 36d0afd..0988fcc 100755 (executable)
@@ -359,6 +359,10 @@ case "$1" in
     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") "$@"
@@ -379,7 +383,7 @@ case "$1" in
         /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"