Merge commit 'origin/citrix'
authorJustin Pettit <jpettit@nicira.com>
Sat, 5 Sep 2009 05:23:29 +0000 (22:23 -0700)
committerJustin Pettit <jpettit@nicira.com>
Sat, 5 Sep 2009 05:23:29 +0000 (22:23 -0700)
1  2 
datapath/datapath.c
lib/rconn.c
vswitchd/ovs-vswitchd.conf.5.in
xenserver/vswitch-xen.spec

diff --combined datapath/datapath.c
@@@ -165,8 -165,7 +165,8 @@@ static void dp_ifinfo_notify(int event
                kfree_skb(skb);
                goto errout;
        }
 -      err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
 +      rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
 +      return;
  errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_LINK, err);
@@@ -225,7 -224,9 +225,7 @@@ static int create_dp(int dp_idx, const 
  
        /* Initialize kobject for bridge.  This will be added as
         * /sys/class/net/<devname>/brif later, if sysfs is enabled. */
 -      kobject_set_name(&dp->ifobj, SYSFS_BRIDGE_PORT_SUBDIR); /* "brif" */
        dp->ifobj.kset = NULL;
 -      dp->ifobj.parent = NULL;
        kobject_init(&dp->ifobj, &dp_ktype);
  
        /* Allocate table. */
        mutex_unlock(&dp_mutex);
        rtnl_unlock();
  
 -#ifdef SUPPORT_SYSFS
        dp_sysfs_add_dp(dp);
 -#endif
  
        return 0;
  
@@@ -283,7 -286,9 +283,7 @@@ static void do_destroy_dp(struct datapa
                if (p->port_no != ODPP_LOCAL)
                        dp_del_port(p);
  
 -#ifdef SUPPORT_SYSFS
        dp_sysfs_del_dp(dp);
 -#endif
  
        rcu_assign_pointer(dps[dp->dp_idx], NULL);
  
@@@ -328,7 -333,7 +328,7 @@@ static void release_nbp(struct kobject 
  }
  
  struct kobj_type brport_ktype = {
 -#ifdef SUPPORT_SYSFS
 +#ifdef CONFIG_SYSFS
        .sysfs_ops = &brport_sysfs_ops,
  #endif
        .release = release_nbp
@@@ -365,7 -370,9 +365,7 @@@ static int new_nbp(struct datapath *dp
  
        /* Initialize kobject for bridge.  This will be added as
         * /sys/class/net/<devname>/brport later, if sysfs is enabled. */
 -      kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR); /* "brport" */
        p->kobj.kset = NULL;
 -      p->kobj.parent = &p->dev->NETDEV_DEV_MEMBER.kobj;
        kobject_init(&p->kobj, &brport_ktype);
  
        dp_ifinfo_notify(RTM_NEWLINK, p);
@@@ -385,6 -392,11 +385,6 @@@ static int add_port(int dp_idx, struct 
        if (copy_from_user(&port, portp, sizeof port))
                goto out;
        port.devname[IFNAMSIZ - 1] = '\0';
 -      port_no = port.port;
 -
 -      err = -EINVAL;
 -      if (port_no < 0 || port_no >= DP_MAX_PORTS)
 -              goto out;
  
        rtnl_lock();
        dp = get_dp_locked(dp_idx);
        if (!dp)
                goto out_unlock_rtnl;
  
 -      err = -EEXIST;
 -      if (dp->ports[port_no])
 -              goto out_unlock_dp;
 +      for (port_no = 1; port_no < DP_MAX_PORTS; port_no++)
 +              if (!dp->ports[port_no])
 +                      goto got_port_no;
 +      err = -EFBIG;
 +      goto out_unlock_dp;
  
 +got_port_no:
        if (!(port.flags & ODP_PORT_INTERNAL)) {
                err = -ENODEV;
                dev = dev_get_by_name(&init_net, port.devname);
        if (err)
                goto out_put;
  
 -#ifdef SUPPORT_SYSFS
        dp_sysfs_add_if(dp->ports[port_no]);
 -#endif
 +
 +      err = __put_user(port_no, &port.port);
  
  out_put:
        dev_put(dev);
@@@ -439,8 -448,10 +439,8 @@@ int dp_del_port(struct net_bridge_port 
  {
        ASSERT_RTNL();
  
 -#ifdef SUPPORT_SYSFS
        if (p->port_no != ODPP_LOCAL)
                dp_sysfs_del_if(p);
 -#endif
        dp_ifinfo_notify(RTM_DELLINK, p);
  
        p->dp->n_ports--;
@@@ -576,7 -587,7 +576,7 @@@ static int dp_frame_hook(struct net_bri
  #error
  #endif
  
 -#ifdef CONFIG_XEN
 +#if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
  /* This code is copied verbatim from net/dev/core.c in Xen's
   * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644.  We can't call those functions
   * directly because they aren't exported. */
@@@ -592,7 -603,7 +592,7 @@@ static int skb_pull_up_to(struct sk_buf
        }
  }
  
 -int skb_checksum_setup(struct sk_buff *skb)
 +int vswitch_skb_checksum_setup(struct sk_buff *skb)
  {
        if (skb->proto_csum_blank) {
                if (skb->protocol != htons(ETH_P_IP))
  out:
        return -EPROTO;
  }
 -#endif
 +#else
 +int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; }
 +#endif /* CONFIG_XEN && linux == 2.6.18 */
  
  int
  dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
  
        /* If a checksum-deferred packet is forwarded to the controller,
         * correct the pointers and checksum.  This happens on a regular basis
 -       * only on Xen (the CHECKSUM_HW case), on which VMs can pass up packets
 -       * that do not have their checksum computed.  We also implement it for
 -       * the non-Xen case, but it is difficult to trigger or test this case
 -       * there, hence the WARN_ON_ONCE().
 +       * only on Xen, on which VMs can pass up packets that do not have their
 +       * checksum computed.
         */
 -      err = skb_checksum_setup(skb);
 +      err = vswitch_skb_checksum_setup(skb);
        if (err)
                goto err_kfree_skb;
  #ifndef CHECKSUM_HW
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
 -              WARN_ON_ONCE(1);
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
                /* Until 2.6.22, the start of the transport header was also the
                 * start of data to be checksummed.  Linux 2.6.22 introduced
@@@ -824,7 -836,6 +824,7 @@@ static void get_stats(struct sw_flow *f
        stats->n_bytes = flow->byte_count;
        stats->ip_tos = flow->ip_tos;
        stats->tcp_flags = flow->tcp_flags;
 +      stats->error = 0;
  }
  
  static void clear_stats(struct sw_flow *flow)
@@@ -952,6 -963,8 +952,6 @@@ static int put_actions(const struct sw_
  
        if (!n_actions)
                return 0;
 -      if (ufp->n_actions > INT_MAX / sizeof(union odp_action))
 -              return -EINVAL;
  
        sf_acts = rcu_dereference(flow->sf_acts);
        if (__put_user(sf_acts->n_actions, &ufp->n_actions) ||
@@@ -977,7 -990,9 +977,7 @@@ static int answer_query(struct sw_flow 
        return put_actions(flow, ufp);
  }
  
 -static int del_or_query_flow(struct datapath *dp,
 -                           struct odp_flow __user *ufp,
 -                           unsigned int cmd)
 +static int del_flow(struct datapath *dp, struct odp_flow __user *ufp)
  {
        struct dp_table *table = rcu_dereference(dp->table);
        struct odp_flow uf;
        if (!flow)
                goto error;
  
 -      if (cmd == ODP_FLOW_DEL) {
 -              /* XXX redundant lookup */
 -              error = dp_table_delete(table, flow);
 -              if (error)
 -                      goto error;
 +      /* XXX redundant lookup */
 +      error = dp_table_delete(table, flow);
 +      if (error)
 +              goto error;
  
 -              /* XXX These statistics might lose a few packets, since other
 -               * CPUs can be using this flow.  We used to synchronize_rcu()
 -               * to make sure that we get completely accurate stats, but that
 -               * blows our performance, badly. */
 -              dp->n_flows--;
 -              error = answer_query(flow, ufp);
 -              flow_deferred_free(flow);
 -      } else {
 -              error = answer_query(flow, ufp);
 -      }
 +      /* XXX These statistics might lose a few packets, since other CPUs can
 +       * be using this flow.  We used to synchronize_rcu() to make sure that
 +       * we get completely accurate stats, but that blows our performance,
 +       * badly. */
 +      dp->n_flows--;
 +      error = answer_query(flow, ufp);
 +      flow_deferred_free(flow);
  
  error:
        return error;
  }
  
 -static int query_multiple_flows(struct datapath *dp,
 -                              const struct odp_flowvec *flowvec)
 +static int query_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
  {
        struct dp_table *table = rcu_dereference(dp->table);
        int i;
  
                flow = dp_table_lookup(table, &uf.key);
                if (!flow)
 -                      error = __clear_user(&ufp->stats, sizeof ufp->stats);
 +                      error = __put_user(ENOENT, &ufp->stats.error);
                else
                        error = answer_query(flow, ufp);
                if (error)
@@@ -1107,6 -1127,7 +1107,7 @@@ static int do_execute(struct datapath *
        struct odp_flow_key key;
        struct sk_buff *skb;
        struct sw_flow_actions *actions;
+       struct ethhdr *eth;
        int err;
  
        err = -EFAULT;
                           execute.length))
                goto error_free_skb;
  
+       skb_reset_mac_header(skb);
+       eth = eth_hdr(skb);
+     /* Normally, setting the skb 'protocol' field would be handled by a
+      * call to eth_type_trans(), but it assumes there's a sending
+      * device, which we may not have. */
+       if (ntohs(eth->h_proto) >= 1536)
+               skb->protocol = eth->h_proto;
+       else
+               skb->protocol = htons(ETH_P_802_2);
        flow_extract(skb, execute.in_port, &key);
        err = execute_actions(dp, skb, &key, actions->actions,
                              actions->n_actions, GFP_KERNEL);
@@@ -1160,7 -1192,8 +1172,7 @@@ error
        return err;
  }
  
 -static int
 -get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
 +static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
  {
        struct odp_stats stats;
        int i;
@@@ -1275,7 -1308,7 +1287,7 @@@ list_ports(struct datapath *dp, struct 
                                break;
                }
        }
 -      return put_user(idx, &pvp->n_ports);
 +      return put_user(dp->n_ports, &pvp->n_ports);
  }
  
  /* RCU callback for freeing a dp_port_group */
@@@ -1359,28 -1392,24 +1371,28 @@@ static long openvswitch_ioctl(struct fi
        /* Handle commands with special locking requirements up front. */
        switch (cmd) {
        case ODP_DP_CREATE:
 -              return create_dp(dp_idx, (char __user *)argp);
 +              err = create_dp(dp_idx, (char __user *)argp);
 +              goto exit;
  
        case ODP_DP_DESTROY:
 -              return destroy_dp(dp_idx);
 +              err = destroy_dp(dp_idx);
 +              goto exit;
  
        case ODP_PORT_ADD:
 -              return add_port(dp_idx, (struct odp_port __user *)argp);
 +              err = add_port(dp_idx, (struct odp_port __user *)argp);
 +              goto exit;
  
        case ODP_PORT_DEL:
                err = get_user(port_no, (int __user *)argp);
 -              if (err)
 -                      break;
 -              return del_port(dp_idx, port_no);
 +              if (!err)
 +                      err = del_port(dp_idx, port_no);
 +              goto exit;
        }
  
        dp = get_dp_locked(dp_idx);
 +      err = -ENODEV;
        if (!dp)
 -              return -ENODEV;
 +              goto exit;
  
        switch (cmd) {
        case ODP_DP_STATS:
                break;
  
        case ODP_FLOW_DEL:
 -      case ODP_FLOW_GET:
 -              err = del_or_query_flow(dp, (struct odp_flow __user *)argp,
 -                                      cmd);
 +              err = del_flow(dp, (struct odp_flow __user *)argp);
                break;
  
 -      case ODP_FLOW_GET_MULTIPLE:
 -              err = do_flowvec_ioctl(dp, argp, query_multiple_flows);
 +      case ODP_FLOW_GET:
 +              err = do_flowvec_ioctl(dp, argp, query_flows);
                break;
  
        case ODP_FLOW_LIST:
                break;
        }
        mutex_unlock(&dp->mutex);
 +exit:
        return err;
  }
  
diff --combined lib/rconn.c
@@@ -89,7 -89,7 +89,7 @@@ struct rconn 
      time_t last_admitted;
  
      /* These values are simply for statistics reporting, not used directly by
 -     * anything internal to the rconn (or the secchan for that matter). */
 +     * anything internal to the rconn (or ofproto for that matter). */
      unsigned int packets_received;
      unsigned int n_attempted_connections, n_successful_connections;
      time_t creation_time;
       * a response. */
      int probe_interval;         /* Secs of inactivity before sending probe. */
  
+     /* When we create a vconn we obtain these values, to save them past the end
+      * of the vconn's lifetime.  Otherwise, in-band control will only allow
+      * traffic when a vconn is actually open, but it is nice to allow ARP to
+      * complete even between connection attempts, and it is also polite to
+      * allow traffic from other switches to go through to the controller
+      * whether or not we are connected.
+      *
+      * We don't cache the local port, because that changes from one connection
+      * attempt to the next. */
+     uint32_t local_ip, remote_ip;
+     uint16_t remote_port;
      /* Messages sent or received are copied to the monitor connections. */
  #define MAX_MONITORS 8
      struct vconn *monitors[8];
@@@ -121,6 -133,7 +133,7 @@@ static unsigned int elapsed_in_this_sta
  static unsigned int timeout(const struct rconn *);
  static bool timed_out(const struct rconn *);
  static void state_transition(struct rconn *, enum state);
+ static void set_vconn_name(struct rconn *, const char *name);
  static int try_send(struct rconn *);
  static int reconnect(struct rconn *);
  static void disconnect(struct rconn *, int error);
@@@ -236,8 -249,7 +249,7 @@@ in
  rconn_connect(struct rconn *rc, const char *name)
  {
      rconn_disconnect(rc);
-     free(rc->name);
-     rc->name = xstrdup(name);
+     set_vconn_name(rc, name);
      rc->reliable = true;
      return reconnect(rc);
  }
@@@ -248,8 -260,7 +260,7 @@@ rconn_connect_unreliably(struct rconn *
  {
      assert(vconn != NULL);
      rconn_disconnect(rc);
-     free(rc->name);
-     rc->name = xstrdup(name);
+     set_vconn_name(rc, name);
      rc->reliable = false;
      rc->vconn = vconn;
      rc->last_connected = time_now();
@@@ -273,8 -284,7 +284,7 @@@ rconn_disconnect(struct rconn *rc
              vconn_close(rc->vconn);
              rc->vconn = NULL;
          }
-         free(rc->name);
-         rc->name = xstrdup("void");
+         set_vconn_name(rc, "void");
          rc->reliable = false;
  
          rc->backoff = 0;
@@@ -323,6 -333,9 +333,9 @@@ reconnect(struct rconn *rc
      rc->n_attempted_connections++;
      retval = vconn_open(rc->name, OFP_VERSION, &rc->vconn);
      if (!retval) {
+         rc->remote_ip = vconn_get_remote_ip(rc->vconn);
+         rc->local_ip = vconn_get_local_ip(rc->vconn);
+         rc->remote_port = vconn_get_remote_port(rc->vconn);
          rc->backoff_deadline = time_now() + rc->backoff;
          state_transition(rc, S_CONNECTING);
      } else {
@@@ -635,20 -648,20 +648,20 @@@ rconn_failure_duration(const struct rco
      return rconn_is_connected(rconn) ? 0 : time_now() - rconn->last_admitted;
  }
  
- /* Returns the IP address of the peer, or 0 if the peer is not connected over
-  * an IP-based protocol or if its IP address is not known. */
+ /* Returns the IP address of the peer, or 0 if the peer's IP address is not
+  * known. */
  uint32_t
  rconn_get_remote_ip(const struct rconn *rconn) 
  {
-     return rconn->vconn ? vconn_get_remote_ip(rconn->vconn) : 0;
+     return rconn->remote_ip;
  }
  
- /* Returns the transport port of the peer, or 0 if the peer does not 
-  * contain a port or if the port is not known. */
+ /* Returns the transport port of the peer, or 0 if the peer's port is not
+  * known. */
  uint16_t
  rconn_get_remote_port(const struct rconn *rconn) 
  {
-     return rconn->vconn ? vconn_get_remote_port(rconn->vconn) : 0;
+     return rconn->remote_port;
  }
  
  /* Returns the IP address used to connect to the peer, or 0 if the
  uint32_t
  rconn_get_local_ip(const struct rconn *rconn) 
  {
-     return rconn->vconn ? vconn_get_local_ip(rconn->vconn) : 0;
+     return rconn->local_ip;
  }
  
  /* Returns the transport port used to connect to the peer, or 0 if the
@@@ -805,6 -818,19 +818,19 @@@ rconn_packet_counter_dec(struct rconn_p
      }
  }
  \f
+ /* Set the name of the remote vconn to 'name' and clear out the cached IP
+  * address and port information, since changing the name also likely changes
+  * these values. */
+ static void
+ set_vconn_name(struct rconn *rc, const char *name)
+ {
+     free(rc->name);
+     rc->name = xstrdup(name);
+     rc->local_ip = 0;
+     rc->remote_ip = 0;
+     rc->remote_port = 0;
+ }
  /* Tries to send a packet from 'rc''s send buffer.  Returns 0 if successful,
   * otherwise a positive errno value. */
  static int
  .  RE
  .  PP
  ..
 -.TH ovs\-vswitchd.conf 5 "April 2009" "Open vSwitch" "OpenVSwitch Manual"
 +.TH ovs\-vswitchd.conf 5 "June 2009" "Open vSwitch" "Open vSwitch Manual"
  .
  .SH NAME
  ovs\-vswitchd.conf \- configuration file for \fBovs\-vswitchd\fR
  .
  .SH DESCRIPTION
  This manual page describes the syntax for the configuration file used 
 -by \fBovs\-vswitchd\fR(8), the virtual switch daemon.
 +by \fBovs\-vswitchd\fR(8), the Open vSwitch daemon.
  .PP
  The configuration file is based on key-value pairs, which are given
  one per line in the form \fIkey\fB=\fIvalue\fR.  Each \fIkey\fR
@@@ -50,13 -50,14 +50,13 @@@ configure \fBovs\-vswitchd\fR
  .SS "Bridge Configuration"
  A bridge (switch) with a given \fIname\fR is configured by specifying
  the names of its network devices as values for key
 -\fBbridge.\fIname\fB.port\fR.  (The specified \fIname\fR may not begin
 -with \fBdp\fR or \fBnl:\fR followed by a digit.)
 +\fBbridge.\fIname\fB.port\fR.
  .PP
  The names given on \fBbridge.\fIname\fB.port\fR must be the names of
  existing network devices, except for ``internal ports.''  An internal
  port is a simulated network device that receives traffic only
 -through the virtual switch and switches any traffic sent it through
 -virtual switch.  An internal port may configured with an IP address,
 +through the switch and switches any traffic sent it through the
 +switch.  An internal port may configured with an IP address,
  etc. using the usual system tools (e.g. \fBifconfig\fR, \fBip\fR).  To
  designate network device \fInetdev\fR as an internal port, add
  \fBiface.\fInetdev\fB.internal=true\fR to the configuration file.
@@@ -266,6 -267,22 +266,22 @@@ a trunk port, the frame's VLAN tag wil
  any existing tag; when it is sent out an implicit VLAN port, the frame
  will not be tagged.  This type of mirroring may be referred to as
  RSPAN.
+ .IP
+ Please note that mirroring to a VLAN can disrupt a network that
+ contains unmanaged switches.  Consider an unmanaged physical switch
+ with two ports: port 1, connected to an end host, and port 2,
+ connected to an Open vSwitch configured to mirror received packets
+ into VLAN 123 on port 2.  Suppose that the end host sends a packet on
+ port 1 that the physical switch forwards to port 2.  The Open vSwitch
+ forwards this packet to its destination and then reflects it back on
+ port 2 in VLAN 123.  This reflected packet causes the unmanaged
+ physical switch to replace the MAC learning table entry, which
+ correctly pointed to port 1, with one that incorrectly points to port
+ 2.  Afterward, the physical switch will direct packets destined for
+ the end host to the Open vSwitch on port 2, instead of to the end host
+ on port 1, disrupting connectivity.  If mirroring to a VLAN is desired
+ in this scenario, then the physical switch must be replaced by one
+ that learns Ethernet addresses on a per-VLAN basis.
  .ST "Example"
  The following \fBovs\-vswitchd\fR configuration copies all frames received
  on \fBeth1\fR or \fBeth2\fR to \fBeth3\fR.
@@@ -338,7 -355,7 +354,7 @@@ This can be overridden with the \fBnetf
  \fBnetflow.\fIbridge\fB.engine-id\fR, respectively.  Each takes a value
  between 0 and 255, inclusive. 
  
 -Many NetFlow collectors do not expect multiple virtual switches to be
 +Many NetFlow collectors do not expect multiple switches to be
  sending messages from the same host, and they do not store the engine
  information which could be used to disambiguate the traffic.  To prevent
  flows from multiple switches appearing as if they came on the interface,
@@@ -412,7 -429,7 +428,7 @@@ switch will perform all configured brid
  .TP
  \fBdiscover\fR
  Use controller discovery to find the local OpenFlow controller.
 -Refer to \fBsecchan\fR(8) for information on how to configure a DHCP
 +Refer to \fB\ovs\-openflowd\fR(8) for information on how to configure a DHCP
  server to support controller discovery.  The following additional
  options control the discovery process:
  .
@@@ -425,8 -442,8 +441,8 @@@ the regular expression will be accepted
  .IP
  The default regular expression is \fBssl:.*\fR, meaning that only SSL
  controller connections will be accepted, when SSL is configured (see
 -\fBSSL Configuration\fR), and \fB.*\fR otherwise, meaning that any
 -controller will be accepted.
 +\fBSSL Configuration\fR), and \fBtcp:.*\fR otherwise, meaning that only
 +TCP controller connections will be accepted.
  .IP
  The regular expression is implicitly anchored at the beginning of the
  controller location string, as if it begins with \fB^\fR.
@@@ -471,7 -488,7 +487,7 @@@ not in use, the following additional se
  By default, or if this is set to \fBtrue\fR, \fBovs\-vswitchd\fR connects
  to the controller in-band.  If this is set to \fBfalse\fR,
  \fBovs\-vswitchd\fR connects to the controller out-of-band.  Refer to
 -\fBsecchan\fR(8) for a description of in-band and out-of-band control.
 +\fBovs\-openflowd\fR(8) for a description of in-band and out-of-band control.
  .IP "\fBbridge.\fIname\fB.controller.ip=\fIip\fR"
  If specified, the IP address to configure on the bridge's local port.
  .IP "\fBbridge.\fIname\fB.controller.netmask=\fInetmask\fR"
@@@ -489,11 -506,11 +505,11 @@@ This optional setting may be set to \fI
  The minimum value of \fIsecs\fR is 5 seconds.  The default is taken
  from \fBmgmt.inactivity-probe\fR (see above).
  .IP
 -When the virtual switch is connected to the controller, it waits for a
 +When the switch is connected to the controller, it waits for a
  message to be received from the controller for \fIsecs\fR seconds
  before it sends a inactivity probe to the controller.  After sending
  the inactivity probe, if no response is received for an additional
 -\fIsecs\fR seconds, the secure channel assumes that the connection has
 +\fIsecs\fR seconds, \fBovs-vswitchd\fR assumes that the connection has
  been broken and attempts to reconnect.
  .IP
  Changing the inactivity probe interval also changes the interval
@@@ -501,7 -518,7 +517,7 @@@ before entering standalone mode (see be
  .IP "\fBbridge.\fIname\fB.controller.fail-mode=\fBstandalone\fR|\fBsecure\fR"
  .IQ "\fBmgmt.fail-mode=standalone\fR|\fBsecure\fR"
  When a controller is configured, it is, ordinarily, responsible for
 -setting up all flows on the virtual switch.  Thus, if the connection to
 +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.
@@@ -526,7 -543,7 +542,7 @@@ connection attempts starts at 1 second 
  attempt until it reaches the maximum.  The default maximum backoff
  time is taken from \fBmgmt.max-backoff\fR.
  .ST "Controller Rate-Limiting"
 -These settings configure how the virtual switch applies a ``token
 +These settings configure how the switch applies a ``token
  bucket'' to limit the rate at which packets in unknown flows are
  forwarded to the OpenFlow controller for flow-setup processing.  This
  feature prevents a single bridge from overwhelming a controller.
@@@ -579,23 -596,24 +595,23 @@@ When \fBovs\-vswitchd\fR is configured 
  for controller connectivity, the following settings are required:
  .TP
  \fBssl.private-key=\fIprivkey.pem\fR
 -Specifies a PEM file containing the private key used as the virtual
 +Specifies a PEM file containing the private key used as the 
  switch's identity for SSL connections to the controller.
  .TP
  \fBssl.certificate=\fIcert.pem\fR
  Specifies a PEM file containing a certificate, signed by the
  certificate authority (CA) used by the controller and manager, that
 -certifies the virtual switch's private key, identifying a trustworthy
 +certifies the switch's private key, identifying a trustworthy
  switch.
  .TP
  \fBssl.ca-cert=\fIcacert.pem\fR
  Specifies a PEM file containing the CA certificate used to verify that
 -the virtual switch is connected to a trustworthy controller.
 +the switch is connected to a trustworthy controller.
  .PP
  These files are read only once, at \fBovs\-vswitchd\fR startup time.  If
  their contents change, \fBovs\-vswitchd\fR must be killed and restarted.
  .PP
 -These SSL settings apply to all SSL connections made by the virtual
 -switch.
 +These SSL settings apply to all SSL connections made by the switch.
  .ST "CA Certificate Bootstrap"
  Ordinarily, all of the files named in the SSL configuration must exist
  when \fBovs\-vswitchd\fR starts.  However, if \fBssl.bootstrap-ca-cert\fR
@@@ -633,11 -651,8 +649,11 @@@ Listens for connections on the Unix dom
  Listens for SSL connections on \fIport\fR (default: 6633).  SSL must
  be configured when this form is used (see \fBSSL Configuration\fR,
  above).
 -.IP "\fBptcp:\fR[\fIport\fR]"
 +.IP "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
  Listens for TCP connections on \fIport\fR (default: 6633).
 +By default, \fB\ovs\-vswitchd\fR listens for connections to any local
 +IP address, but \fIip\fR may be specified to limit connections to the
 +specified local \fIip\fR.
  .RE
  To entirely disable listening for management connections, set
  \fBbridge.\fIname\fB.openflow.listeners\fR to the single value
@@@ -51,8 -51,6 +51,6 @@@ install -m 755 xenserver/etc_init.d_vsw
  install -m 755 xenserver/etc_init.d_vswitch-xapi-update \
           $RPM_BUILD_ROOT/etc/init.d/vswitch-xapi-update
  install -d -m 755 $RPM_BUILD_ROOT/etc/sysconfig
- install -m 755 xenserver/etc_sysconfig_vswitch.example \
-          $RPM_BUILD_ROOT/etc/sysconfig/vswitch.example
  install -d -m 755 $RPM_BUILD_ROOT/etc/logrotate.d
  install -m 755 xenserver/etc_logrotate.d_vswitch \
           $RPM_BUILD_ROOT/etc/logrotate.d/vswitch
@@@ -73,6 -71,8 +71,8 @@@ install -m 755 xenserver/usr_sbin_xen-b
               $RPM_BUILD_ROOT%{_prefix}/scripts/xen-bugtool
  install -m 755 xenserver/usr_sbin_brctl \
               $RPM_BUILD_ROOT%{_prefix}/scripts/brctl
+ install -m 755 xenserver/root_vswitch_scripts_sysconfig.template \
+          $RPM_BUILD_ROOT/root/vswitch/scripts/sysconfig.template
  install -m 644 \
          xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \
                 $RPM_BUILD_ROOT%{_prefix}/scripts/XSFeatureVSwitch.py
@@@ -86,17 -86,16 +86,17 @@@ rm -rf 
      $RPM_BUILD_ROOT/root/vswitch/bin/ovs-controller \
      $RPM_BUILD_ROOT/root/vswitch/bin/ovs-discover \
      $RPM_BUILD_ROOT/root/vswitch/bin/ovs-kill \
 +    $RPM_BUILD_ROOT/root/vswitch/bin/ovs-openflowd \
      $RPM_BUILD_ROOT/root/vswitch/bin/ovs-pki \
      $RPM_BUILD_ROOT/root/vswitch/bin/ovs-switchui \
      $RPM_BUILD_ROOT/root/vswitch/bin/ovs-wdt \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/secchan \
 +    $RPM_BUILD_ROOT/root/vswitch/kernel_modules/veth_mod.ko \
      $RPM_BUILD_ROOT/root/vswitch/sbin/ovs-monitor \
      $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-controller.8 \
      $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-discover.8 \
      $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-kill.8 \
 +    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-openflowd.8 \
      $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-pki.8 \
 -    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/secchan.8 \
      $RPM_BUILD_ROOT/root/vswitch/share/openvswitch
  
  %clean
  # Ensure ovs-vswitchd.conf exists
  touch /etc/ovs-vswitchd.conf
  
+ # Create default or update existing /etc/sysconfig/vswitch.
+ SYSCONFIG=/etc/sysconfig/vswitch
+ TEMPLATE=/root/vswitch/scripts/sysconfig.template
+ if [ ! -e $SYSCONFIG ]; then
+     cp $TEMPLATE $SYSCONFIG
+ else
+     for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE)
+     do
+         if ! grep $var $SYSCONFIG >/dev/null 2>&1; then
+             echo >> $SYSCONFIG
+             sed -n "/$var:/,/$var=/p" $TEMPLATE >> $SYSCONFIG
+         fi
+     done
+ fi
  # Replace XenServer files by our versions.
  mkdir -p %{_prefix}/xs-original \
      || printf "Could not create script backup directory.\n"
  /etc/init.d/vswitch
  /etc/init.d/vswitch-xapi-update
  /etc/xapi.d/plugins/vswitch-cfg-update
- /etc/sysconfig/vswitch.example
  /etc/logrotate.d/vswitch
  /etc/profile.d/vswitch.sh
  /root/vswitch/kernel_modules/brcompat_mod.ko
  /root/vswitch/kernel_modules/openvswitch_mod.ko
 -/root/vswitch/kernel_modules/veth_mod.ko
  /root/vswitch/scripts/dump-vif-details
  /root/vswitch/scripts/interface-reconfigure
  /root/vswitch/scripts/vif
  /root/vswitch/scripts/xen-bugtool
  /root/vswitch/scripts/XSFeatureVSwitch.py
  /root/vswitch/scripts/brctl
+ /root/vswitch/scripts/sysconfig.template
  # Following two files are generated automatically by rpm.  We don't
  # really need them and they won't be used on the XenServer, but there
  # isn't an obvious place to get rid of them since they are generated