Merge branch "partner", to simplify partner integration.
authorBen Pfaff <blp@nicira.com>
Mon, 15 Sep 2008 22:31:36 +0000 (15:31 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 15 Sep 2008 22:31:36 +0000 (15:31 -0700)
25 files changed:
1  2 
controller/controller.c
include/Makefile.am
include/rconn.h
include/vconn-provider.h
include/vconn.h
lib/Makefile.am
lib/dpif.c
lib/learning-switch.c
lib/ofp-print.c
lib/rconn.c
lib/vconn-netlink.c
lib/vconn-ssl.c
lib/vconn-stream.c
lib/vconn-tcp.c
lib/vconn-unix.c
lib/vconn.c
secchan/secchan.c
switch/datapath.c
switch/datapath.h
switch/switch-flow.c
switch/switch.c
switch/table-hash.c
switch/table-linear.c
tests/test-stp.c
utilities/dpctl.c

@@@ -111,25 -112,22 +112,25 @@@ main(int argc, char *argv[]
          struct vconn *vconn;
          int retval;
  
 -        retval = vconn_open(name, &vconn);
 -        if (retval) {
 -            VLOG_ERR("%s: connect: %s", name, strerror(retval));
 -            continue;
 -        }
 -
 -        if (vconn_is_passive(vconn)) {
 -            if (n_listeners >= MAX_LISTENERS) {
 -                ofp_fatal(0, "max %d passive connections", n_listeners);
 -            }
 -            listeners[n_listeners++] = vconn;
 -        } else {
 +        retval = vconn_open(name, OFP_VERSION, &vconn);
 +        if (!retval) {
              if (n_switches >= MAX_SWITCHES) {
-                 fatal(0, "max %d switch connections", n_switches);
+                 ofp_fatal(0, "max %d switch connections", n_switches);
              }
              new_switch(&switches[n_switches++], vconn, name);
-                     fatal(0, "max %d passive connections", n_listeners);
 +            continue;
 +        } else if (retval == EAFNOSUPPORT) {
 +            struct pvconn *pvconn;
 +            retval = pvconn_open(name, &pvconn);
 +            if (!retval) {
 +                if (n_listeners >= MAX_LISTENERS) {
++                    ofp_fatal(0, "max %d passive connections", n_listeners);
 +                }
 +                listeners[n_listeners++] = pvconn;
 +            }
 +        }
 +        if (retval) {
 +            VLOG_ERR("%s: connect: %s", name, strerror(retval));
          }
      }
      if (n_switches == 0 && n_listeners == 0) {
Simple merge
diff --cc include/rconn.h
Simple merge
@@@ -94,96 -81,49 +94,96 @@@ struct vconn_class 
       * complete; instead, it should return EAGAIN immediately. */
      int (*connect)(struct vconn *vconn);
  
 -    /* Tries to accept a new connection on 'vconn', which must be a passive
 -     * vconn.  If successful, stores the new connection in '*new_vconnp' and
 -     * returns 0.  Otherwise, returns a positive errno value.
 -     *
 -     * The accept function must not block waiting for a connection.  If no
 -     * connection is ready to be accepted, it should return EAGAIN.
 -     *
 -     * Nonnull iff this is a passive vconn (one that accepts connections and
 -     * does not transfer data). */
 -    int (*accept)(struct vconn *vconn, struct vconn **new_vconnp);
 -
 -    /* Tries to receive an OpenFlow message from 'vconn', which must be an
 -     * active vconn.  If successful, stores the received message into '*msgp'
 -     * and returns 0.  The caller is responsible for destroying the message
 -     * with ofpbuf_delete().  On failure, returns a positive errno value and
 -     * stores a null pointer into '*msgp'.
 +    /* Tries to receive an OpenFlow message from 'vconn'.  If successful,
 +     * stores the received message into '*msgp' and returns 0.  The caller is
-      * responsible for destroying the message with buffer_delete().  On
++     * responsible for destroying the message with ofpbuf_delete().  On
 +     * failure, returns a positive errno value and stores a null pointer into
 +     * '*msgp'.
       *
       * If the connection has been closed in the normal fashion, returns EOF.
       *
       * The recv function must not block waiting for a packet to arrive.  If no
 -     * packets have been received, it should return EAGAIN.
 -     *
 -     * Nonnull iff this is an active vconn (one that transfers data and does
 -     * not accept connections). */
 +     * packets have been received, it should return EAGAIN. */
-     int (*recv)(struct vconn *vconn, struct buffer **msgp);
+     int (*recv)(struct vconn *vconn, struct ofpbuf **msgp);
  
 -    /* Tries to queue 'msg' for transmission on 'vconn', which must be an
 -     * active vconn.  If successful, returns 0, in which case ownership of
 -     * 'msg' is transferred to the vconn.  Success does not guarantee that
 -     * 'msg' has been or ever will be delivered to the peer, only that it has
 -     * been queued for transmission.
 +    /* Tries to queue 'msg' for transmission on 'vconn'.  If successful,
 +     * returns 0, in which case ownership of 'msg' is transferred to the vconn.
 +     * Success does not guarantee that 'msg' has been or ever will be delivered
 +     * to the peer, only that it has been queued for transmission.
       *
       * Returns a positive errno value on failure, in which case the caller
       * retains ownership of 'msg'.
       *
       * The send function must not block.  If 'msg' cannot be immediately
 -     * accepted for transmission, it should return EAGAIN.
 -     *
 -     * Nonnull iff this is an active vconn (one that transfers data and does
 -     * not accept connections). */
 +     * accepted for transmission, it should return EAGAIN. */
-     int (*send)(struct vconn *vconn, struct buffer *msg);
+     int (*send)(struct vconn *vconn, struct ofpbuf *msg);
  
 -    void (*wait)(struct vconn *vconn, enum vconn_wait_type);
 +    /* Arranges for the poll loop to wake up when 'vconn' is ready to take an
 +     * action of the given 'type'. */
 +    void (*wait)(struct vconn *vconn, enum vconn_wait_type type);
 +};
 +\f
 +/* Passive virtual connection to an OpenFlow device.
 + *
 + * This structure should be treated as opaque by vconn implementations. */
 +struct pvconn {
 +    struct pvconn_class *class;
 +    char *name;
  };
  
 +void pvconn_init(struct pvconn *, struct pvconn_class *, const char *name);
 +static inline void pvconn_assert_class(const struct pvconn *pvconn,
 +                                       const struct pvconn_class *class)
 +{
 +    assert(pvconn->class == class);
 +}
 +
 +struct pvconn_class {
 +    /* Prefix for connection names, e.g. "ptcp", "pssl". */
 +    const char *name;
 +
 +    /* Attempts to start listening for OpenFlow connections.  'name' is the
 +     * full connection name provided by the user, e.g. "nl:0", "tcp:1.2.3.4".
 +     * This name is useful for error messages but must not be modified.
 +     *
 +     * 'suffix' is a copy of 'name' following the colon and may be modified.
 +     *
 +     * Returns 0 if successful, otherwise a positive errno value.  If
 +     * successful, stores a pointer to the new connection in '*pvconnp'.
 +     *
 +     * The listen function must not block.  If the connection cannot be
 +     * completed immediately, it should return EAGAIN (not EINPROGRESS, as
 +     * returned by the connect system call) and continue the connection in the
 +     * background. */
 +    int (*listen)(const char *name, char *suffix, struct pvconn **pvconnp);
 +
 +    /* Closes 'pvconn' and frees associated memory. */
 +    void (*close)(struct pvconn *pvconn);
 +
 +    /* Tries to accept a new connection on 'pvconn'.  If successful, stores the
 +     * new connection in '*new_vconnp' and returns 0.  Otherwise, returns a
 +     * positive errno value.
 +     *
 +     * The accept function must not block waiting for a connection.  If no
 +     * connection is ready to be accepted, it should return EAGAIN. */
 +    int (*accept)(struct pvconn *pvconn, struct vconn **new_vconnp);
 +
 +    /* Arranges for the poll loop to wake up when a connection is ready to be
 +     * accepted on 'pvconn'. */
 +    void (*wait)(struct pvconn *pvconn);
 +};
 +
 +/* Active and passive vconn classes. */
 +extern struct vconn_class tcp_vconn_class;
 +extern struct pvconn_class ptcp_pvconn_class;
 +extern struct vconn_class unix_vconn_class;
 +extern struct pvconn_class punix_pvconn_class;
 +#ifdef HAVE_OPENSSL
 +extern struct vconn_class ssl_vconn_class;
 +extern struct pvconn_class pssl_pvconn_class;
 +#endif
 +#ifdef HAVE_NETLINK
 +extern struct vconn_class netlink_vconn_class;
 +#endif
 +
  #endif /* vconn-provider.h */
diff --cc include/vconn.h
  #include <stddef.h>
  #include <stdint.h>
  
- struct buffer;
+ struct ofpbuf;
  struct flow;
 -struct pollfd;
  struct ofp_header;
 +struct pvconn;
  struct vconn;
  
 -/* Client interface to vconns, which provide a virtual connection to an
 - * OpenFlow device. */
 -
  void vconn_usage(bool active, bool passive);
 -int vconn_open(const char *name, struct vconn **);
 +
 +/* Active vconns: virtual connections to OpenFlow devices. */
 +int vconn_open(const char *name, int min_version, struct vconn **);
  void vconn_close(struct vconn *);
 -bool vconn_is_passive(const struct vconn *);
 +const char *vconn_get_name(const struct vconn *);
  uint32_t vconn_get_ip(const struct vconn *);
  int vconn_connect(struct vconn *);
- int vconn_recv(struct vconn *, struct buffer **);
- int vconn_send(struct vconn *, struct buffer *);
- int vconn_transact(struct vconn *, struct buffer *, struct buffer **);
 -int vconn_accept(struct vconn *, struct vconn **);
+ int vconn_recv(struct vconn *, struct ofpbuf **);
+ int vconn_send(struct vconn *, struct ofpbuf *);
+ int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **);
  
 -int vconn_open_block(const char *name, struct vconn **);
 +int vconn_open_block(const char *name, int min_version, struct vconn **);
- int vconn_send_block(struct vconn *, struct buffer *);
- int vconn_recv_block(struct vconn *, struct buffer **);
+ int vconn_send_block(struct vconn *, struct ofpbuf *);
+ int vconn_recv_block(struct vconn *, struct ofpbuf **);
  
  enum vconn_wait_type {
      WAIT_CONNECT,
@@@ -70,27 -73,33 +70,27 @@@ void vconn_connect_wait(struct vconn *)
  void vconn_recv_wait(struct vconn *);
  void vconn_send_wait(struct vconn *);
  
- void *make_openflow(size_t openflow_len, uint8_t type, struct buffer **);
 +/* Passive vconns: virtual listeners for incoming OpenFlow connections. */
 +int pvconn_open(const char *name, struct pvconn **);
 +void pvconn_close(struct pvconn *);
 +int pvconn_accept(struct pvconn *, int min_version, struct vconn **);
 +void pvconn_wait(struct pvconn *);
 +
 +/* OpenFlow protocol utility functions. */
+ void *make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **);
  void *make_openflow_xid(size_t openflow_len, uint8_t type,
-                         uint32_t xid, struct buffer **);
- void update_openflow_length(struct buffer *);
- struct buffer *make_add_flow(const struct flow *, uint32_t buffer_id,
+                         uint32_t xid, struct ofpbuf **);
+ void update_openflow_length(struct ofpbuf *);
+ struct ofpbuf *make_add_flow(const struct flow *, uint32_t buffer_id,
                               uint16_t max_idle, size_t n_actions);
- struct buffer *make_add_simple_flow(const struct flow *,
+ struct ofpbuf *make_add_simple_flow(const struct flow *,
                                      uint32_t buffer_id, uint16_t out_port,
                                      uint16_t max_idle);
- struct buffer *make_buffered_packet_out(uint32_t buffer_id,
+ struct ofpbuf *make_buffered_packet_out(uint32_t buffer_id,
                                          uint16_t in_port, uint16_t out_port);
- struct buffer *make_unbuffered_packet_out(const struct buffer *packet,
+ struct ofpbuf *make_unbuffered_packet_out(const struct ofpbuf *packet,
                                            uint16_t in_port, uint16_t out_port);
- struct buffer *make_echo_request(void);
- struct buffer *make_echo_reply(const struct ofp_header *rq);
+ struct ofpbuf *make_echo_request(void);
+ struct ofpbuf *make_echo_reply(const struct ofp_header *rq);
  
 -extern struct vconn_class tcp_vconn_class;
 -extern struct vconn_class ptcp_vconn_class;
 -extern struct vconn_class unix_vconn_class;
 -extern struct vconn_class punix_vconn_class;
 -#ifdef HAVE_OPENSSL
 -extern struct vconn_class ssl_vconn_class;
 -extern struct vconn_class pssl_vconn_class;
 -#endif
 -#ifdef HAVE_NETLINK
 -extern struct vconn_class netlink_vconn_class;
 -#endif
 -
  #endif /* vconn.h */
diff --cc lib/Makefile.am
Simple merge
diff --cc lib/dpif.c
Simple merge
@@@ -73,10 -72,8 +73,10 @@@ struct lswitch 
   * rate limit relatively high. */
  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
  
- static void queue_tx(struct lswitch *, struct rconn *, struct buffer *);
+ static void queue_tx(struct lswitch *, struct rconn *, struct ofpbuf *);
  static void send_features_request(struct lswitch *, struct rconn *);
 +static void process_switch_features(struct lswitch *, struct rconn *,
 +                                    struct ofp_switch_features *);
  static void process_packet_in(struct lswitch *, struct rconn *,
                                struct ofp_packet_in *);
  static void process_echo_request(struct lswitch *, struct rconn *,
@@@ -118,6 -111,14 +118,15 @@@ lswitch_destroy(struct lswitch *sw
      }
  }
  
+ static size_t
+ min_size(uint8_t type)
+ {
+     return (type == OFPT_FEATURES_REPLY ? sizeof(struct ofp_switch_features)
+             : type == OFPT_PACKET_IN ? offsetof (struct ofp_packet_in, data)
++            : type == OFPT_PORT_STATUS ? sizeof(struct ofp_port_status)
+             : sizeof(struct ofp_header));
+ }
  /* Processes 'msg', which should be an OpenFlow received on 'rconn', according
   * to the learning switch state in 'sw'.  The most likely result of processing
   * is that flow-setup and packet-out OpenFlow messages will be sent out on
@@@ -167,7 -161,8 +170,7 @@@ send_features_request(struct lswitch *s
  {
      time_t now = time_now();
      if (now >= sw->last_features_request + 1) {
-         struct buffer *b;
+         struct ofpbuf *b;
 -        struct ofp_header *ofr;
          struct ofp_switch_config *osc;
  
          /* Send OFPT_FEATURES_REQUEST. */
@@@ -276,62 -265,3 +279,62 @@@ process_echo_request(struct lswitch *sw
  {
      queue_tx(sw, rconn, make_echo_reply(rq));
  }
-             struct buffer *b;
 +
 +static void
 +process_port_status(struct lswitch *sw, struct rconn *rconn,
 +                    struct ofp_port_status *ops)
 +{
 +    process_phy_port(sw, rconn, &ops->desc);
 +}
 +
 +static void
 +process_phy_port(struct lswitch *sw, struct rconn *rconn,
 +                 const struct ofp_phy_port *opp)
 +{
 +    if (sw->capabilities & OFPC_STP && opp->features & ntohl(OFPPF_STP)) {
 +        uint32_t flags = ntohl(opp->flags);
 +        uint32_t new_flags = flags & ~(OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP
 +                                       | OFPPFL_NO_FWD | OFPPFL_NO_PACKET_IN);
 +        if (!(flags & (OFPPFL_NO_STP | OFPPFL_PORT_DOWN | OFPPFL_LINK_DOWN))) {
 +            bool forward = false;
 +            bool learn = false;
 +            switch (flags & OFPPFL_STP_MASK) {
 +            case OFPPFL_STP_LISTEN:
 +            case OFPPFL_STP_BLOCK:
 +                break;
 +            case OFPPFL_STP_LEARN:
 +                learn = true;
 +                break;
 +            case OFPPFL_STP_FORWARD:
 +                forward = learn = true;
 +                break;
 +            }
 +            if (!forward) {
 +                new_flags |= OFPPFL_NO_RECV | OFPPFL_NO_FWD;
 +            }
 +            if (!learn) {
 +                new_flags |= OFPPFL_NO_PACKET_IN;
 +            }
 +        }
 +        if (flags != new_flags) {
 +            struct ofp_port_mod *opm;
-                 buffer_delete(b);
++            struct ofpbuf *b;
 +            int retval;
 +
 +            VLOG_WARN("port %d: flags=%x new_flags=%x",
 +                      ntohs(opp->port_no), flags, new_flags);
 +            opm = make_openflow(sizeof *opm, OFPT_PORT_MOD, &b);
 +            opm->mask = htonl(flags ^ new_flags);
 +            opm->desc = *opp;
 +            opm->desc.flags = htonl(new_flags);
 +            retval = rconn_send(rconn, b, NULL);
 +            if (retval) {
 +                if (retval != ENOTCONN) {
 +                    VLOG_WARN_RL(&rl, "%s: send: %s",
 +                                 rconn_get_name(rconn), strerror(retval));
 +                }
++                ofpbuf_delete(b);
 +            }
 +        }
 +    }
 +}
diff --cc lib/ofp-print.c
@@@ -940,11 -890,18 +946,18 @@@ print_stats(struct ds *string, int type
              { 0, 0, NULL, },
              { 0, SIZE_MAX, ofp_port_stats_reply },
          },
-         [OFPST_VENDOR] = {
+         {
 -            OFPST_SWITCH,
 -            "switch status",
 -            { 0, 0, NULL, },
 -            { 0, SIZE_MAX, switch_status_reply },
++            OFPST_VENDOR,
 +            "vendor-specific",
 +            { sizeof(uint32_t), SIZE_MAX, vendor_stat },
 +            { sizeof(uint32_t), SIZE_MAX, vendor_stat },
          },
+         {
+             -1,
+             "unknown",
+             { 0, 0, NULL, },
+             { 0, 0, NULL, },
+         },
      };
  
      const struct stats_type *s;
@@@ -1025,12 -987,8 +1043,14 @@@ struct openflow_packet 
  };
  
  static const struct openflow_packet packets[] = {
-     [OFPT_HELLO] = {
++    {
++        OFPT_HELLO,
 +        "hello",
 +        sizeof (struct ofp_header),
 +        NULL,
 +    },
-     [OFPT_FEATURES_REQUEST] = {
+     {
+         OFPT_FEATURES_REQUEST,
          "features_request",
          sizeof (struct ofp_header),
          NULL,
          sizeof (struct ofp_port_status),
          ofp_print_port_status
      },
-     [OFPT_ERROR] = {
+     {
 -        OFPT_ERROR_MSG,
++        OFPT_ERROR,
          "error_msg",
          sizeof (struct ofp_error_msg),
          ofp_print_error_msg,
diff --cc lib/rconn.c
  #include <limits.h>
  #include <stdlib.h>
  #include <string.h>
- #include "buffer.h"
+ #include "ofpbuf.h"
 +#include "openflow.h"
  #include "poll-loop.h"
- #include "ofp-print.h"
  #include "sat-math.h"
  #include "timeval.h"
  #include "util.h"
@@@ -131,7 -124,6 +130,7 @@@ static int reconnect(struct rconn *)
  static void disconnect(struct rconn *, int error);
  static void flush_queue(struct rconn *);
  static void question_connectivity(struct rconn *);
- static void copy_to_monitor(struct rconn *, const struct buffer *);
++static void copy_to_monitor(struct rconn *, const struct ofpbuf *);
  
  /* Creates a new rconn, connects it (reliably) to 'name', and returns it. */
  struct rconn *
@@@ -430,10 -425,9 +429,10 @@@ struct ofpbuf 
  rconn_recv(struct rconn *rc)
  {
      if (rc->state & (S_ACTIVE | S_IDLE)) {
-         struct buffer *buffer;
+         struct ofpbuf *buffer;
          int error = vconn_recv(rc->vconn, &buffer);
          if (!error) {
 +            copy_to_monitor(rc, buffer);
              rc->last_received = time_now();
              rc->packets_received++;
              if (rc->state == S_IDLE) {
@@@ -471,10 -465,9 +470,10 @@@ rconn_recv_wait(struct rconn *rc
   * takes care of sending if you call rconn_run(), which will have the side
   * effect of waking up poll_block(). */
  int
- rconn_send(struct rconn *rc, struct buffer *b, int *n_queued)
+ rconn_send(struct rconn *rc, struct ofpbuf *b, int *n_queued)
  {
      if (rconn_is_connected(rc)) {
 +        copy_to_monitor(rc, b);
          b->private = n_queued;
          if (n_queued) {
              ++*n_queued;
@@@ -788,31 -766,3 +787,31 @@@ question_connectivity(struct rconn *rc
          rc->last_questioned = now;
      }
  }
- copy_to_monitor(struct rconn *rc, const struct buffer *b)
 +
 +static void
-     struct buffer *clone = NULL;
++copy_to_monitor(struct rconn *rc, const struct ofpbuf *b)
 +{
-             clone = buffer_clone(b);
++    struct ofpbuf *clone = NULL;
 +    int retval;
 +    size_t i;
 +
 +    for (i = 0; i < rc->n_monitors; ) {
 +        struct vconn *vconn = rc->monitors[i];
 +
 +        if (!clone) {
-     buffer_delete(clone);
++            clone = ofpbuf_clone(b);
 +        }
 +        retval = vconn_send(vconn, clone);
 +        if (!retval) {
 +            clone = NULL;
 +        } else if (retval != EAGAIN) {
 +            VLOG_DBG("%s: closing monitor connection to %s: %s",
 +                     rconn_get_name(rc), vconn_get_name(vconn),
 +                     strerror(retval));
 +            rc->monitors[i] = rc->monitors[--rc->n_monitors];
 +            continue;
 +        }
 +        i++;
 +    }
++    ofpbuf_delete(clone);
 +}
@@@ -142,10 -142,12 +142,11 @@@ netlink_wait(struct vconn *vconn, enum 
  }
  
  struct vconn_class netlink_vconn_class = {
-     .name = "nl",
-     .open = netlink_open,
-     .close = netlink_close,
-     .recv = netlink_recv,
-     .send = netlink_send,
-     .wait = netlink_wait,
+     "nl",                       /* name */
+     netlink_open,               /* open */
+     netlink_close,              /* close */
+     NULL,                       /* connect */
 -    NULL,                       /* accept */
+     netlink_recv,               /* recv */
+     netlink_send,               /* send */
+     netlink_wait,               /* wait */
  };
diff --cc lib/vconn-ssl.c
@@@ -652,13 -651,14 +651,13 @@@ ssl_wait(struct vconn *vconn, enum vcon
  }
  
  struct vconn_class ssl_vconn_class = {
-     .name = "ssl",
-     .open = ssl_open,
-     .close = ssl_close,
-     .connect = ssl_connect,
-     .recv = ssl_recv,
-     .send = ssl_send,
-     .wait = ssl_wait,
+     "ssl",                      /* name */
+     ssl_open,                   /* open */
+     ssl_close,                  /* close */
+     ssl_connect,                /* connect */
 -    NULL,                       /* accept */
+     ssl_recv,                   /* recv */
+     ssl_send,                   /* send */
+     ssl_wait,                   /* wait */
  };
  \f
  /* Passive SSL. */
@@@ -246,12 -246,14 +246,13 @@@ stream_wait(struct vconn *vconn, enum v
  }
  
  static struct vconn_class stream_vconn_class = {
-     .name = "stream",
-     .close = stream_close,
-     .connect = stream_connect,
-     .recv = stream_recv,
-     .send = stream_send,
-     .wait = stream_wait,
+     "stream",                   /* name */
+     NULL,                       /* open */
+     stream_close,               /* close */
+     stream_connect,             /* connect */
 -    NULL,                       /* accept */
+     stream_recv,                /* recv */
+     stream_send,                /* send */
+     stream_wait,                /* wait */
  };
  \f
  /* Passive stream socket vconn. */
diff --cc lib/vconn-tcp.c
@@@ -127,8 -127,14 +127,13 @@@ tcp_open(const char *name, char *suffix
  }
  
  struct vconn_class tcp_vconn_class = {
-     .name = "tcp",
-     .open = tcp_open,
+     "tcp",                      /* name */
+     tcp_open,                   /* open */
+     NULL,                       /* close */
+     NULL,                       /* connect */
 -    NULL,                       /* accept */
+     NULL,                       /* recv */
+     NULL,                       /* send */
+     NULL,                       /* wait */
  };
  \f
  /* Passive TCP. */
@@@ -82,8 -81,14 +81,13 @@@ unix_open(const char *name, char *suffi
  }
  
  struct vconn_class unix_vconn_class = {
-     .name = "unix",
-     .open = unix_open,
+     "unix",                     /* name */
+     unix_open,                  /* open */
+     NULL,                       /* close */
+     NULL,                       /* connect */
 -    NULL,                       /* accept */
+     NULL,                       /* recv */
+     NULL,                       /* send */
+     NULL,                       /* wait */
  };
  \f
  /* Passive UNIX socket. */
diff --cc lib/vconn.c
  #include <poll.h>
  #include <stdlib.h>
  #include <string.h>
- #include "buffer.h"
 +#include "dynamic-string.h"
  #include "flow.h"
  #include "ofp-print.h"
+ #include "ofpbuf.h"
  #include "openflow.h"
  #include "poll-loop.h"
  #include "random.h"
@@@ -89,9 -70,6 +89,9 @@@ static struct pvconn_class *pvconn_clas
   * really need to see them. */
  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(600, 600);
  
- static int do_recv(struct vconn *, struct buffer **);
- static int do_send(struct vconn *, struct buffer *);
++static int do_recv(struct vconn *, struct ofpbuf **);
++static int do_send(struct vconn *, struct ofpbuf *);
 +
  /* Check the validity of the vconn class structures. */
  static void
  check_vconn_classes(void)
@@@ -267,157 -235,40 +267,157 @@@ vconn_get_ip(const struct vconn *vconn
      return vconn->ip;
  }
  
 -/* Tries to complete the connection on 'vconn', which must be an active
 - * vconn.  If 'vconn''s connection is complete, returns 0 if the connection
 - * was successful or a positive errno value if it failed.  If the
 - * connection is still in progress, returns EAGAIN. */
 -int
 -vconn_connect(struct vconn *vconn)
 +static void
 +vcs_connecting(struct vconn *vconn) 
  {
 -    if (vconn->connect_status == EAGAIN) {
 -        vconn->connect_status = (vconn->class->connect)(vconn);
 -        assert(vconn->connect_status != EINPROGRESS);
 +    int retval = (vconn->class->connect)(vconn);
 +    assert(retval != EINPROGRESS);
 +    if (!retval) {
 +        vconn->state = VCS_SEND_HELLO;
 +    } else if (retval != EAGAIN) {
 +        vconn->state = VCS_DISCONNECTED;
 +        vconn->error = retval;
      }
 -    return vconn->connect_status;
  }
  
 -/* Tries to accept a new connection on 'vconn', which must be a passive vconn.
 - * If successful, stores the new connection in '*new_vconn' and returns 0.
 - * Otherwise, returns a positive errno value.
 - *
 - * vconn_accept will not block waiting for a connection.  If no connection is
 - * ready to be accepted, it returns EAGAIN immediately. */
 -int
 -vconn_accept(struct vconn *vconn, struct vconn **new_vconn)
 +static void
 +vcs_send_hello(struct vconn *vconn)
  {
-     struct buffer *b;
++    struct ofpbuf *b;
      int retval;
  
 -    retval = (vconn->class->accept)(vconn, new_vconn);
 +    make_openflow(sizeof(struct ofp_header), OFPT_HELLO, &b);
 +    retval = do_send(vconn, b);
 +    if (!retval) {
 +        vconn->state = VCS_RECV_HELLO;
 +    } else {
-         buffer_delete(b);
++        ofpbuf_delete(b);
 +        if (retval != EAGAIN) {
 +            vconn->state = VCS_DISCONNECTED;
 +            vconn->error = retval;
 +        }
 +    }
 +}
 +
 +static void
 +vcs_recv_hello(struct vconn *vconn)
 +{
-     struct buffer *b;
++    struct ofpbuf *b;
 +    int retval;
  
-             buffer_delete(b);
 +    retval = do_recv(vconn, &b);
 +    if (!retval) {
 +        struct ofp_header *oh = b->data;
 +
 +        if (oh->type == OFPT_HELLO) {
 +            if (b->size > sizeof *oh) {
 +                struct ds msg = DS_EMPTY_INITIALIZER;
 +                ds_put_format(&msg, "%s: extra-long hello:\n", vconn->name);
 +                ds_put_hex_dump(&msg, b->data, b->size, 0, true);
 +                VLOG_WARN_RL(&rl, ds_cstr(&msg));
 +                ds_destroy(&msg);
 +            }
 +
 +            vconn->version = MIN(OFP_VERSION, oh->version);
 +            if (vconn->version < vconn->min_version) {
 +                VLOG_WARN_RL(&rl, "%s: version negotiation failed: we support "
 +                             "versions 0x%02x to 0x%02x inclusive but peer "
 +                             "supports no later than version 0x%02"PRIx8,
 +                             vconn->name, vconn->min_version, OFP_VERSION,
 +                             oh->version);
 +                vconn->state = VCS_SEND_ERROR;
 +            } else {
 +                VLOG_DBG("%s: negotiated OpenFlow version 0x%02x "
 +                         "(we support versions 0x%02x to 0x%02x inclusive, "
 +                         "peer no later than version 0x%02"PRIx8")",
 +                         vconn->name, vconn->version, vconn->min_version,
 +                         OFP_VERSION, oh->version);
 +                vconn->state = VCS_CONNECTED;
 +            }
-             buffer_delete(b);
++            ofpbuf_delete(b);
 +            return;
 +        } else {
 +            char *s = ofp_to_string(b->data, b->size, 1);
 +            VLOG_WARN_RL(&rl, "%s: received message while expecting hello: %s",
 +                         vconn->name, s);
 +            free(s);
 +            retval = EPROTO;
-     struct buffer *b;
++            ofpbuf_delete(b);
 +        }
 +    }
 +
 +    if (retval != EAGAIN) {
 +        vconn->state = VCS_DISCONNECTED;
 +        vconn->error = retval;
 +    }
 +}
 +
 +static void
 +vcs_send_error(struct vconn *vconn)
 +{
 +    struct ofp_error_msg *error;
-     buffer_put(b, s, strlen(s));
++    struct ofpbuf *b;
 +    char s[128];
 +    int retval;
 +
 +    snprintf(s, sizeof s, "We support versions 0x%02x to 0x%02x inclusive but "
 +             "you support no later than version 0x%02"PRIx8".",
 +             vconn->min_version, OFP_VERSION, vconn->version);
 +    error = make_openflow(sizeof *error, OFPT_ERROR, &b);
 +    error->type = htons(OFPET_HELLO_FAILED);
 +    error->code = htons(OFPHFC_INCOMPATIBLE);
++    ofpbuf_put(b, s, strlen(s));
 +    retval = do_send(vconn, b);
      if (retval) {
-         buffer_delete(b);
 -        *new_vconn = NULL;
 -    } else {
 -        assert((*new_vconn)->connect_status != EAGAIN
 -               || (*new_vconn)->class->connect);
++        ofpbuf_delete(b);
 +    }
 +    if (retval != EAGAIN) {
 +        vconn->state = VCS_DISCONNECTED;
 +        vconn->error = retval ? retval : EPROTO;
      }
 -    return retval;
 +}
 +
 +/* Tries to complete the connection on 'vconn', which must be an active
 + * vconn.  If 'vconn''s connection is complete, returns 0 if the connection
 + * was successful or a positive errno value if it failed.  If the
 + * connection is still in progress, returns EAGAIN. */
 +int
 +vconn_connect(struct vconn *vconn)
 +{
 +    enum vconn_state last_state;
 +
 +    assert(vconn->min_version >= 0);
 +    do {
 +        last_state = vconn->state;
 +        switch (vconn->state) {
 +        case VCS_CONNECTING:
 +            vcs_connecting(vconn);
 +            break;
 +
 +        case VCS_SEND_HELLO:
 +            vcs_send_hello(vconn);
 +            break;
 +
 +        case VCS_RECV_HELLO:
 +            vcs_recv_hello(vconn);
 +            break;
 +
 +        case VCS_CONNECTED:
 +            return 0;
 +
 +        case VCS_SEND_ERROR:
 +            vcs_send_error(vconn);
 +            break;
 +
 +        case VCS_DISCONNECTED:
 +            return vconn->error;
 +
 +        default:
 +            NOT_REACHED();
 +        }
 +    } while (vconn->state != last_state);
 +
 +    return EAGAIN;
  }
  
  /* Tries to receive an OpenFlow message from 'vconn', which must be an active
@@@ -433,45 -284,25 +433,45 @@@ vconn_recv(struct vconn *vconn, struct 
  {
      int retval = vconn_connect(vconn);
      if (!retval) {
 -        retval = (vconn->class->recv)(vconn, msgp);
 -        if (!retval) {
 -            struct ofp_header *oh;
 -
 -            if (VLOG_IS_DBG_ENABLED()) {
 -                char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
 -                VLOG_DBG_RL(&rl, "%s: received: %s", vconn->name, s);
 -                free(s);
 -            }
 +        retval = do_recv(vconn, msgp);
 +    }
 +    return retval;
 +}
  
 -            oh = ofpbuf_at_assert(*msgp, 0, sizeof *oh);
 -            if (oh->version != OFP_VERSION) {
 +static int
- do_recv(struct vconn *vconn, struct buffer **msgp)
++do_recv(struct vconn *vconn, struct ofpbuf **msgp)
 +{
 +    int retval;
 +
 +    retval = (vconn->class->recv)(vconn, msgp);
 +    if (!retval) {
 +        struct ofp_header *oh;
 +
 +        if (VLOG_IS_DBG_ENABLED()) {
 +            char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
 +            VLOG_DBG_RL(&rl, "%s: received: %s", vconn->name, s);
 +            free(s);
 +        }
 +
-         oh = buffer_at_assert(*msgp, 0, sizeof *oh);
++        oh = ofpbuf_at_assert(*msgp, 0, sizeof *oh);
 +        if (oh->version != vconn->version
 +            && oh->type != OFPT_HELLO
 +            && oh->type != OFPT_ERROR
 +            && oh->type != OFPT_ECHO_REQUEST
 +            && oh->type != OFPT_ECHO_REPLY
 +            && oh->type != OFPT_VENDOR)
 +        {
 +            if (vconn->version < 0) {
 +                VLOG_ERR_RL(&rl, "%s: received OpenFlow version %02"PRIx8" "
 +                            "before version negotiation complete",
 +                            vconn->name, oh->version);
 +            } else {
                  VLOG_ERR_RL(&rl, "%s: received OpenFlow version %02"PRIx8" "
                              "!= expected %02x",
 -                            vconn->name, oh->version, OFP_VERSION);
 -                ofpbuf_delete(*msgp);
 -                *msgp = NULL;
 -                return EPROTO;
 +                            vconn->name, oh->version, vconn->version);
              }
-             buffer_delete(*msgp);
++            ofpbuf_delete(*msgp);
 +            retval = EPROTO;
          }
      }
      if (retval) {
@@@ -496,28 -327,18 +496,28 @@@ vconn_send(struct vconn *vconn, struct 
  {
      int retval = vconn_connect(vconn);
      if (!retval) {
 -        assert(msg->size >= sizeof(struct ofp_header));
 -        assert(((struct ofp_header *) msg->data)->length == htons(msg->size));
 -        if (!VLOG_IS_DBG_ENABLED()) { 
 -            retval = (vconn->class->send)(vconn, msg);
 -        } else {
 -            char *s = ofp_to_string(msg->data, msg->size, 1);
 -            retval = (vconn->class->send)(vconn, msg);
 -            if (retval != EAGAIN) {
 -                VLOG_DBG_RL(&rl, "%s: sent (%s): %s", vconn->name, strerror(retval), s);
 -            }
 -            free(s);
 +        retval = do_send(vconn, msg);
 +    }
 +    return retval;
 +}
 +
 +static int
- do_send(struct vconn *vconn, struct buffer *msg)
++do_send(struct vconn *vconn, struct ofpbuf *msg)
 +{
 +    int retval;
 +
 +    assert(msg->size >= sizeof(struct ofp_header));
 +    assert(((struct ofp_header *) msg->data)->length == htons(msg->size));
 +    if (!VLOG_IS_DBG_ENABLED()) {
 +        retval = (vconn->class->send)(vconn, msg);
 +    } else {
 +        char *s = ofp_to_string(msg->data, msg->size, 1);
 +        retval = (vconn->class->send)(vconn, msg);
 +        if (retval != EAGAIN) {
 +            VLOG_DBG_RL(&rl, "%s: sent (%s): %s",
 +                        vconn->name, strerror(retval), s);
          }
 +        free(s);
      }
      return retval;
  }
@@@ -800,9 -543,9 +800,9 @@@ make_unbuffered_packet_out(const struc
  {
      struct ofp_packet_out *opo;
      size_t size = sizeof *opo + sizeof opo->actions[0];
-     struct buffer *out = buffer_new(size + packet->size);
-     opo = buffer_put_uninit(out, size);
+     struct ofpbuf *out = ofpbuf_new(size + packet->size);
+     opo = ofpbuf_put_uninit(out, size);
 -    memset(opo, 0, sizeof *opo);
 +    memset(opo, 0, size);
      opo->header.version = OFP_VERSION;
      opo->header.type = OFPT_PACKET_OUT;
      opo->buffer_id = htonl(UINT32_MAX);
@@@ -58,7 -57,7 +57,8 @@@
  #include "list.h"
  #include "mac-learning.h"
  #include "netdev.h"
 +#include "nicira-ext.h"
+ #include "ofpbuf.h"
  #include "openflow.h"
  #include "packets.h"
  #include "poll-loop.h"
@@@ -155,10 -148,6 +155,10 @@@ static struct hook make_hook(bool (*loc
                               void (*periodic_cb)(void *),
                               void (*wait_cb)(void *),
                               void *aux);
- static void get_ofp_packet_payload(struct ofp_packet_in *, struct buffer *);
 +static struct ofp_packet_in *get_ofp_packet_in(struct relay *);
 +static bool get_ofp_packet_eth_header(struct relay *, struct ofp_packet_in **,
 +                                      struct eth_header **);
++static void get_ofp_packet_payload(struct ofp_packet_in *, struct ofpbuf *);
  
  struct switch_status;
  struct status_reply;
@@@ -369,35 -338,8 +369,35 @@@ main(int argc, char *argv[]
      return 0;
  }
  
-         fatal(retval, "opening %s", name);
 +static struct pvconn *
 +open_passive_vconn(const char *name)
 +{
 +    struct pvconn *pvconn;
 +    int retval;
 +
 +    retval = pvconn_open(name, &pvconn);
 +    if (retval && retval != EAGAIN) {
++        ofp_fatal(retval, "opening %s", name);
 +    }
 +    return pvconn;
 +}
 +
 +static struct vconn *
 +accept_vconn(struct pvconn *pvconn)
 +{
 +    struct vconn *new;
 +    int retval;
 +
 +    retval = pvconn_accept(pvconn, OFP_VERSION, &new);
 +    if (retval && retval != EAGAIN) {
 +        VLOG_WARN_RL(&vrl, "accept failed (%s)", strerror(retval));
 +    }
 +    return new;
 +}
 +
  static struct hook
 -make_hook(bool (*packet_cb)(struct relay *, int half, void *aux),
 +make_hook(bool (*local_packet_cb)(struct relay *, void *aux),
 +          bool (*remote_packet_cb)(struct relay *, void *aux),
            void (*periodic_cb)(void *aux),
            void (*wait_cb)(void *aux),
            void *aux)
      h.aux = aux;
      return h;
  }
-     struct buffer *msg = r->halves[HALF_LOCAL].rxbuf;
 +
 +static struct ofp_packet_in *
 +get_ofp_packet_in(struct relay *r)
 +{
++    struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
 +    struct ofp_header *oh = msg->data;
 +    if (oh->type == OFPT_PACKET_IN) {
 +        if (msg->size >= offsetof (struct ofp_packet_in, data)) {
 +            return msg->data;
 +        } else {
 +            VLOG_WARN("packet too short (%zu bytes) for packet_in",
 +                      msg->size);
 +        }
 +    }
 +    return NULL;
 +}
 +
 +static bool
 +get_ofp_packet_eth_header(struct relay *r, struct ofp_packet_in **opip,
 +                          struct eth_header **ethp)
 +{
 +    const int min_len = offsetof(struct ofp_packet_in, data) + ETH_HEADER_LEN;
 +    struct ofp_packet_in *opi = get_ofp_packet_in(r);
 +    if (opi && ntohs(opi->header.length) >= min_len) {
 +        *opip = opi;
 +        *ethp = (void *) opi->data;
 +        return true;
 +    }
 +    return false;
 +}
 +
  \f
  /* OpenFlow message relaying. */
  
@@@ -513,11 -426,11 +513,11 @@@ relay_run(struct relay *r, const struc
  
              if (!this->rxbuf) {
                  this->rxbuf = rconn_recv(this->rconn);
 -                if (this->rxbuf) {
 +                if (this->rxbuf && (i == HALF_REMOTE || !r->is_mgmt_conn)) {
                      const struct hook *h;
                      for (h = hooks; h < &hooks[n_hooks]; h++) {
 -                        if (h->packet_cb(r, i, h->aux)) {
 +                        if (h->packet_cb[i] && h->packet_cb[i](r, h->aux)) {
-                             buffer_delete(this->rxbuf);
+                             ofpbuf_delete(this->rxbuf);
                              this->rxbuf = NULL;
                              progress = true;
                              break;
@@@ -584,558 -497,6 +584,558 @@@ relay_destroy(struct relay *r
      free(r);
  }
  \f
-     struct buffer *msg = r->halves[HALF_LOCAL].rxbuf;
 +/* Port status watcher. */
 +
 +typedef void port_watcher_cb_func(uint16_t port_no,
 +                                  const struct ofp_phy_port *old,
 +                                  const struct ofp_phy_port *new,
 +                                  void *aux);
 +
 +struct port_watcher_cb {
 +    port_watcher_cb_func *function;
 +    void *aux;
 +};
 +
 +struct port_watcher {
 +    struct rconn *local_rconn;
 +    struct rconn *remote_rconn;
 +    struct ofp_phy_port ports[OFPP_MAX + 1];
 +    time_t last_feature_request;
 +    bool got_feature_reply;
 +    int n_txq;
 +    struct port_watcher_cb cbs[2];
 +    int n_cbs;
 +};
 +
 +/* Returns the number of fields that differ from 'a' to 'b'. */
 +static int
 +opp_differs(const struct ofp_phy_port *a, const struct ofp_phy_port *b)
 +{
 +    BUILD_ASSERT_DECL(sizeof *a == 36); /* Trips when we add or remove fields. */
 +    return ((a->port_no != b->port_no)
 +            + (memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr) != 0)
 +            + (memcmp(a->name, b->name, sizeof a->name) != 0)
 +            + (a->flags != b->flags)
 +            + (a->speed != b->speed)
 +            + (a->features != b->features));
 +}
 +
 +static void
 +sanitize_opp(struct ofp_phy_port *opp)
 +{
 +    size_t i;
 +
 +    for (i = 0; i < sizeof opp->name; i++) {
 +        char c = opp->name[i];
 +        if (c && (c < 0x20 || c > 0x7e)) {
 +            opp->name[i] = '.';
 +        }
 +    }
 +    opp->name[sizeof opp->name - 1] = '\0';
 +}
 +
 +static int
 +port_no_to_pw_idx(int port_no)
 +{
 +    return (port_no < OFPP_MAX ? port_no
 +            : port_no == OFPP_LOCAL ? OFPP_MAX
 +            : -1);
 +}
 +
 +static void
 +call_pw_callbacks(struct port_watcher *pw, int port_no,
 +                  const struct ofp_phy_port *old,
 +                  const struct ofp_phy_port *new)
 +{
 +    if (opp_differs(old, new)) {
 +        int i;
 +        for (i = 0; i < pw->n_cbs; i++) {
 +            pw->cbs[i].function(port_no, old, new, pw->cbs[i].aux);
 +        }
 +    }
 +}
 +
 +static void
 +update_phy_port(struct port_watcher *pw, struct ofp_phy_port *opp,
 +                uint8_t reason, bool seen[OFPP_MAX + 1])
 +{
 +    struct ofp_phy_port *pw_opp;
 +    struct ofp_phy_port old;
 +    uint16_t port_no;
 +    int idx;
 +
 +    port_no = ntohs(opp->port_no);
 +    idx = port_no_to_pw_idx(port_no);
 +    if (idx < 0) {
 +        return;
 +    }
 +
 +    if (seen) {
 +        seen[idx] = true;
 +    }
 +
 +    pw_opp = &pw->ports[idx];
 +    old = *pw_opp;
 +    if (reason == OFPPR_DELETE) {
 +        memset(pw_opp, 0, sizeof *pw_opp);
 +        pw_opp->port_no = htons(OFPP_NONE);
 +    } else if (reason == OFPPR_MOD || reason == OFPPR_ADD) {
 +        *pw_opp = *opp;
 +        sanitize_opp(pw_opp);
 +    }
 +    call_pw_callbacks(pw, port_no, &old, pw_opp);
 +}
 +
 +static bool
 +port_watcher_local_packet_cb(struct relay *r, void *pw_)
 +{
 +    struct port_watcher *pw = pw_;
-     struct buffer *msg = r->halves[HALF_REMOTE].rxbuf;
++    struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
 +    struct ofp_header *oh = msg->data;
 +
 +    if (oh->type == OFPT_FEATURES_REPLY
 +        && msg->size >= offsetof(struct ofp_switch_features, ports)) {
 +        struct ofp_switch_features *osf = msg->data;
 +        bool seen[ARRAY_SIZE(pw->ports)];
 +        size_t n_ports;
 +        size_t i;
 +
 +        pw->got_feature_reply = true;
 +
 +        /* Update each port included in the message. */
 +        memset(seen, 0, sizeof seen);
 +        n_ports = ((msg->size - offsetof(struct ofp_switch_features, ports))
 +                   / sizeof *osf->ports);
 +        for (i = 0; i < n_ports; i++) {
 +            update_phy_port(pw, &osf->ports[i], OFPPR_MOD, seen);
 +        }
 +
 +        /* Delete all the ports not included in the message. */
 +        for (i = 0; i < ARRAY_SIZE(pw->ports); i++) {
 +            if (!seen[i]) {
 +                update_phy_port(pw, &pw->ports[i], OFPPR_DELETE, NULL);
 +            }
 +        }
 +    } else if (oh->type == OFPT_PORT_STATUS
 +               && msg->size >= sizeof(struct ofp_port_status)) {
 +        struct ofp_port_status *ops = msg->data;
 +        update_phy_port(pw, &ops->desc, ops->reason, NULL);
 +    }
 +    return false;
 +}
 +
 +static bool
 +port_watcher_remote_packet_cb(struct relay *r, void *pw_)
 +{
 +    struct port_watcher *pw = pw_;
-         struct buffer *b;
++    struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
 +    struct ofp_header *oh = msg->data;
 +
 +    if (oh->type == OFPT_PORT_MOD
 +        && msg->size >= sizeof(struct ofp_port_mod)) {
 +        struct ofp_port_mod *opm = msg->data;
 +        uint16_t port_no = ntohs(opm->desc.port_no);
 +        int idx = port_no_to_pw_idx(port_no);
 +        if (idx >= 0) {
 +            struct ofp_phy_port *pw_opp = &pw->ports[idx];
 +            if (pw_opp->port_no != htons(OFPP_NONE)) {
 +                struct ofp_phy_port old = *pw_opp;
 +                pw_opp->flags = ((pw_opp->flags & ~opm->mask)
 +                                 | (opm->desc.flags & opm->mask));
 +                call_pw_callbacks(pw, port_no, &old, pw_opp);
 +            }
 +        }
 +    }
 +    return false;
 +}
 +
 +static void
 +port_watcher_periodic_cb(void *pw_)
 +{
 +    struct port_watcher *pw = pw_;
 +
 +    if (!pw->got_feature_reply && time_now() >= pw->last_feature_request + 5) {
-     struct buffer *b;
++        struct ofpbuf *b;
 +        make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &b);
 +        rconn_send_with_limit(pw->local_rconn, b, &pw->n_txq, 1);
 +        pw->last_feature_request = time_now();
 +    }
 +}
 +
 +static void
 +put_duplexes(struct ds *ds, const char *name, uint32_t features,
 +             uint32_t hd_bit, uint32_t fd_bit)
 +{
 +    if (features & (hd_bit | fd_bit)) {
 +        ds_put_format(ds, " %s", name);
 +        if (features & hd_bit) {
 +            ds_put_cstr(ds, "(HD)");
 +        }
 +        if (features & fd_bit) {
 +            ds_put_cstr(ds, "(FD)");
 +        }
 +    }
 +}
 +
 +static void
 +log_port_status(uint16_t port_no,
 +                const struct ofp_phy_port *old,
 +                const struct ofp_phy_port *new,
 +                void *aux)
 +{
 +    if (VLOG_IS_DBG_ENABLED()) {
 +        bool was_enabled = old->port_no != htons(OFPP_NONE);
 +        bool now_enabled = new->port_no != htons(OFPP_NONE);
 +        uint32_t features = ntohl(new->features);
 +        struct ds ds;
 +
 +        if (old->flags != new->flags && opp_differs(old, new) == 1) {
 +            /* Don't care if only flags changed. */
 +            return;
 +        }
 +
 +        ds_init(&ds);
 +        ds_put_format(&ds, "\"%s\", "ETH_ADDR_FMT, new->name,
 +                      ETH_ADDR_ARGS(new->hw_addr));
 +        if (ntohl(new->speed)) {
 +            ds_put_format(&ds, ", speed %"PRIu32, ntohl(new->speed));
 +        }
 +        if (features & (OFPPF_10MB_HD | OFPPF_10MB_FD
 +                        | OFPPF_100MB_HD | OFPPF_100MB_FD
 +                        | OFPPF_1GB_HD | OFPPF_1GB_FD | OFPPF_10GB_FD)) {
 +            ds_put_cstr(&ds, ", supports");
 +            put_duplexes(&ds, "10M", features, OFPPF_10MB_HD, OFPPF_10MB_FD);
 +            put_duplexes(&ds, "100M", features,
 +                         OFPPF_100MB_HD, OFPPF_100MB_FD);
 +            put_duplexes(&ds, "1G", features, OFPPF_100MB_HD, OFPPF_100MB_FD);
 +            if (features & OFPPF_10GB_FD) {
 +                ds_put_cstr(&ds, " 10G");
 +            }
 +        }
 +        if (was_enabled != now_enabled) {
 +            if (now_enabled) {
 +                VLOG_DBG("Port %d added: %s", port_no, ds_cstr(&ds));
 +            } else {
 +                VLOG_DBG("Port %d deleted", port_no);
 +            }
 +        } else {
 +            VLOG_DBG("Port %d changed: %s", port_no, ds_cstr(&ds));
 +        }
 +        ds_destroy(&ds);
 +    }
 +}
 +
 +static void
 +port_watcher_register_callback(struct port_watcher *pw,
 +                               port_watcher_cb_func *function,
 +                               void *aux)
 +{
 +    assert(pw->n_cbs < ARRAY_SIZE(pw->cbs));
 +    pw->cbs[pw->n_cbs].function = function;
 +    pw->cbs[pw->n_cbs].aux = aux;
 +    pw->n_cbs++;
 +}
 +
 +static uint32_t
 +port_watcher_get_flags(const struct port_watcher *pw, int port_no)
 +{
 +    int idx = port_no_to_pw_idx(port_no);
 +    return idx >= 0 ? ntohl(pw->ports[idx].flags) : 0;
 +}
 +
 +static void
 +port_watcher_set_flags(struct port_watcher *pw,
 +                       int port_no, uint32_t flags, uint32_t mask)
 +{
 +    struct ofp_phy_port old;
 +    struct ofp_phy_port *p;
 +    struct ofp_port_mod *opm;
 +    struct ofp_port_status *ops;
-     struct buffer payload;
++    struct ofpbuf *b;
 +    int idx;
 +
 +    idx = port_no_to_pw_idx(port_no);
 +    if (idx < 0) {
 +        return;
 +    }
 +
 +    p = &pw->ports[idx];
 +    if (!((ntohl(p->flags) ^ flags) & mask)) {
 +        return;
 +    }
 +    old = *p;
 +
 +    /* Update our idea of the flags. */
 +    p->flags = htonl((ntohl(p->flags) & ~mask) | (flags & mask));
 +    call_pw_callbacks(pw, port_no, &old, p);
 +
 +    /* Change the flags in the datapath. */
 +    opm = make_openflow(sizeof *opm, OFPT_PORT_MOD, &b);
 +    opm->mask = htonl(mask);
 +    opm->desc = *p;
 +    rconn_send(pw->local_rconn, b, NULL);
 +
 +    /* Notify the controller that the flags changed. */
 +    ops = make_openflow(sizeof *ops, OFPT_PORT_STATUS, &b);
 +    ops->reason = OFPPR_MOD;
 +    ops->desc = *p;
 +    rconn_send(pw->remote_rconn, b, NULL);
 +}
 +
 +static bool
 +port_watcher_is_ready(const struct port_watcher *pw)
 +{
 +    return pw->got_feature_reply;
 +}
 +
 +static struct hook
 +port_watcher_create(struct rconn *local_rconn, struct rconn *remote_rconn,
 +                    struct port_watcher **pwp)
 +{
 +    struct port_watcher *pw;
 +    int i;
 +
 +    pw = *pwp = xcalloc(1, sizeof *pw);
 +    pw->local_rconn = local_rconn;
 +    pw->remote_rconn = remote_rconn;
 +    pw->last_feature_request = TIME_MIN;
 +    for (i = 0; i < OFPP_MAX; i++) {
 +        pw->ports[i].port_no = htons(OFPP_NONE);
 +    }
 +    port_watcher_register_callback(pw, log_port_status, NULL);
 +    return make_hook(port_watcher_local_packet_cb,
 +                     port_watcher_remote_packet_cb,
 +                     port_watcher_periodic_cb, NULL, pw);
 +}
 +\f
 +/* Spanning tree protocol. */
 +
 +/* Extra time, in seconds, at boot before going into fail-open, to give the
 + * spanning tree protocol time to figure out the network layout. */
 +#define STP_EXTRA_BOOT_TIME 30
 +
 +struct stp_data {
 +    struct stp *stp;
 +    struct port_watcher *pw;
 +    struct rconn *local_rconn;
 +    struct rconn *remote_rconn;
 +    uint8_t dpid[ETH_ADDR_LEN];
 +    long long int last_tick_256ths;
 +    int n_txq;
 +};
 +
 +static bool
 +stp_local_packet_cb(struct relay *r, void *stp_)
 +{
 +    struct stp_data *stp = stp_;
 +    struct ofp_packet_in *opi;
 +    struct eth_header *eth;
 +    struct llc_header *llc;
-     llc = buffer_at_assert(&payload, sizeof *eth, sizeof *llc);
++    struct ofpbuf payload;
 +    uint16_t port_no;
 +    struct flow flow;
 +
 +    if (!get_ofp_packet_eth_header(r, &opi, &eth)
 +        || !eth_addr_equals(eth->eth_dst, stp_eth_addr)) {
 +        return false;
 +    }
 +
 +    port_no = ntohs(opi->in_port);
 +    if (port_no >= STP_MAX_PORTS) {
 +        /* STP only supports 255 ports. */
 +        return false;
 +    }
 +    if (port_watcher_get_flags(stp->pw, port_no) & OFPPFL_NO_STP) {
 +        /* We're not doing STP on this port. */
 +        return false;
 +    }
 +
 +    if (opi->reason == OFPR_ACTION) {
 +        /* The controller set up a flow for this, so we won't intercept it. */
 +        return false;
 +    }
 +
 +    get_ofp_packet_payload(opi, &payload);
 +    flow_extract(&payload, port_no, &flow);
 +    if (flow.dl_type != htons(OFP_DL_TYPE_NOT_ETH_TYPE)) {
 +        VLOG_DBG("non-LLC frame received on STP multicast address");
 +        return false;
 +    }
-     if (buffer_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
++    llc = ofpbuf_at_assert(&payload, sizeof *eth, sizeof *llc);
 +    if (llc->llc_dsap != STP_LLC_DSAP) {
 +        VLOG_DBG("bad DSAP 0x%02"PRIx8" received on STP multicast address",
 +                 llc->llc_dsap);
 +        return false;
 +    }
 +
 +    /* Trim off padding on payload. */
 +    if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
 +        payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN;
 +    }
-     struct buffer pkt, *opo;
++    if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
 +        struct stp_port *p = stp_get_port(stp->stp, port_no);
 +        stp_received_bpdu(p, payload.data, payload.size);
 +    }
 +
 +    return true;
 +}
 +
 +static long long int
 +time_256ths(void)
 +{
 +    return time_msec() * 256 / 1000;
 +}
 +
 +static void
 +stp_periodic_cb(void *stp_)
 +{
 +    struct stp_data *stp = stp_;
 +    long long int now_256ths = time_256ths();
 +    long long int elapsed_256ths = now_256ths - stp->last_tick_256ths;
 +    struct stp_port *p;
 +
 +    if (!port_watcher_is_ready(stp->pw)) {
 +        /* Can't start STP until we know port flags, because port flags can
 +         * disable STP. */
 +        return;
 +    }
 +    if (elapsed_256ths <= 0) {
 +        return;
 +    }
 +
 +    stp_tick(stp->stp, MIN(INT_MAX, elapsed_256ths));
 +    stp->last_tick_256ths = now_256ths;
 +
 +    while (stp_get_changed_port(stp->stp, &p)) {
 +        int port_no = stp_port_no(p);
 +        enum stp_state state = stp_port_get_state(p);
 +
 +        if (state != STP_DISABLED) {
 +            VLOG_WARN("STP: Port %d entered %s state",
 +                      port_no, stp_state_name(state));
 +        }
 +        if (!(port_watcher_get_flags(stp->pw, port_no) & OFPPFL_NO_STP)) {
 +            uint32_t flags;
 +            switch (state) {
 +            case STP_LISTENING:
 +                flags = OFPPFL_STP_LISTEN;
 +                break;
 +            case STP_LEARNING:
 +                flags = OFPPFL_STP_LEARN;
 +                break;
 +            case STP_DISABLED:
 +            case STP_FORWARDING:
 +                flags = OFPPFL_STP_FORWARD;
 +                break;
 +            case STP_BLOCKING:
 +                flags = OFPPFL_STP_BLOCK;
 +                break;
 +            default:
 +                VLOG_DBG_RL(&vrl, "STP: Port %d has bad state %x",
 +                            port_no, state);
 +                flags = OFPPFL_STP_FORWARD;
 +                break;
 +            }
 +            if (!stp_forward_in_state(state)) {
 +                flags |= OFPPFL_NO_FLOOD;
 +            }
 +            port_watcher_set_flags(stp->pw, port_no, flags,
 +                                   OFPPFL_STP_MASK | OFPPFL_NO_FLOOD);
 +        } else {
 +            /* We don't own those flags. */
 +        }
 +    }
 +}
 +
 +static void
 +stp_wait_cb(void *stp_ UNUSED)
 +{
 +    poll_timer_wait(1000);
 +}
 +
 +static void
 +send_bpdu(const void *bpdu, size_t bpdu_size, int port_no, void *stp_)
 +{
 +    struct stp_data *stp = stp_;
 +    struct eth_header *eth;
 +    struct llc_header *llc;
-     buffer_init(&pkt, ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
-     eth = buffer_put_uninit(&pkt, sizeof *eth);
-     llc = buffer_put_uninit(&pkt, sizeof *llc);
-     buffer_put(&pkt, bpdu, bpdu_size);
++    struct ofpbuf pkt, *opo;
 +
 +    /* Packet skeleton. */
-     buffer_uninit(&pkt);
++    ofpbuf_init(&pkt, ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
++    eth = ofpbuf_put_uninit(&pkt, sizeof *eth);
++    llc = ofpbuf_put_uninit(&pkt, sizeof *llc);
++    ofpbuf_put(&pkt, bpdu, bpdu_size);
 +
 +    /* 802.2 header. */
 +    memcpy(eth->eth_dst, stp_eth_addr, ETH_ADDR_LEN);
 +    memcpy(eth->eth_src, stp->pw->ports[port_no].hw_addr, ETH_ADDR_LEN);
 +    eth->eth_type = htons(pkt.size - ETH_HEADER_LEN);
 +
 +    /* LLC header. */
 +    llc->llc_dsap = STP_LLC_DSAP;
 +    llc->llc_ssap = STP_LLC_SSAP;
 +    llc->llc_cntl = STP_LLC_CNTL;
 +
 +    opo = make_unbuffered_packet_out(&pkt, OFPP_NONE, port_no);
-         fatal(retval, "Could not open %s device", s->of_name);
++    ofpbuf_uninit(&pkt);
 +    rconn_send_with_limit(stp->local_rconn, opo, &stp->n_txq, OFPP_MAX);
 +}
 +
 +static void
 +stp_port_watcher_cb(uint16_t port_no,
 +                    const struct ofp_phy_port *old,
 +                    const struct ofp_phy_port *new,
 +                    void *stp_)
 +{
 +    struct stp_data *stp = stp_;
 +    struct stp_port *p;
 +
 +    /* STP only supports a maximum of 255 ports, one less than OpenFlow.  We
 +     * don't support STP on OFPP_LOCAL, either.  */
 +    if (port_no >= STP_MAX_PORTS) {
 +        return;
 +    }
 +
 +    p = stp_get_port(stp->stp, port_no);
 +    if (new->port_no == htons(OFPP_NONE)
 +        || new->flags & htonl(OFPPFL_NO_STP)) {
 +        stp_port_disable(p);
 +    } else {
 +        stp_port_enable(p);
 +        stp_port_set_speed(p, new->speed);
 +    }
 +}
 +
 +static struct hook
 +stp_hook_create(const struct settings *s, struct port_watcher *pw,
 +                struct rconn *local, struct rconn *remote)
 +{
 +    uint8_t dpid[ETH_ADDR_LEN];
 +    struct netdev *netdev;
 +    struct stp_data *stp;
 +    int retval;
 +
 +    retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, &netdev);
 +    if (retval) {
++        ofp_fatal(retval, "Could not open %s device", s->of_name);
 +    }
 +    memcpy(dpid, netdev_get_etheraddr(netdev), ETH_ADDR_LEN);
 +    netdev_close(netdev);
 +
 +    stp = xcalloc(1, sizeof *stp);
 +    stp->stp = stp_create("stp", eth_addr_to_uint64(dpid), send_bpdu, stp);
 +    stp->pw = pw;
 +    memcpy(stp->dpid, dpid, ETH_ADDR_LEN);
 +    stp->local_rconn = local;
 +    stp->remote_rconn = remote;
 +    stp->last_tick_256ths = time_256ths();
 +
 +    port_watcher_register_callback(pw, stp_port_watcher_cb, stp);
 +    return make_hook(stp_local_packet_cb, NULL,
 +                     stp_periodic_cb, stp_wait_cb, stp);
 +}
 +\f
  /* In-band control. */
  
  struct in_band_data {
@@@ -1225,56 -576,74 +1225,56 @@@ in_band_local_packet_cb(struct relay *r
  {
      struct in_band_data *in_band = in_band_;
      struct rconn *rc = r->halves[HALF_LOCAL].rconn;
 -    struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
      struct ofp_packet_in *opi;
 -    struct ofp_header *oh;
 -    size_t pkt_ofs, pkt_len;
 -    struct ofpbuf pkt;
 +    struct eth_header *eth;
-     struct buffer payload;
++    struct ofpbuf payload;
      struct flow flow;
 -    uint16_t in_port, out_port;
 -    const uint8_t *controller_mac;
 +    uint16_t in_port;
 +    int out_port;
  
 -    if (half != HALF_LOCAL || r->is_mgmt_conn) {
 +    if (!get_ofp_packet_eth_header(r, &opi, &eth)) {
          return false;
      }
 -
 -    oh = msg->data;
 -    if (oh->type != OFPT_PACKET_IN) {
 -        return false;
 -    }
 -    if (msg->size < offsetof(struct ofp_packet_in, data)) {
 -        VLOG_WARN_RL(&vrl, "packet too short (%zu bytes) for packet_in",
 -                     msg->size);
 -        return false;
 -    }
 -
 -    /* Extract flow data from 'opi' into 'flow'. */
 -    opi = msg->data;
      in_port = ntohs(opi->in_port);
 -    pkt_ofs = offsetof(struct ofp_packet_in, data);
 -    pkt_len = ntohs(opi->header.length) - pkt_ofs;
 -    pkt.data = opi->data;
 -    pkt.size = pkt_len;
 -    flow_extract(&pkt, in_port, &flow);
  
      /* Deal with local stuff. */
 -    controller_mac = get_controller_mac(in_band);
      if (in_port == OFPP_LOCAL) {
          /* Sent by secure channel. */
 -        out_port = mac_learning_lookup(in_band->ml, flow.dl_dst);
 -    } else if (eth_addr_equals(flow.dl_dst, in_band->mac)) {
 +        out_port = mac_learning_lookup(in_band->ml, eth->eth_dst);
 +    } else if (eth_addr_equals(eth->eth_dst, in_band->mac)) {
          /* Sent to secure channel. */
          out_port = OFPP_LOCAL;
 -        if (mac_learning_learn(in_band->ml, flow.dl_src, in_port)) {
 -            VLOG_DBG_RL(&vrl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
 -                        ETH_ADDR_ARGS(flow.dl_src), in_port);
 -        }
 -    } else if (flow.dl_type == htons(ETH_TYPE_ARP)
 -               && eth_addr_is_broadcast(flow.dl_dst)
 -               && is_controller_mac(flow.dl_src, in_band)) {
 +        in_band_learn_mac(in_band, in_port, eth->eth_src);
 +    } else if (eth->eth_type == htons(ETH_TYPE_ARP)
 +               && eth_addr_is_broadcast(eth->eth_dst)
 +               && is_controller_mac(eth->eth_src, in_band)) {
          /* ARP sent by controller. */
          out_port = OFPP_FLOOD;
 -    } else if (is_controller_mac(flow.dl_dst, in_band)) {
 -        if (mac_learning_learn(in_band->ml, flow.dl_src, in_port)) {
 -            VLOG_DBG_RL(&vrl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
 -                        ETH_ADDR_ARGS(flow.dl_src), in_port);
 -        }
 -
 -        out_port = mac_learning_lookup(in_band->ml, controller_mac);
 -        if (in_port != out_port) {
 +    } else if (is_controller_mac(eth->eth_dst, in_band)
 +               || is_controller_mac(eth->eth_src, in_band)) {
 +        /* Traffic to or from controller.  Switch it by hand. */
 +        in_band_learn_mac(in_band, in_port, eth->eth_src);
 +        out_port = mac_learning_lookup(in_band->ml, eth->eth_dst);
 +    } else {
 +        const uint8_t *controller_mac;
 +        controller_mac = get_controller_mac(in_band);
 +        if (eth->eth_type == htons(ETH_TYPE_ARP)
 +            && eth_addr_is_broadcast(eth->eth_dst)
 +            && is_controller_mac(eth->eth_src, in_band)) {
 +            /* ARP sent by controller. */
 +            out_port = OFPP_FLOOD;
 +        } else if (is_controller_mac(eth->eth_dst, in_band)
 +                   && in_port == mac_learning_lookup(in_band->ml,
 +                                                     controller_mac)) {
 +            /* Drop controller traffic that arrives on the controller port. */
 +            out_port = -1;
 +        } else {
              return false;
          }
 -
 -        /* This is controller traffic that arrived on the controller port.
 -         * It will get dropped below. */
 -    } else if (is_controller_mac(flow.dl_src, in_band)) {
 -        out_port = mac_learning_lookup(in_band->ml, flow.dl_dst);
 -    } else {
 -        return false;
      }
  
 +    get_ofp_packet_payload(opi, &payload);
 +    flow_extract(&payload, in_port, &flow);
      if (in_port == out_port) {
          /* The input and output port match.  Set up a flow to drop packets. */
          queue_tx(rc, in_band, make_add_flow(&flow, ntohl(opi->buffer_id),
      } else {
          /* We don't know that MAC.  Send along the packet without setting up a
           * flow. */
-         struct buffer *b;
+         struct ofpbuf *b;
          if (ntohl(opi->buffer_id) == UINT32_MAX) {
 -            b = make_unbuffered_packet_out(&pkt, in_port, out_port);
 +            b = make_unbuffered_packet_out(&payload, in_port, out_port);
          } else {
              b = make_buffered_packet_out(ntohl(opi->buffer_id),
                                           in_port, out_port);
@@@ -1331,14 -700,6 +1331,14 @@@ in_band_status_cb(struct status_reply *
      }
  }
  
- get_ofp_packet_payload(struct ofp_packet_in *opi, struct buffer *payload)
 +static void
++get_ofp_packet_payload(struct ofp_packet_in *opi, struct ofpbuf *payload)
 +{
 +    payload->data = opi->data;
 +    payload->size = ntohs(opi->header.length) - offsetof(struct ofp_packet_in,
 +                                                         data);
 +}
 +
  static struct hook
  in_band_hook_create(const struct settings *s, struct switch_status *ss,
                      struct rconn *remote)
@@@ -1572,7 -937,7 +1572,7 @@@ rate_limit_local_packet_cb(struct rela
          return false;
      } else {
          /* Otherwise queue it up for the periodic callback to drain out. */
-         struct buffer *msg = r->halves[HALF_LOCAL].rxbuf;
 -        struct ofp_packet_in *opi = msg->data;
++        struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
          int port = ntohs(opi->in_port) % OFPP_MAX;
          if (rl->n_queued >= s->burst_limit) {
              drop_packet(rl);
@@@ -1679,21 -1044,31 +1679,21 @@@ switch_status_remote_packet_cb(struct r
  {
      struct switch_status *ss = ss_;
      struct rconn *rc = r->halves[HALF_REMOTE].rconn;
-     struct buffer *msg = r->halves[HALF_REMOTE].rxbuf;
+     struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
      struct switch_status_category *c;
 -    struct ofp_stats_request *osr;
 -    struct ofp_stats_reply *reply;
 +    struct nicira_header *request;
 +    struct nicira_header *reply;
      struct status_reply sr;
-     struct buffer *b;
 -    struct ofp_header *oh;
+     struct ofpbuf *b;
      int retval;
  
 -    if (half == HALF_LOCAL) {
 +    if (msg->size < sizeof(struct nicira_header)) {
          return false;
      }
 -
 -    oh = msg->data;
 -    if (oh->type != OFPT_STATS_REQUEST) {
 -        return false;
 -    }
 -    if (msg->size < sizeof(struct ofp_stats_request)) {
 -        VLOG_WARN_RL(&vrl, "packet too short (%zu bytes) for stats_request",
 -                     msg->size);
 -        return false;
 -    }
 -
 -    osr = msg->data;
 -    if (osr->type != htons(OFPST_SWITCH)) {
 +    request = msg->data;
 +    if (request->header.type != OFPT_VENDOR
 +        || request->vendor_id != htonl(NX_VENDOR_ID)
 +        || request->subtype != htonl(NXT_STATUS_REQUEST)) {
          return false;
      }
  
@@@ -2123,13 -1498,6 +2124,13 @@@ parse_options(int argc, char *argv[], s
              s->listener_names[s->n_listeners++] = optarg;
              break;
  
-                 fatal(0, "-m or --monitor may only be specified once");
 +        case 'm':
 +            if (s->monitor_name) {
++                ofp_fatal(0, "-m or --monitor may only be specified once");
 +            }
 +            s->monitor_name = optarg;
 +            break;
 +
          case 'h':
              usage();
  
  #include "packets.h"
  #include "poll-loop.h"
  #include "rconn.h"
- #include "vconn.h"
 +#include "stp.h"
+ #include "switch-flow.h"
  #include "table.h"
  #include "timeval.h"
+ #include "vconn.h"
  #include "xtoxll.h"
  
  #define THIS_MODULE VLM_datapath
@@@ -142,25 -150,24 +145,25 @@@ static void remote_run(struct datapath 
  static void remote_wait(struct remote *);
  static void remote_destroy(struct remote *);
  
- void dp_output_port(struct datapath *, struct buffer *,
+ void dp_output_port(struct datapath *, struct ofpbuf *,
 -                    int in_port, int out_port);
 +                    int in_port, int out_port, bool ignore_no_fwd);
  void dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm);
- void dp_output_control(struct datapath *, struct buffer *, int in_port,
+ void dp_output_control(struct datapath *, struct ofpbuf *, int in_port,
                         size_t max_len, int reason);
  static void send_flow_expired(struct datapath *, struct sw_flow *,
                                enum ofp_flow_expired_reason);
  static int update_port_status(struct sw_port *p);
  static void send_port_status(struct sw_port *p, uint8_t status);
  static void del_switch_port(struct sw_port *p);
- static void execute_actions(struct datapath *, struct buffer *,
+ static void execute_actions(struct datapath *, struct ofpbuf *,
                              int in_port, const struct sw_flow_key *,
 -                            const struct ofp_action *, int n_actions);
 +                            const struct ofp_action *, int n_actions,
 +                            bool ignore_no_fwd);
- static void modify_vlan(struct buffer *buffer, const struct sw_flow_key *key,
+ static void modify_vlan(struct ofpbuf *buffer, const struct sw_flow_key *key,
                          const struct ofp_action *a);
- static void modify_nh(struct buffer *buffer, uint16_t eth_proto,
+ static void modify_nh(struct ofpbuf *buffer, uint16_t eth_proto,
                        uint8_t nw_proto, const struct ofp_action *a);
- static void modify_th(struct buffer *buffer, uint16_t eth_proto,
+ static void modify_th(struct ofpbuf *buffer, uint16_t eth_proto,
                            uint8_t nw_proto, const struct ofp_action *a);
  
  /* Buffers are identified to userspace by a 31-bit opaque ID.  We divide the ID
  
  #define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS)
  
- int run_flow_through_tables(struct datapath *, struct buffer *,
 -int run_flow_through_tables(struct datapath *, struct ofpbuf *, int in_port);
 -void fwd_port_input(struct datapath *, struct ofpbuf *, int in_port);
++int run_flow_through_tables(struct datapath *, struct ofpbuf *,
 +                            struct sw_port *);
- void fwd_port_input(struct datapath *, struct buffer *, struct sw_port *);
++void fwd_port_input(struct datapath *, struct ofpbuf *, struct sw_port *);
  int fwd_control_input(struct datapath *, const struct sender *,
                        const void *, size_t);
  
@@@ -528,15 -534,14 +531,15 @@@ output_all(struct datapath *dp, struct 
              continue;
          }
          if (prev_port != -1) {
-             dp_output_port(dp, buffer_clone(buffer), in_port, prev_port,
 -            dp_output_port(dp, ofpbuf_clone(buffer), in_port, prev_port);
++            dp_output_port(dp, ofpbuf_clone(buffer), in_port, prev_port,
 +                           false);
          }
          prev_port = port_no(dp, p);
      }
      if (prev_port != -1)
 -        dp_output_port(dp, buffer, in_port, prev_port);
 +        dp_output_port(dp, buffer, in_port, prev_port, false);
      else
-         buffer_delete(buffer);
+         ofpbuf_delete(buffer);
  
      return 0;
  }
@@@ -564,8 -569,8 +567,8 @@@ output_packet(struct datapath *dp, stru
  /* Takes ownership of 'buffer' and transmits it to 'out_port' on 'dp'.
   */
  void
- dp_output_port(struct datapath *dp, struct buffer *buffer,
+ dp_output_port(struct datapath *dp, struct ofpbuf *buffer,
 -               int in_port, int out_port)
 +               int in_port, int out_port, bool ignore_no_fwd)
  {
  
      assert(buffer);
      } else if (out_port == OFPP_IN_PORT) {
          output_packet(dp, buffer, in_port);
      } else if (out_port == OFPP_TABLE) {
 -              if (run_flow_through_tables(dp, buffer, in_port)) {
 +        struct sw_port *p = in_port < OFPP_MAX ? &dp->ports[in_port] : 0;
 +              if (run_flow_through_tables(dp, buffer, p)) {
-                       buffer_delete(buffer);
+                       ofpbuf_delete(buffer);
          }
      } else {
          if (in_port == out_port) {
@@@ -672,13 -683,16 +675,13 @@@ dp_send_features_reply(struct datapath 
  
      ofr = make_openflow_reply(sizeof *ofr, OFPT_FEATURES_REPLY,
                                 sender, &buffer);
 -    ofr->datapath_id    = htonll(dp->id); 
 -    ofr->n_exact        = htonl(2 * TABLE_HASH_MAX_FLOWS);
 -    ofr->n_compression  = 0;         /* Not supported */
 -    ofr->n_general      = htonl(TABLE_LINEAR_MAX_FLOWS);
 -    ofr->buffer_mb      = htonl(UINT32_MAX);
 -    ofr->n_buffers      = htonl(N_PKT_BUFFERS);
 -    ofr->capabilities   = htonl(OFP_SUPPORTED_CAPABILITIES);
 -    ofr->actions        = htonl(OFP_SUPPORTED_ACTIONS);
 +    ofr->datapath_id  = htonll(dp->id); 
 +    ofr->n_tables     = dp->chain->n_tables;
 +    ofr->n_buffers    = htonl(N_PKT_BUFFERS);
 +    ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES);
 +    ofr->actions      = htonl(OFP_SUPPORTED_ACTIONS);
      LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
-         struct ofp_phy_port *opp = buffer_put_uninit(buffer, sizeof *opp);
+         struct ofp_phy_port *opp = ofpbuf_put_uninit(buffer, sizeof *opp);
          memset(opp, 0, sizeof *opp);
          fill_port_desc(dp, p, opp);
      }
@@@ -793,11 -807,12 +796,11 @@@ send_flow_expired(struct datapath *dp, 
  
  void
  dp_send_error_msg(struct datapath *dp, const struct sender *sender,
 -        uint16_t type, uint16_t code, const uint8_t *data, size_t len)
 +                  uint16_t type, uint16_t code, const void *data, size_t len)
  {
-     struct buffer *buffer;
+     struct ofpbuf *buffer;
      struct ofp_error_msg *oem;
 -    oem = make_openflow_reply(sizeof(*oem)+len, OFPT_ERROR_MSG, 
 -                              sender, &buffer);
 +    oem = make_openflow_reply(sizeof(*oem)+len, OFPT_ERROR, sender, &buffer);
      oem->type = htons(type);
      oem->code = htons(code);
      memcpy(oem->data, data, len);
@@@ -838,29 -853,23 +841,29 @@@ fill_flow_stats(struct ofpbuf *buffer, 
  }
  
  \f
 -/* 'buffer' was received on 'in_port', a physical switch port between 0 and
 - * OFPP_MAX.  Process it according to 'dp''s flow table.  Returns 0 if
 +/* 'buffer' was received on 'p', which may be a a physical switch port or a
 + * null pointer.  Process it according to 'dp''s flow table.  Returns 0 if
   * successful, in which case 'buffer' is destroyed, or -ESRCH if there is no
   * matching flow, in which case 'buffer' still belongs to the caller. */
- int run_flow_through_tables(struct datapath *dp, struct buffer *buffer,
+ int run_flow_through_tables(struct datapath *dp, struct ofpbuf *buffer,
 -                            int in_port)
 +                            struct sw_port *p)
  {
      struct sw_flow_key key;
      struct sw_flow *flow;
  
      key.wildcards = 0;
 -    if (flow_extract(buffer, in_port, &key.flow)
 +    if (flow_extract(buffer, p ? port_no(dp, p) : OFPP_NONE, &key.flow)
          && (dp->flags & OFPC_FRAG_MASK) == OFPC_FRAG_DROP) {
          /* Drop fragment. */
-         buffer_delete(buffer);
+         ofpbuf_delete(buffer);
          return 0;
      }
-               buffer_delete(buffer);
 +      if (p && p->flags & (OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP)
 +        && p->flags & (!eth_addr_equals(key.flow.dl_dst, stp_eth_addr)
 +                       ? OFPPFL_NO_RECV : OFPPFL_NO_RECV_STP)) {
++              ofpbuf_delete(buffer);
 +              return 0;
 +      }
  
      flow = chain_lookup(dp->chain, &key);
      if (flow != NULL) {
      }
  }
  
 -/* 'buffer' was received on 'in_port', a physical switch port between 0 and
 - * OFPP_MAX.  Process it according to 'dp''s flow table, sending it up to the
 - * controller if no flow matches.  Takes ownership of 'buffer'. */
 -void fwd_port_input(struct datapath *dp, struct ofpbuf *buffer, int in_port) 
 +/* 'buffer' was received on 'p', which may be a a physical switch port or a
 + * null pointer.  Process it according to 'dp''s flow table, sending it up to
 + * the controller if no flow matches.  Takes ownership of 'buffer'. */
- void fwd_port_input(struct datapath *dp, struct buffer *buffer,
++void fwd_port_input(struct datapath *dp, struct ofpbuf *buffer,
 +                    struct sw_port *p)
  {
 -    if (run_flow_through_tables(dp, buffer, in_port)) {
 -        dp_output_control(dp, buffer, in_port, dp->miss_send_len,
 -                          OFPR_NO_MATCH);
 +    if (run_flow_through_tables(dp, buffer, p)) {
 +        dp_output_control(dp, buffer, port_no(dp, p),
 +                          dp->miss_send_len, OFPR_NO_MATCH);
      }
  }
  
  static void
- do_output(struct datapath *dp, struct buffer *buffer, int in_port,
+ do_output(struct datapath *dp, struct ofpbuf *buffer, int in_port,
 -          size_t max_len, int out_port)
 +          size_t max_len, int out_port, bool ignore_no_fwd)
  {
      if (out_port != OFPP_CONTROLLER) {
 -        dp_output_port(dp, buffer, in_port, out_port);
 +        dp_output_port(dp, buffer, in_port, out_port, ignore_no_fwd);
      } else {
          dp_output_control(dp, buffer, in_port, max_len, OFPR_ACTION);
      }
  }
  
  static void
- execute_actions(struct datapath *dp, struct buffer *buffer,
+ execute_actions(struct datapath *dp, struct ofpbuf *buffer,
                  int in_port, const struct sw_flow_key *key,
 -                const struct ofp_action *actions, int n_actions)
 +                const struct ofp_action *actions, int n_actions,
 +                bool ignore_no_fwd)
  {
      /* Every output action needs a separate clone of 'buffer', but the common
       * case is just a single output action, so that doing a clone and then
          struct eth_header *eh = buffer->l2;
  
          if (prev_port != -1) {
-             do_output(dp, buffer_clone(buffer), in_port, max_len, prev_port,
 -            do_output(dp, ofpbuf_clone(buffer), in_port, max_len, prev_port);
++            do_output(dp, ofpbuf_clone(buffer), in_port, max_len, prev_port,
 +                      ignore_no_fwd);
              prev_port = -1;
          }
  
          }
      }
      if (prev_port != -1)
 -        do_output(dp, buffer, in_port, max_len, prev_port);
 +        do_output(dp, buffer, in_port, max_len, prev_port, ignore_no_fwd);
      else
-         buffer_delete(buffer);
+         ofpbuf_delete(buffer);
  }
  
- static void modify_nh(struct buffer *buffer, uint16_t eth_proto,
+ static void modify_nh(struct ofpbuf *buffer, uint16_t eth_proto,
                        uint8_t nw_proto, const struct ofp_action *a)
  {
      if (eth_proto == ETH_TYPE_IP) {
  fwd_control_input(struct datapath *dp, const struct sender *sender,
                    const void *msg, size_t length)
  {
-     struct openflow_packet {
-         size_t min_size;
-         int (*handler)(struct datapath *, const struct sender *, const void *);
-     };
-     static const struct openflow_packet packets[] = {
-         [OFPT_FEATURES_REQUEST] = {
-             sizeof (struct ofp_header),
-             recv_features_request,
-         },
-         [OFPT_GET_CONFIG_REQUEST] = {
-             sizeof (struct ofp_header),
-             recv_get_config_request,
-         },
-         [OFPT_SET_CONFIG] = {
-             sizeof (struct ofp_switch_config),
-             recv_set_config,
-         },
-         [OFPT_PACKET_OUT] = {
-             sizeof (struct ofp_packet_out),
-             recv_packet_out,
-         },
-         [OFPT_FLOW_MOD] = {
-             sizeof (struct ofp_flow_mod),
-             recv_flow,
-         },
-         [OFPT_PORT_MOD] = {
-             sizeof (struct ofp_port_mod),
-             recv_port_mod,
-         },
-         [OFPT_STATS_REQUEST] = {
-             sizeof (struct ofp_stats_request),
-             recv_stats_request,
-         },
-         [OFPT_ECHO_REQUEST] = {
-             sizeof (struct ofp_header),
-             recv_echo_request,
-         },
-         [OFPT_ECHO_REPLY] = {
-             sizeof (struct ofp_header),
-             recv_echo_reply,
-         },
-     };
+     int (*handler)(struct datapath *, const struct sender *, const void *);
      struct ofp_header *oh;
+     size_t min_size;
  
+     /* Check encapsulated length. */
      oh = (struct ofp_header *) msg;
-     if (ntohs(oh->length) > length)
+     if (ntohs(oh->length) > length) {
+         return -EINVAL;
+     }
+     assert(oh->version == OFP_VERSION);
+     /* Figure out how to handle it. */
+     switch (oh->type) {
+     case OFPT_FEATURES_REQUEST:
+         min_size = sizeof(struct ofp_header);
+         handler = recv_features_request;
+         break;
+     case OFPT_GET_CONFIG_REQUEST:
+         min_size = sizeof(struct ofp_header);
+         handler = recv_get_config_request;
+         break;
+     case OFPT_SET_CONFIG:
+         min_size = sizeof(struct ofp_switch_config);
+         handler = recv_set_config;
+         break;
+     case OFPT_PACKET_OUT:
+         min_size = sizeof(struct ofp_packet_out);
+         handler = recv_packet_out;
+         break;
+     case OFPT_FLOW_MOD:
+         min_size = sizeof(struct ofp_flow_mod);
+         handler = recv_flow;
+         break;
+     case OFPT_PORT_MOD:
+         min_size = sizeof(struct ofp_port_mod);
+         handler = recv_port_mod;
+         break;
+     case OFPT_STATS_REQUEST:
+         min_size = sizeof(struct ofp_stats_request);
+         handler = recv_stats_request;
+         break;
+     case OFPT_ECHO_REQUEST:
+         min_size = sizeof(struct ofp_header);
+         handler = recv_echo_request;
+         break;
+     case OFPT_ECHO_REPLY:
+         min_size = sizeof(struct ofp_header);
+         handler = recv_echo_reply;
+         break;
+     default:
 -        return -ENOSYS;
++        dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE,
++                          msg, length);
 +        return -EINVAL;
-     if (oh->type < ARRAY_SIZE(packets)) {
-         const struct openflow_packet *pkt = &packets[oh->type];
-         if (pkt->handler) {
-             if (length < pkt->min_size)
-                 return -EFAULT;
-             return pkt->handler(dp, sender, msg);
-         }
      }
-     dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE,
-                       msg, length);
-     return -EINVAL;
+     /* Handle it. */
+     if (length < min_size)
+         return -EFAULT;
+     return handler(dp, sender, msg);
  }
  \f
  /* Packet buffering. */
Simple merge
Simple merge
diff --cc switch/switch.c
@@@ -99,21 -99,24 +99,21 @@@ main(int argc, char *argv[]
      rconn = rconn_create(60, max_backoff);
      error = rconn_connect(rconn, argv[optind]);
      if (error == EAFNOSUPPORT) {
-         fatal(0, "no support for %s vconn", argv[optind]);
+         ofp_fatal(0, "no support for %s vconn", argv[optind]);
      }
      error = dp_new(&dp, dpid, rconn);
 -    if (listen_vconn_name) {
 -        struct vconn *listen_vconn;
 +    if (listen_pvconn_name) {
 +        struct pvconn *listen_pvconn;
          int retval;
 -        
 -        retval = vconn_open(listen_vconn_name, &listen_vconn);
 +
 +        retval = pvconn_open(listen_pvconn_name, &listen_pvconn);
          if (retval && retval != EAGAIN) {
-             fatal(retval, "opening %s", listen_pvconn_name);
 -            ofp_fatal(retval, "opening %s", listen_vconn_name);
 -        }
 -        if (!vconn_is_passive(listen_vconn)) {
 -            ofp_fatal(0, "%s is not a passive vconn", listen_vconn_name);
++            ofp_fatal(retval, "opening %s", listen_pvconn_name);
          }
 -        dp_add_listen_vconn(dp, listen_vconn);
 +        dp_add_listen_pvconn(dp, listen_pvconn);
      }
      if (error) {
-         fatal(error, "could not create datapath");
+         ofp_fatal(error, "could not create datapath");
      }
      if (port_list) {
          add_ports(dp, port_list); 
@@@ -264,10 -268,10 +265,10 @@@ parse_options(int argc, char *argv[]
              break;
  
          case 'l':
 -            if (listen_vconn_name) {
 +            if (listen_pvconn_name) {
-                 fatal(0, "-l or --listen may be only specified once");
+                 ofp_fatal(0, "-l or --listen may be only specified once");
              }
 -            listen_vconn_name = optarg;
 +            listen_pvconn_name = optarg;
              break;
  
          VCONN_SSL_OPTION_HANDLERS
Simple merge
@@@ -36,6 -36,6 +36,7 @@@
  #include <stdlib.h>
  #include "flow.h"
  #include "list.h"
++#include "openflow.h"
  #include "switch-flow.h"
  #include "datapath.h"
  
index 237e7d5,0000000..9b87c0e
mode 100644,000000..100644
--- /dev/null
@@@ -1,661 -1,0 +1,661 @@@
-         fatal(0, "usage: test-stp INPUT.STP\n");
 +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
 + * Junior University
 + *
 + * We are making the OpenFlow specification and associated documentation
 + * (Software) available for public use and benefit with the expectation
 + * that others will use, modify and enhance the Software and contribute
 + * those enhancements back to the community. However, since we would
 + * like to make the Software available for broadest use, with as few
 + * restrictions as possible permission is hereby granted, free of
 + * charge, to any person obtaining a copy of this Software to deal in
 + * the Software under the copyrights without restriction, including
 + * without limitation the rights to use, copy, modify, merge, publish,
 + * distribute, sublicense, and/or sell copies of the Software, and to
 + * permit persons to whom the Software is furnished to do so, subject to
 + * the following conditions:
 + *
 + * The above copyright notice and this permission notice shall be
 + * included in all copies or substantial portions of the Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 + * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 + * SOFTWARE.
 + *
 + * The name and trademarks of copyright holder(s) may NOT be used in
 + * advertising or publicity pertaining to the Software or any
 + * derivatives without specific, written prior permission.
 + */
 +
 +#include "stp.h"
 +#include <assert.h>
 +#include <ctype.h>
 +#include <errno.h>
 +#include <inttypes.h>
 +#include <stdarg.h>
 +#include <stdlib.h>
 +#include "packets.h"
 +
 +struct bpdu {
 +    int port_no;
 +    void *data;
 +    size_t size;
 +};
 +
 +struct bridge {
 +    struct test_case *tc;
 +    int id;
 +    bool reached;
 +
 +    struct stp *stp;
 +
 +    struct lan *ports[STP_MAX_PORTS];
 +    int n_ports;
 +
 +#define RXQ_SIZE 16
 +    struct bpdu rxq[RXQ_SIZE];
 +    int rxq_head, rxq_tail;
 +};
 +
 +struct lan_conn {
 +    struct bridge *bridge;
 +    int port_no;
 +};
 +
 +struct lan {
 +    struct test_case *tc;
 +    const char *name;
 +    bool reached;
 +    struct lan_conn conns[16];
 +    int n_conns;
 +};
 +
 +struct test_case {
 +    struct bridge *bridges[16];
 +    int n_bridges;
 +    struct lan *lans[26];
 +    int n_lans;
 +};
 +
 +static const char *file_name;
 +static int line_number;
 +static char line[128];
 +static char *pos, *token;
 +static int n_warnings;
 +
 +static struct test_case *
 +new_test_case(void)
 +{
 +    struct test_case *tc = xmalloc(sizeof *tc);
 +    tc->n_bridges = 0;
 +    tc->n_lans = 0;
 +    return tc;
 +}
 +
 +static void
 +send_bpdu(const void *data, size_t size, int port_no, void *b_)
 +{
 +    struct bridge *b = b_;
 +    struct lan *lan;
 +    int i;
 +
 +    assert(port_no < b->n_ports);
 +    lan = b->ports[port_no];
 +    if (!lan) {
 +        return;
 +    }
 +    for (i = 0; i < lan->n_conns; i++) {
 +        struct lan_conn *conn = &lan->conns[i];
 +        if (conn->bridge != b || conn->port_no != port_no) {
 +            struct bridge *dst = conn->bridge;
 +            struct bpdu *bpdu = &dst->rxq[dst->rxq_head++ % RXQ_SIZE];
 +            assert(dst->rxq_head - dst->rxq_tail <= RXQ_SIZE);
 +            bpdu->data = xmemdup(data, size);
 +            bpdu->size = size;
 +            bpdu->port_no = conn->port_no;
 +        }
 +    }
 +}
 +
 +static struct bridge *
 +new_bridge(struct test_case *tc, int id)
 +{
 +    struct bridge *b = xmalloc(sizeof *b);
 +    char name[16];
 +    b->tc = tc;
 +    b->id = id;
 +    snprintf(name, sizeof name, "stp%x", id);
 +    b->stp = stp_create(name, id, send_bpdu, b);
 +    assert(tc->n_bridges < ARRAY_SIZE(tc->bridges));
 +    b->n_ports = 0;
 +    b->rxq_head = b->rxq_tail = 0;
 +    tc->bridges[tc->n_bridges++] = b;
 +    return b;
 +}
 +
 +static struct lan *
 +new_lan(struct test_case *tc, const char *name)
 +{
 +    struct lan *lan = xmalloc(sizeof *lan);
 +    lan->tc = tc;
 +    lan->name = xstrdup(name);
 +    lan->n_conns = 0;
 +    assert(tc->n_lans < ARRAY_SIZE(tc->lans));
 +    tc->lans[tc->n_lans++] = lan;
 +    return lan;
 +}
 +
 +static void
 +reconnect_port(struct bridge *b, int port_no, struct lan *new_lan)
 +{
 +    struct lan *old_lan;
 +    int j;
 +
 +    assert(port_no < b->n_ports);
 +    old_lan = b->ports[port_no];
 +    if (old_lan == new_lan) {
 +        return;
 +    }
 +
 +    /* Disconnect from old_lan. */
 +    if (old_lan) {
 +        for (j = 0; j < old_lan->n_conns; j++) {
 +            struct lan_conn *c = &old_lan->conns[j];
 +            if (c->bridge == b && c->port_no == port_no) {
 +                memmove(c, c + 1, sizeof *c * (old_lan->n_conns - j - 1));
 +                old_lan->n_conns--;
 +                break;
 +            }
 +        }
 +    }
 +
 +    /* Connect to new_lan. */
 +    b->ports[port_no] = new_lan;
 +    if (new_lan) {
 +        int conn_no = new_lan->n_conns++;
 +        assert(conn_no < ARRAY_SIZE(new_lan->conns));
 +        new_lan->conns[conn_no].bridge = b;
 +        new_lan->conns[conn_no].port_no = port_no;
 +    }
 +}
 +
 +static void
 +new_port(struct bridge *b, struct lan *lan, int path_cost)
 +{
 +    int port_no = b->n_ports++;
 +    struct stp_port *p = stp_get_port(b->stp, port_no);
 +    assert(port_no < ARRAY_SIZE(b->ports));
 +    b->ports[port_no] = NULL;
 +    stp_port_set_path_cost(p, path_cost);
 +    stp_port_enable(p);
 +    reconnect_port(b, port_no, lan);
 +}
 +
 +static void
 +dump(struct test_case *tc)
 +{
 +    int i;
 +
 +    for (i = 0; i < tc->n_bridges; i++) {
 +        struct bridge *b = tc->bridges[i];
 +        struct stp *stp = b->stp;
 +        int j;
 +
 +        printf("%s:", stp_get_name(stp));
 +        if (stp_is_root_bridge(stp)) {
 +            printf(" root");
 +        }
 +        printf("\n");
 +        for (j = 0; j < b->n_ports; j++) {
 +            struct stp_port *p = stp_get_port(stp, j);
 +            enum stp_state state = stp_port_get_state(p);
 +
 +            printf("\tport %d", j);
 +            if (b->ports[j]) {
 +                printf(" (lan %s)", b->ports[j]->name);
 +            } else {
 +                printf(" (disconnected)");
 +            }
 +            printf(": %s", stp_state_name(state));
 +            if (p == stp_get_root_port(stp)) {
 +                printf(" (root port, root_path_cost=%u)", stp_get_root_path_cost(stp));
 +            }
 +            printf("\n");
 +        }
 +    }
 +}
 +
 +static void dump_lan_tree(struct test_case *, struct lan *, int level);
 +
 +static void
 +dump_bridge_tree(struct test_case *tc, struct bridge *b, int level)
 +{
 +    int i;
 +
 +    if (b->reached) {
 +        return;
 +    }
 +    b->reached = true;
 +    for (i = 0; i < level; i++) {
 +        printf("\t");
 +    }
 +    printf("%s\n", stp_get_name(b->stp));
 +    for (i = 0; i < b->n_ports; i++) {
 +        struct lan *lan = b->ports[i];
 +        struct stp_port *p = stp_get_port(b->stp, i);
 +        if (stp_port_get_state(p) == STP_FORWARDING && lan) {
 +            dump_lan_tree(tc, lan, level + 1);
 +        }
 +    }
 +}
 +
 +static void
 +dump_lan_tree(struct test_case *tc, struct lan *lan, int level) 
 +{
 +    int i;
 +
 +    if (lan->reached) {
 +        return;
 +    }
 +    lan->reached = true;
 +    for (i = 0; i < level; i++) {
 +        printf("\t");
 +    }
 +    printf("%s\n", lan->name);
 +    for (i = 0; i < lan->n_conns; i++) {
 +        struct bridge *b = lan->conns[i].bridge;
 +        dump_bridge_tree(tc, b, level + 1);
 +    }
 +}
 +
 +static void
 +tree(struct test_case *tc)
 +{
 +    int i;
 +
 +    for (i = 0; i < tc->n_bridges; i++) {
 +        struct bridge *b = tc->bridges[i];
 +        b->reached = false;
 +    }
 +    for (i = 0; i < tc->n_lans; i++) {
 +        struct lan *lan = tc->lans[i];
 +        lan->reached = false;
 +    }
 +    for (i = 0; i < tc->n_bridges; i++) {
 +        struct bridge *b = tc->bridges[i];
 +        struct stp *stp = b->stp;
 +        if (stp_is_root_bridge(stp)) {
 +            dump_bridge_tree(tc, b, 0);
 +        }
 +    }
 +}
 +
 +static void
 +simulate(struct test_case *tc, int granularity)
 +{
 +    int time;
 +
 +    for (time = 0; time < 256 * 180; time += granularity) {
 +        int round_trips;
 +        int i;
 +
 +        for (i = 0; i < tc->n_bridges; i++) {
 +            stp_tick(tc->bridges[i]->stp, granularity);
 +        }
 +        for (round_trips = 0; round_trips < granularity; round_trips++) {
 +            bool any = false;
 +            for (i = 0; i < tc->n_bridges; i++) {
 +                struct bridge *b = tc->bridges[i];
 +                for (; b->rxq_tail != b->rxq_head; b->rxq_tail++) {
 +                    struct bpdu *bpdu = &b->rxq[b->rxq_tail % RXQ_SIZE];
 +                    stp_received_bpdu(stp_get_port(b->stp, bpdu->port_no),
 +                                      bpdu->data, bpdu->size);
 +                    any = true;
 +                }
 +            }
 +            if (!any) {
 +                break;
 +            }
 +        }
 +    }
 +}
 +
 +static void
 +err(const char *message, ...)
 +    PRINTF_FORMAT(1, 2)
 +    NO_RETURN;
 +
 +static void
 +err(const char *message, ...)
 +{
 +    va_list args;
 +
 +    fprintf(stderr, "%s:%d:%td: ", file_name, line_number, pos - line);
 +    va_start(args, message);
 +    vfprintf(stderr, message, args);
 +    va_end(args);
 +    putc('\n', stderr);
 +
 +    exit(EXIT_FAILURE);
 +}
 +
 +static void
 +warn(const char *message, ...)
 +    PRINTF_FORMAT(1, 2);
 +
 +static void
 +warn(const char *message, ...)
 +{
 +    va_list args;
 +
 +    fprintf(stderr, "%s:%d: ", file_name, line_number);
 +    va_start(args, message);
 +    vfprintf(stderr, message, args);
 +    va_end(args);
 +    putc('\n', stderr);
 +
 +    n_warnings++;
 +}
 +
 +static bool
 +get_token(void)
 +{
 +    char *start;
 +
 +    while (isspace((unsigned char) *pos)) {
 +        pos++;
 +    }
 +    if (*pos == '\0') {
 +        token = NULL;
 +        return false;
 +    }
 +
 +    start = pos;
 +    if (isalpha((unsigned char) *pos)) {
 +        while (isalpha((unsigned char) *++pos)) {
 +            continue;
 +        }
 +    } else if (isdigit((unsigned char) *pos)) {
 +        if (*pos == '0' && (pos[1] == 'x' || pos[1] == 'X')) {
 +            pos += 2;
 +            while (isxdigit((unsigned char) *pos)) {
 +                pos++;
 +            }
 +        } else {
 +            while (isdigit((unsigned char) *++pos)) {
 +                continue;
 +            }
 +        }
 +    } else {
 +        pos++;
 +    }
 +
 +    free(token);
 +    token = xmemdup0(start, pos - start);
 +    return true;
 +}
 +
 +static bool
 +get_int(int *intp)
 +{
 +    char *save_pos = pos;
 +    if (token && isdigit((unsigned char) *token)) {
 +        *intp = strtol(token, NULL, 0);
 +        get_token();
 +        return true;
 +    } else {
 +        pos = save_pos;
 +        return false;
 +    }
 +}
 +
 +static bool
 +match(const char *want)
 +{
 +    if (token && !strcmp(want, token)) {
 +        get_token();
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +static int
 +must_get_int(void)
 +{
 +    int x;
 +    if (!get_int(&x)) {
 +        err("expected integer");
 +    }
 +    return x;
 +}
 +
 +static void
 +must_match(const char *want)
 +{
 +    if (!match(want)) {
 +        err("expected \"%s\"", want);
 +    }
 +}
 +
 +int
 +main(int argc, char *argv[])
 +{
 +    struct test_case *tc;
 +    FILE *input_file;
 +    int i;
 +
 +    if (argc != 2) {
-         fatal(errno, "error opening \"%s\"", file_name);
++        ofp_fatal(0, "usage: test-stp INPUT.STP\n");
 +    }
 +    file_name = argv[1];
 +
 +    input_file = fopen(file_name, "r");
 +    if (!input_file) {
++        ofp_fatal(errno, "error opening \"%s\"", file_name);
 +    }
 +
 +    tc = new_test_case();
 +    for (i = 0; i < 26; i++) {
 +        char name[2];
 +        name[0] = 'a' + i;
 +        name[1] = '\0';
 +        new_lan(tc, name);
 +    }
 +
 +    for (line_number = 1; fgets(line, sizeof line, input_file);
 +         line_number++)
 +    {
 +        char *newline, *hash;
 +
 +        newline = strchr(line, '\n');
 +        if (newline) {
 +            *newline = '\0';
 +        }
 +        hash = strchr(line, '#');
 +        if (hash) {
 +            *hash = '\0';
 +        }
 +
 +        pos = line;
 +        if (!get_token()) {
 +            continue;
 +        }
 +        if (match("bridge")) {
 +            struct bridge *bridge;
 +            int bridge_no, port_no;
 +
 +            bridge_no = must_get_int();
 +            if (bridge_no < tc->n_bridges) {
 +                bridge = tc->bridges[bridge_no];
 +            } else if (bridge_no == tc->n_bridges) {
 +                bridge = new_bridge(tc, must_get_int());
 +            } else {
 +                err("bridges must be numbered consecutively from 0");
 +            }
 +            if (match("^")) {
 +                stp_set_bridge_priority(bridge->stp, must_get_int());
 +            }
 +
 +            if (match("=")) {
 +                for (port_no = 0; port_no < STP_MAX_PORTS; port_no++) {
 +                    struct stp_port *p = stp_get_port(bridge->stp, port_no);
 +                    if (!token || match("X")) {
 +                        stp_port_disable(p);
 +                    } else if (match("_")) {
 +                        /* Nothing to do. */
 +                    } else {
 +                        struct lan *lan;
 +                        int path_cost;
 +
 +                        if (!strcmp(token, "0")) {
 +                            lan = NULL;
 +                        } else if (strlen(token) == 1 && islower(*token)) {
 +                            lan = tc->lans[*token - 'a']; 
 +                        } else {
 +                            err("%s is not a valid LAN name "
 +                                "(0 or a lowercase letter)", token);
 +                        }
 +                        get_token();
 +
 +                        path_cost = match(":") ? must_get_int() : 10;
 +                        if (port_no < bridge->n_ports) {
 +                            stp_port_set_path_cost(p, path_cost);
 +                            stp_port_enable(p);
 +                            reconnect_port(bridge, port_no, lan);
 +                        } else if (port_no == bridge->n_ports) {
 +                            new_port(bridge, lan, path_cost);
 +                        } else {
 +                            err("ports must be numbered consecutively");
 +                        }
 +                        if (match("^")) {
 +                            stp_port_set_priority(p, must_get_int());
 +                        }
 +                    }
 +                }
 +            }
 +        } else if (match("run")) {
 +            simulate(tc, must_get_int());
 +        } else if (match("dump")) {
 +            dump(tc);
 +        } else if (match("tree")) {
 +            tree(tc);
 +        } else if (match("check")) {
 +            struct bridge *b;
 +            struct stp *stp;
 +            int bridge_no, port_no;
 +
 +            bridge_no = must_get_int();
 +            if (bridge_no >= tc->n_bridges) {
 +                err("no bridge numbered %d", bridge_no);
 +            }
 +            b = tc->bridges[bridge_no];
 +            stp = b->stp;
 +
 +            must_match("=");
 +
 +            if (match("rootid")) {
 +                uint64_t rootid;
 +                must_match(":");
 +                rootid = must_get_int();
 +                if (match("^")) {
 +                    rootid |= (uint64_t) must_get_int() << 48;
 +                } else {
 +                    rootid |= UINT64_C(0x8000) << 48;
 +                }
 +                if (stp_get_designated_root(stp) != rootid) {
 +                    warn("%s: root %"PRIx64", not %"PRIx64,
 +                         stp_get_name(stp), stp_get_designated_root(stp),
 +                         rootid);
 +                }
 +            }
 +
 +            if (match("root")) {
 +                if (stp_get_root_path_cost(stp)) {
 +                    warn("%s: root path cost of root is %u but should be 0",
 +                         stp_get_name(stp), stp_get_root_path_cost(stp));
 +                }
 +                if (!stp_is_root_bridge(stp)) {
 +                    warn("%s: root is %"PRIx64", not %"PRIx64,
 +                         stp_get_name(stp),
 +                         stp_get_designated_root(stp), stp_get_bridge_id(stp));
 +                }
 +                for (port_no = 0; port_no < b->n_ports; port_no++) {
 +                    struct stp_port *p = stp_get_port(stp, port_no);
 +                    enum stp_state state = stp_port_get_state(p);
 +                    if (!(state & (STP_DISABLED | STP_FORWARDING))) {
 +                        warn("%s: root port %d in state %s",
 +                             stp_get_name(b->stp), port_no,
 +                             stp_state_name(state));
 +                    }
 +                }
 +            } else {
 +                for (port_no = 0; port_no < STP_MAX_PORTS; port_no++) {
 +                    struct stp_port *p = stp_get_port(stp, port_no);
 +                    enum stp_state state;
 +                    if (token == NULL || match("D")) {
 +                        state = STP_DISABLED;
 +                    } else if (match("B")) {
 +                        state = STP_BLOCKING;
 +                    } else if (match("Li")) {
 +                        state = STP_LISTENING;
 +                    } else if (match("Le")) {
 +                        state = STP_LEARNING;
 +                    } else if (match("F")) {
 +                        state = STP_FORWARDING;
 +                    } else if (match("_")) {
 +                        continue;
 +                    } else {
 +                        err("unknown port state %s", token);
 +                    }
 +                    if (stp_port_get_state(p) != state) {
 +                        warn("%s port %d: state is %s but should be %s",
 +                             stp_get_name(stp), port_no,
 +                             stp_state_name(stp_port_get_state(p)),
 +                             stp_state_name(state));
 +                    }
 +                    if (state == STP_FORWARDING) {
 +                        struct stp_port *root_port = stp_get_root_port(stp);
 +                        if (match(":")) {
 +                            int root_path_cost = must_get_int();
 +                            if (p != root_port) {
 +                                warn("%s: port %d is not the root port",
 +                                     stp_get_name(stp), port_no);
 +                                if (!root_port) {
 +                                    warn("%s: (there is no root port)",
 +                                         stp_get_name(stp));
 +                                } else {
 +                                    warn("%s: (port %d is the root port)",
 +                                         stp_get_name(stp),
 +                                         stp_port_no(root_port));
 +                                }
 +                            } else if (root_path_cost
 +                                       != stp_get_root_path_cost(stp)) {
 +                                warn("%s: root path cost is %u, should be %d",
 +                                     stp_get_name(stp),
 +                                     stp_get_root_path_cost(stp),
 +                                     root_path_cost);
 +                            }
 +                        } else if (p == root_port) {
 +                            warn("%s: port %d is the root port but "
 +                                 "not expected to be",
 +                                 stp_get_name(stp), port_no);
 +                        }
 +                    }
 +                }
 +            }
 +            if (n_warnings) {
 +                exit(EXIT_FAILURE);
 +            }
 +        }
 +        if (get_token()) {
 +            err("trailing garbage on line");
 +        }
 +    }
 +
 +    return 0;
 +}
@@@ -53,9 -53,8 +53,9 @@@
  #include "command-line.h"
  #include "compiler.h"
  #include "dpif.h"
 +#include "nicira-ext.h"
  #include "ofp-print.h"
+ #include "ofpbuf.h"
  #include "openflow.h"
  #include "packets.h"
  #include "random.h"
@@@ -328,14 -339,8 +328,14 @@@ static void do_del_port(int argc UNUSED
  \f
  /* Generic commands. */
  
 +static void
 +open_vconn(const char *name, struct vconn **vconnp)
 +{
 +    run(vconn_open_block(name, OFP_VERSION, vconnp), "connecting to %s", name);
 +}
 +
  static void *
- alloc_stats_request(size_t body_len, uint16_t type, struct buffer **bufferp)
+ alloc_stats_request(size_t body_len, uint16_t type, struct ofpbuf **bufferp)
  {
      struct ofp_stats_request *rq;
      rq = make_openflow((offsetof(struct ofp_stats_request, body)
@@@ -353,13 -358,13 +353,13 @@@ send_openflow_buffer(struct vconn *vcon
  }
  
  static void
- dump_transaction(const char *vconn_name, struct buffer *request)
+ dump_transaction(const char *vconn_name, struct ofpbuf *request)
  {
      struct vconn *vconn;
-     struct buffer *reply;
+     struct ofpbuf *reply;
  
      update_openflow_length(request);
 -    run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name);
 +    open_vconn(vconn_name, &vconn);
      run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
      ofp_print(stdout, reply->data, reply->size, 1);
      vconn_close(vconn);
@@@ -422,32 -427,12 +422,32 @@@ do_show(int argc UNUSED, char *argv[]
  static void
  do_status(int argc, char *argv[])
  {
 -    struct ofpbuf *request;
 -    alloc_stats_request(0, OFPST_SWITCH, &request);
 +    struct nicira_header *request, *reply;
 +    struct vconn *vconn;
-     struct buffer *b;
++    struct ofpbuf *b;
 +
 +    request = make_openflow(sizeof *request, OFPT_VENDOR, &b);
 +    request->vendor_id = htonl(NX_VENDOR_ID);
 +    request->subtype = htonl(NXT_STATUS_REQUEST);
      if (argc > 2) {
-         buffer_put(b, argv[2], strlen(argv[2]));
 -        ofpbuf_put(request, argv[2], strlen(argv[2]));
++        ofpbuf_put(b, argv[2], strlen(argv[2]));
      }
 -    dump_stats_transaction(argv[1], request);
 +    open_vconn(argv[1], &vconn);
 +    run(vconn_transact(vconn, b, &b), "talking to %s", argv[1]);
 +    vconn_close(vconn);
 +
 +    if (b->size < sizeof *reply) {
-         fatal(0, "short reply (%zu bytes)", b->size);
++        ofp_fatal(0, "short reply (%zu bytes)", b->size);
 +    }
 +    reply = b->data;
 +    if (reply->header.type != OFPT_VENDOR
 +        || reply->vendor_id != ntohl(NX_VENDOR_ID)
 +        || reply->subtype != ntohl(NXT_STATUS_REPLY)) {
 +        ofp_print(stderr, b->data, b->size, 2);
-         fatal(0, "bad reply");
++        ofp_fatal(0, "bad reply");
 +    }
 +
 +    fwrite(reply + 1, b->size, 1, stdout);
  }
  
  static void
@@@ -829,12 -816,12 +831,12 @@@ static void do_add_flows(int argc, cha
  
      file = fopen(argv[2], "r");
      if (file == NULL) {
-         fatal(errno, "%s: open", argv[2]);
+         ofp_fatal(errno, "%s: open", argv[2]);
      }
  
 -    run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
 +    open_vconn(argv[1], &vconn);
      while (fgets(line, sizeof line, file)) {
-         struct buffer *buffer;
+         struct ofpbuf *buffer;
          struct ofp_flow_mod *ofm;
          uint16_t priority, idle_timeout, hard_timeout;
          size_t size;
@@@ -879,8 -866,8 +881,8 @@@ static void do_del_flows(int argc, cha
      struct vconn *vconn;
      uint16_t priority;
  
 -    run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
 +    open_vconn(argv[1], &vconn);
-     struct buffer *buffer;
+     struct ofpbuf *buffer;
      struct ofp_flow_mod *ofm;
      size_t size;
  
      vconn_close(vconn);
  }
  
-         struct buffer *b;
 +static void
 +do_monitor(int argc UNUSED, char *argv[])
 +{
 +    struct vconn *vconn;
 +    const char *name;
 +
 +    /* If the user specified, e.g., "nl:0", append ":1" to it to ensure that
 +     * the connection will subscribe to listen for asynchronous messages, such
 +     * as packet-in messages. */
 +    if (!strncmp(argv[1], "nl:", 3) && strrchr(argv[1], ':') == &argv[1][2]) {
 +        name = xasprintf("%s:1", argv[1]);
 +    } else {
 +        name = argv[1];
 +    }
 +    open_vconn(argv[1], &vconn);
 +    for (;;) {
-         buffer_delete(b);
++        struct ofpbuf *b;
 +        run(vconn_recv_block(vconn, &b), "vconn_recv");
 +        ofp_print(stderr, b->data, b->size, 2);
++        ofpbuf_delete(b);
 +    }
 +}
 +
  static void
  do_dump_ports(int argc, char *argv[])
  {
  static void
  do_probe(int argc, char *argv[])
  {
-     struct buffer *request;
+     struct ofpbuf *request;
      struct vconn *vconn;
-     struct buffer *reply;
+     struct ofpbuf *reply;
  
      make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request);
 -    run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
 +    open_vconn(argv[1], &vconn);
      run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
      if (reply->size != request->size) {
-         fatal(0, "reply does not match request");
+         ofp_fatal(0, "reply does not match request");
      }
-     buffer_delete(reply);
+     ofpbuf_delete(reply);
      vconn_close(vconn);
  }
  
@@@ -1036,13 -1000,13 +1038,13 @@@ do_ping(int argc, char *argv[]
  
      payload = argc > 2 ? atoi(argv[2]) : 64;
      if (payload > max_payload) {
-         fatal(0, "payload must be between 0 and %zu bytes", max_payload);
+         ofp_fatal(0, "payload must be between 0 and %zu bytes", max_payload);
      }
  
 -    run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
 +    open_vconn(argv[1], &vconn);
      for (i = 0; i < 10; i++) {
          struct timeval start, end;
-         struct buffer *request, *reply;
+         struct ofpbuf *request, *reply;
          struct ofp_header *rq_hdr, *rpy_hdr;
  
          rq_hdr = make_openflow(sizeof(struct ofp_header) + payload,
@@@ -1095,10 -1059,10 +1097,10 @@@ do_benchmark(int argc, char *argv[]
      printf("Sending %d packets * %u bytes (with header) = %u bytes total\n",
             count, message_size, count * message_size);
  
 -    run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
 +    open_vconn(argv[1], &vconn);
      gettimeofday(&start, NULL);
      for (i = 0; i < count; i++) {
-         struct buffer *request, *reply;
+         struct ofpbuf *request, *reply;
          struct ofp_header *rq_hdr;
  
          rq_hdr = make_openflow(message_size, OFPT_ECHO_REQUEST, &request);