Merge commit 'origin/master'
authorBen Pfaff <blp@nicira.com>
Mon, 15 Sep 2008 22:43:44 +0000 (15:43 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 15 Sep 2008 22:43:44 +0000 (15:43 -0700)
Conflicts:

switch/datapath.c
utilities/dpctl.c

1  2 
lib/ofp-print.c
switch/datapath.c
switch/switch-flow.c
switch/switch-flow.h
switch/table-hash.c
switch/table-linear.c
utilities/dpctl.c

diff --combined lib/ofp-print.c
  #include <stdlib.h>
  #include <ctype.h>
  
 -#include "buffer.h"
  #include "compiler.h"
  #include "dynamic-string.h"
  #include "flow.h"
 -#include "util.h"
 +#include "ofpbuf.h"
  #include "openflow.h"
  #include "packets.h"
 +#include "util.h"
  
  static void ofp_print_port_name(struct ds *string, uint16_t port);
  static void ofp_print_match(struct ds *, const struct ofp_match *,
@@@ -96,7 -96,7 +96,7 @@@ ofp_packet_to_string(const void *data, 
  
      pcap = tmpfile();
      if (!pcap) {
 -        error(errno, "tmpfile");
 +        ofp_error(errno, "tmpfile");
          return xstrdup("<error>");
      }
  
  
      fflush(pcap);
      if (ferror(pcap)) {
 -        error(errno, "error writing temporary file");
 +        ofp_error(errno, "error writing temporary file");
      }
      rewind(pcap);
  
      tcpdump = popen(command, "r");
      fclose(pcap);
      if (!tcpdump) {
 -        error(errno, "exec(\"%s\")", command);
 +        ofp_error(errno, "exec(\"%s\")", command);
          return xstrdup("<error>");
      }
  
      status = pclose(tcpdump);
      if (WIFEXITED(status)) {
          if (WEXITSTATUS(status))
 -            error(0, "tcpdump exited with status %d", WEXITSTATUS(status));
 +            ofp_error(0, "tcpdump exited with status %d", WEXITSTATUS(status));
      } else if (WIFSIGNALED(status)) {
 -        error(0, "tcpdump exited with signal %d", WTERMSIG(status)); 
 +        ofp_error(0, "tcpdump exited with signal %d", WTERMSIG(status)); 
      }
      return ds_cstr(&ds);
  }
@@@ -180,7 -180,7 +180,7 @@@ ofp_packet_in(struct ds *string, const 
  
      if (verbosity > 0) {
          struct flow flow;
 -        struct buffer packet;
 +        struct ofpbuf packet;
          struct ofp_match match;
          packet.data = (void *) op->data;
          packet.size = data_len;
@@@ -558,9 -558,24 +558,24 @@@ ofp_print_flow_mod(struct ds *string, c
      const struct ofp_flow_mod *ofm = oh;
  
      ofp_print_match(string, &ofm->match, verbosity);
-     ds_put_format(string, " cmd:%d idle:%d hard:%d pri:%d buf:%#x", 
-             ntohs(ofm->command), ntohs(ofm->idle_timeout),
-             ntohs(ofm->hard_timeout),
+     switch (ntohs(ofm->command)) {
+     case OFPFC_ADD:
+         ds_put_cstr(string, " ADD: ");
+         break;
+     case OFPFC_MODIFY:
+         ds_put_cstr(string, " MOD: ");
+         break;
+     case OFPFC_DELETE:
+         ds_put_cstr(string, " DEL: ");
+         break;
+     case OFPFC_DELETE_STRICT:
+         ds_put_cstr(string, " DEL_STRICT: ");
+         break;
+     default:
+         ds_put_format(string, " cmd:%d ", ntohs(ofm->command));
+     }
+     ds_put_format(string, "idle:%d hard:%d pri:%d buf:%#x", 
+             ntohs(ofm->idle_timeout), ntohs(ofm->hard_timeout),
              ofm->match.wildcards ? ntohs(ofm->priority) : (uint16_t)-1,
              ntohl(ofm->buffer_id));
      ofp_print_actions(string, ofm->actions,
@@@ -903,29 -918,25 +918,29 @@@ print_stats(struct ds *string, int type
      };
  
      struct stats_type {
 +        int type;
          const char *name;
          struct stats_msg request;
          struct stats_msg reply;
      };
  
      static const struct stats_type stats_types[] = {
 -        [OFPST_DESC] = {
 +        {
 +            OFPST_DESC,
              "description",
              { 0, 0, NULL },
              { 0, SIZE_MAX, ofp_desc_stats_reply },
          },
 -        [OFPST_FLOW] = {
 +        {
 +            OFPST_FLOW,
              "flow",
              { sizeof(struct ofp_flow_stats_request),
                sizeof(struct ofp_flow_stats_request),
                ofp_flow_stats_request },
              { 0, SIZE_MAX, ofp_flow_stats_reply },
          },
 -        [OFPST_AGGREGATE] = {
 +        {
 +            OFPST_AGGREGATE,
              "aggregate",
              { sizeof(struct ofp_aggregate_stats_request),
                sizeof(struct ofp_aggregate_stats_request),
                sizeof(struct ofp_aggregate_stats_reply),
                ofp_aggregate_stats_reply },
          },
 -        [OFPST_TABLE] = {
 +        {
 +            OFPST_TABLE,
              "table",
              { 0, 0, NULL },
              { 0, SIZE_MAX, ofp_table_stats_reply },
          },
 -        [OFPST_PORT] = {
 +        {
 +            OFPST_PORT,
              "port",
              { 0, 0, NULL, },
              { 0, SIZE_MAX, ofp_port_stats_reply },
          },
 -        [OFPST_VENDOR] = {
 +        {
 +            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;
          ds_put_format(string, " ***unknown type %d***", type);
          return;
      }
 -    s = &stats_types[type];
 +    for (s = stats_types; s->type >= 0; s++) {
 +        if (s->type == type) {
 +            break;
 +        }
 +    }
      ds_put_format(string, " type=%d(%s)\n", type, s->name);
  
      m = direction == REQUEST ? &s->request : &s->reply;
@@@ -1036,111 -1034,93 +1051,111 @@@ ofp_echo(struct ds *string, const void 
  }
  
  struct openflow_packet {
 +    uint8_t type;
      const char *name;
      size_t min_size;
      void (*printer)(struct ds *, const void *, size_t len, int verbosity);
  };
  
  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,
      },
 -    [OFPT_FEATURES_REPLY] = {
 +    {
 +        OFPT_FEATURES_REPLY,
          "features_reply",
          sizeof (struct ofp_switch_features),
          ofp_print_switch_features,
      },
 -    [OFPT_GET_CONFIG_REQUEST] = {
 +    {
 +        OFPT_GET_CONFIG_REQUEST,
          "get_config_request",
          sizeof (struct ofp_header),
          NULL,
      },
 -    [OFPT_GET_CONFIG_REPLY] = {
 +    {
 +        OFPT_GET_CONFIG_REPLY,
          "get_config_reply",
          sizeof (struct ofp_switch_config),
          ofp_print_switch_config,
      },
 -    [OFPT_SET_CONFIG] = {
 +    {
 +        OFPT_SET_CONFIG,
          "set_config",
          sizeof (struct ofp_switch_config),
          ofp_print_switch_config,
      },
 -    [OFPT_PACKET_IN] = {
 +    {
 +        OFPT_PACKET_IN,
          "packet_in",
          offsetof(struct ofp_packet_in, data),
          ofp_packet_in,
      },
 -    [OFPT_PACKET_OUT] = {
 +    {
 +        OFPT_PACKET_OUT,
          "packet_out",
          sizeof (struct ofp_packet_out),
          ofp_packet_out,
      },
 -    [OFPT_FLOW_MOD] = {
 +    {
 +        OFPT_FLOW_MOD,
          "flow_mod",
          sizeof (struct ofp_flow_mod),
          ofp_print_flow_mod,
      },
 -    [OFPT_FLOW_EXPIRED] = {
 +    {
 +        OFPT_FLOW_EXPIRED,
          "flow_expired",
          sizeof (struct ofp_flow_expired),
          ofp_print_flow_expired,
      },
 -    [OFPT_PORT_MOD] = {
 +    {
 +        OFPT_PORT_MOD,
          "port_mod",
          sizeof (struct ofp_port_mod),
          NULL,
      },
 -    [OFPT_PORT_STATUS] = {
 +    {
 +        OFPT_PORT_STATUS,
          "port_status",
          sizeof (struct ofp_port_status),
          ofp_print_port_status
      },
 -    [OFPT_ERROR] = {
 +    {
 +        OFPT_ERROR,
          "error_msg",
          sizeof (struct ofp_error_msg),
          ofp_print_error_msg,
      },
 -    [OFPT_STATS_REQUEST] = {
 +    {
 +        OFPT_STATS_REQUEST,
          "stats_request",
          sizeof (struct ofp_stats_request),
          ofp_stats_request,
      },
 -    [OFPT_STATS_REPLY] = {
 +    {
 +        OFPT_STATS_REPLY,
          "stats_reply",
          sizeof (struct ofp_stats_reply),
          ofp_stats_reply,
      },
 -    [OFPT_ECHO_REQUEST] = {
 +    {
 +        OFPT_ECHO_REQUEST,
          "echo_request",
          sizeof (struct ofp_header),
          ofp_echo,
      },
 -    [OFPT_ECHO_REPLY] = {
 +    {
 +        OFPT_ECHO_REPLY,
          "echo_reply",
          sizeof (struct ofp_header),
          ofp_echo,
@@@ -1166,19 -1146,14 +1181,19 @@@ ofp_to_string(const void *oh_, size_t l
          ds_put_format(&string, "Bad OpenFlow version %"PRIu8":\n", oh->version);
          ds_put_hex_dump(&string, oh, len, 0, true);
          return ds_cstr(&string);
 -    } else if (oh->type >= ARRAY_SIZE(packets) || !packets[oh->type].name) {
 -        ds_put_format(&string, "Unknown OpenFlow packet type %"PRIu8":\n",
 -                oh->type);
 -        ds_put_hex_dump(&string, oh, len, 0, true);
 -        return ds_cstr(&string);
      }
  
 -    pkt = &packets[oh->type];
 +    for (pkt = packets; ; pkt++) {
 +        if (pkt >= &packets[ARRAY_SIZE(packets)]) {
 +            ds_put_format(&string, "Unknown OpenFlow packet type %"PRIu8":\n",
 +                          oh->type);
 +            ds_put_hex_dump(&string, oh, len, 0, true);
 +            return ds_cstr(&string);
 +        } else if (oh->type == pkt->type) {
 +            break;
 +        }
 +    }
 +
      ds_put_format(&string, "%s (xid=0x%"PRIx32"):", pkt->name, oh->xid);
  
      if (ntohs(oh->length) > len)
diff --combined switch/datapath.c
  #include <inttypes.h>
  #include <stdlib.h>
  #include <string.h>
 -#include "buffer.h"
  #include "chain.h"
  #include "csum.h"
  #include "flow.h"
 +#include "list.h"
  #include "netdev.h"
 +#include "ofpbuf.h"
 +#include "openflow.h"
  #include "packets.h"
  #include "poll-loop.h"
  #include "rconn.h"
  #include "stp.h"
 -#include "vconn.h"
 +#include "switch-flow.h"
  #include "table.h"
  #include "timeval.h"
 +#include "vconn.h"
  #include "xtoxll.h"
  
  #define THIS_MODULE VLM_datapath
@@@ -145,25 -142,25 +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, 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,
                              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 *,
                              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);
  
 -uint32_t save_buffer(struct buffer *);
 -static struct buffer *retrieve_buffer(uint32_t id);
 +uint32_t save_buffer(struct ofpbuf *);
 +static struct ofpbuf *retrieve_buffer(uint32_t id);
  static void discard_buffer(uint32_t id);
  
  static int port_no(struct datapath *dp, struct sw_port *p) 
@@@ -292,7 -289,7 +292,7 @@@ dp_run(struct datapath *dp
      time_t now = time_now();
      struct sw_port *p, *pn;
      struct remote *r, *rn;
 -    struct buffer *buffer = NULL;
 +    struct ofpbuf *buffer = NULL;
  
      if (now != dp->last_timeout) {
          struct list deleted = LIST_INITIALIZER(&deleted);
              const int headroom = 128 + 2;
              const int hard_header = VLAN_ETH_HEADER_LEN;
              const int mtu = netdev_get_mtu(p->netdev);
 -            buffer = buffer_new(headroom + hard_header + mtu);
 -            buffer->data += headroom;
 +            buffer = ofpbuf_new(headroom + hard_header + mtu);
 +            buffer->data = (char*)buffer->data + headroom;
          }
          error = netdev_recv(p->netdev, buffer);
          if (!error) {
                          netdev_get_name(p->netdev), strerror(error));
          }
      }
 -    buffer_delete(buffer);
 +    ofpbuf_delete(buffer);
  
      /* Talk to remotes. */
      LIST_FOR_EACH_SAFE (r, rn, struct remote, node, &dp->remotes) {
@@@ -372,7 -369,7 +372,7 @@@ remote_run(struct datapath *dp, struct 
       * other processing doesn't starve. */
      for (i = 0; i < 50; i++) {
          if (!r->cb_dump) {
 -            struct buffer *buffer;
 +            struct ofpbuf *buffer;
              struct ofp_header *oh;
  
              buffer = rconn_recv(r->rconn);
              } else {
                  VLOG_WARN_RL(&rl, "received too-short OpenFlow message");
              }
 -            buffer_delete(buffer); 
 +            ofpbuf_delete(buffer); 
          } else {
              if (r->n_txq < TXQ_LIMIT) {
                  int error = r->cb_dump(dp, r->cb_aux);
@@@ -517,7 -514,7 +517,7 @@@ dp_destroy(struct datapath *dp
   * "flood" argument is set, don't send out ports with flooding disabled.
   */
  static int
 -output_all(struct datapath *dp, struct buffer *buffer, int in_port, int flood)
 +output_all(struct datapath *dp, struct ofpbuf *buffer, int in_port, int flood)
  {
      struct sw_port *p;
      int prev_port;
              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,
                             false);
          }
          prev_port = port_no(dp, p);
      if (prev_port != -1)
          dp_output_port(dp, buffer, in_port, prev_port, false);
      else
 -        buffer_delete(buffer);
 +        ofpbuf_delete(buffer);
  
      return 0;
  }
  
  void
 -output_packet(struct datapath *dp, struct buffer *buffer, int out_port) 
 +output_packet(struct datapath *dp, struct ofpbuf *buffer, int out_port) 
  {
      if (out_port >= 0 && out_port < OFPP_MAX) { 
          struct sw_port *p = &dp->ports[out_port];
          }
      }
  
 -    buffer_delete(buffer);
 +    ofpbuf_delete(buffer);
      VLOG_DBG_RL(&rl, "can't forward to bad port %d\n", out_port);
  }
  
  /* 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, bool ignore_no_fwd)
  {
  
      } else if (out_port == OFPP_TABLE) {
          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) {
  
  static void *
  make_openflow_reply(size_t openflow_len, uint8_t type,
 -                    const struct sender *sender, struct buffer **bufferp)
 +                    const struct sender *sender, struct ofpbuf **bufferp)
  {
      return make_openflow_xid(openflow_len, type, sender ? sender->xid : 0,
                               bufferp);
  }
  
  static int
 -send_openflow_buffer(struct datapath *dp, struct buffer *buffer,
 +send_openflow_buffer(struct datapath *dp, struct ofpbuf *buffer,
                       const struct sender *sender)
  {
      struct remote *remote = sender ? sender->remote : dp->controller;
   * the caller wants to be sent; a value of 0 indicates the entire packet should
   * be sent. */
  void
 -dp_output_control(struct datapath *dp, struct buffer *buffer, int in_port,
 +dp_output_control(struct datapath *dp, struct ofpbuf *buffer, int in_port,
                    size_t max_len, int reason)
  {
      struct ofp_packet_in *opi;
  
      buffer_id = save_buffer(buffer);
      total_len = buffer->size;
 -    if (buffer_id != UINT32_MAX && buffer->size > max_len) {
 +    if (buffer_id != UINT32_MAX && max_len && buffer->size > max_len) {
          buffer->size = max_len;
      }
  
 -    opi = buffer_push_uninit(buffer, offsetof(struct ofp_packet_in, data));
 +    opi = ofpbuf_push_uninit(buffer, offsetof(struct ofp_packet_in, data));
      opi->header.version = OFP_VERSION;
      opi->header.type    = OFPT_PACKET_IN;
      opi->header.length  = htons(buffer->size);
@@@ -669,7 -666,7 +669,7 @@@ static void fill_port_desc(struct datap
  static void
  dp_send_features_reply(struct datapath *dp, const struct sender *sender)
  {
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
      struct ofp_switch_features *ofr;
      struct sw_port *p;
  
      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);
      }
@@@ -764,7 -761,7 +764,7 @@@ update_port_status(struct sw_port *p
  static void
  send_port_status(struct sw_port *p, uint8_t status) 
  {
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
      struct ofp_port_status *ops;
      ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &buffer);
      ops->reason = status;
@@@ -778,7 -775,7 +778,7 @@@ voi
  send_flow_expired(struct datapath *dp, struct sw_flow *flow,
                    enum ofp_flow_expired_reason reason)
  {
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
      struct ofp_flow_expired *ofe;
      ofe = make_openflow_xid(sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &buffer);
      flow_fill_match(&ofe->match, &flow->key);
@@@ -798,7 -795,7 +798,7 @@@ voi
  dp_send_error_msg(struct datapath *dp, const struct sender *sender,
                    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, sender, &buffer);
      oem->type = htons(type);
  }
  
  static void
 -fill_flow_stats(struct buffer *buffer, struct sw_flow *flow,
 +fill_flow_stats(struct ofpbuf *buffer, struct sw_flow *flow,
                  int table_idx, time_t now)
  {
      struct ofp_flow_stats *ofs;
-     int length = sizeof *ofs + sizeof *ofs->actions * flow->n_actions;
+     int length = sizeof *ofs + sizeof *ofs->actions * flow->sf_acts->n_actions;
 -    ofs = buffer_put_uninit(buffer, length);
 +    ofs = ofpbuf_put_uninit(buffer, length);
      ofs->length          = htons(length);
      ofs->table_id        = table_idx;
      ofs->pad             = 0;
      memset(ofs->pad2, 0, sizeof ofs->pad2);
      ofs->packet_count    = htonll(flow->packet_count);
      ofs->byte_count      = htonll(flow->byte_count);
-     memcpy(ofs->actions, flow->actions,
-            sizeof *ofs->actions * flow->n_actions);
+     memcpy(ofs->actions, flow->sf_acts->actions,
+            sizeof *ofs->actions * flow->sf_acts->n_actions);
  }
  
  \f
   * 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,
                              struct sw_port *p)
  {
      struct sw_flow_key key;
      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;
      }
        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)) {
 -              buffer_delete(buffer);
 +              ofpbuf_delete(buffer);
                return 0;
        }
  
      if (flow != NULL) {
          flow_used(flow, buffer);
          execute_actions(dp, buffer, port_no(dp, p),
-                         &key, flow->actions, flow->n_actions, false);
+                         &key, flow->sf_acts->actions, 
+                         flow->sf_acts->n_actions, false);
          return 0;
      } else {
          return -ESRCH;
  /* '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, p)) {
  }
  
  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, bool ignore_no_fwd)
  {
      if (out_port != OFPP_CONTROLLER) {
  }
  
  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,
                  bool ignore_no_fwd)
          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,
                        ignore_no_fwd);
              prev_port = -1;
          }
      if (prev_port != -1)
          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) {
      }
  }
  
 -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)
  {
      if (eth_proto == ETH_TYPE_IP) {
  }
  
  static void
 -modify_vlan(struct buffer *buffer,
 +modify_vlan(struct ofpbuf *buffer,
              const struct sw_flow_key *key, const struct ofp_action *a)
  {
      uint16_t new_id = a->arg.vlan_id;
              tmp.veth_tci = new_id;
              tmp.veth_next_type = eh->eth_type;
              
 -            veh = buffer_push_uninit(buffer, VLAN_HEADER_LEN);
 +            veh = ofpbuf_push_uninit(buffer, VLAN_HEADER_LEN);
              memcpy(veh, &tmp, sizeof tmp);
 -            buffer->l2 -= VLAN_HEADER_LEN;
 +            buffer->l2 = (char*)buffer->l2 - VLAN_HEADER_LEN;
          }
      } else  {
          /* Remove an existing vlan header if it exists */
              tmp.eth_type = veh->veth_next_type;
              
              buffer->size -= VLAN_HEADER_LEN;
 -            buffer->data += VLAN_HEADER_LEN;
 -            buffer->l2 += VLAN_HEADER_LEN;
 +            buffer->data = (char*)buffer->data + VLAN_HEADER_LEN;
 +            buffer->l2 = (char*)buffer->l2 + VLAN_HEADER_LEN;
              memcpy(buffer->data, &tmp, sizeof tmp);
          }
      }
@@@ -1070,7 -1068,7 +1071,7 @@@ static in
  recv_get_config_request(struct datapath *dp, const struct sender *sender,
                          const void *msg) 
  {
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
      struct ofp_switch_config *osc;
  
      osc = make_openflow_reply(sizeof *osc, OFPT_GET_CONFIG_REPLY,
@@@ -1105,7 -1103,7 +1106,7 @@@ recv_packet_out(struct datapath *dp, co
  {
      const struct ofp_packet_out *opo = msg;
      struct sw_flow_key key;
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
      int n_actions = ntohs(opo->n_actions);
      int act_len = n_actions * sizeof opo->actions[0];
  
      if (ntohl(opo->buffer_id) == (uint32_t) -1) {
          /* FIXME: can we avoid copying data here? */
          int data_len = ntohs(opo->header.length) - sizeof *opo - act_len;
 -        buffer = buffer_new(data_len);
 -        buffer_put(buffer, &opo->actions[n_actions], data_len);
 +        buffer = ofpbuf_new(data_len);
 +        ofpbuf_put(buffer, &opo->actions[n_actions], data_len);
      } else {
          buffer = retrieve_buffer(ntohl(opo->buffer_id));
          if (!buffer) {
@@@ -1181,10 -1179,11 +1182,11 @@@ add_flow(struct datapath *dp, const str
      flow->idle_timeout = ntohs(ofm->idle_timeout);
      flow->hard_timeout = ntohs(ofm->hard_timeout);
      flow->used = flow->created = time_now();
-     flow->n_actions = n_actions;
+     flow->sf_acts->n_actions = n_actions;
      flow->byte_count = 0;
      flow->packet_count = 0;
-     memcpy(flow->actions, ofm->actions, n_actions * sizeof *flow->actions);
+     memcpy(flow->sf_acts->actions, ofm->actions, 
+                 n_actions * sizeof *flow->sf_acts->actions);
  
      /* Act. */
      error = chain_insert(dp->chain, flow);
      }
      error = 0;
      if (ntohl(ofm->buffer_id) != UINT32_MAX) {
 -        struct buffer *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
 +        struct ofpbuf *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
          if (buffer) {
              struct sw_flow_key key;
              uint16_t in_port = ntohs(ofm->match.in_port);
@@@ -1215,6 -1214,55 +1217,55 @@@ error
      return error;
  }
  
 -        struct buffer *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
+ static int
+ mod_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
+ {
+     int error = -ENOMEM;
+     int n_actions;
+     int i;
+     struct sw_flow_key key;
+     /* To prevent loops, make sure there's no action to send to the
+      * OFP_TABLE virtual port.
+      */
+     n_actions = (ntohs(ofm->header.length) - sizeof *ofm) 
+             / sizeof *ofm->actions;
+     for (i=0; i<n_actions; i++) {
+         const struct ofp_action *a = &ofm->actions[i];
+         if (a->type == htons(OFPAT_OUTPUT)
+                     && (a->arg.output.port == htons(OFPP_TABLE)
+                         || a->arg.output.port == htons(OFPP_NONE)
+                         || a->arg.output.port == ofm->match.in_port)) {
+             /* xxx Send fancy new error message? */
+             goto error;
+         }
+     }
+     flow_extract_match(&key, &ofm->match);
+     chain_modify(dp->chain, &key, ofm->actions, n_actions);
+     if (ntohl(ofm->buffer_id) != UINT32_MAX) {
++        struct ofpbuf *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
+         if (buffer) {
+             struct sw_flow_key skb_key;
+             uint16_t in_port = ntohs(ofm->match.in_port);
+             flow_extract(buffer, in_port, &skb_key.flow);
+             execute_actions(dp, buffer, in_port, &skb_key,
+                             ofm->actions, n_actions, false);
+         } else {
+             error = -ESRCH; 
+         }
+     }
+     return error;
+ error:
+     if (ntohl(ofm->buffer_id) != (uint32_t) -1)
+         discard_buffer(ntohl(ofm->buffer_id));
+     return error;
+ }
  static int
  recv_flow(struct datapath *dp, const struct sender *sender UNUSED,
            const void *msg)
  
      if (command == OFPFC_ADD) {
          return add_flow(dp, ofm);
+     } else if (command == OFPFC_MODIFY) {
+         return mod_flow(dp, ofm);
      }  else if (command == OFPFC_DELETE) {
          struct sw_flow_key key;
          flow_extract_match(&key, &ofm->match);
  }
  
  static int desc_stats_dump(struct datapath *dp, void *state,
 -                              struct buffer *buffer)
 +                              struct ofpbuf *buffer)
  {
 -    struct ofp_desc_stats *ods = buffer_put_uninit(buffer, sizeof *ods);
 +    struct ofp_desc_stats *ods = ofpbuf_put_uninit(buffer, sizeof *ods);
  
      strncpy(ods->mfr_desc, &mfr_desc, sizeof ods->mfr_desc);
      strncpy(ods->hw_desc, &hw_desc, sizeof ods->hw_desc);
@@@ -1258,7 -1308,7 +1311,7 @@@ struct flow_stats_state 
      struct ofp_flow_stats_request rq;
      time_t now;
  
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
  };
  
  #define MAX_FLOW_STATS_BYTES 4096
@@@ -1283,7 -1333,7 +1336,7 @@@ static int flow_stats_dump_callback(str
  }
  
  static int flow_stats_dump(struct datapath *dp, void *state,
 -                           struct buffer *buffer)
 +                           struct ofpbuf *buffer)
  {
      struct flow_stats_state *s = state;
      struct sw_flow_key match_key;
@@@ -1336,7 -1386,7 +1389,7 @@@ static int aggregate_stats_dump_callbac
  }
  
  static int aggregate_stats_dump(struct datapath *dp, void *state,
 -                                struct buffer *buffer)
 +                                struct ofpbuf *buffer)
  {
      struct aggregate_stats_state *s = state;
      struct ofp_aggregate_stats_request *rq = &s->rq;
      struct sw_flow_key match_key;
      int table_idx;
  
 -    rpy = buffer_put_uninit(buffer, sizeof *rpy);
 +    rpy = ofpbuf_put_uninit(buffer, sizeof *rpy);
      memset(rpy, 0, sizeof *rpy);
  
      flow_extract_match(&match_key, &rq->match);
@@@ -1378,11 -1428,11 +1431,11 @@@ static void aggregate_stats_done(void *
  }
  
  static int table_stats_dump(struct datapath *dp, void *state,
 -                            struct buffer *buffer)
 +                            struct ofpbuf *buffer)
  {
      int i;
      for (i = 0; i < dp->chain->n_tables; i++) {
 -        struct ofp_table_stats *ots = buffer_put_uninit(buffer, sizeof *ots);
 +        struct ofp_table_stats *ots = ofpbuf_put_uninit(buffer, sizeof *ots);
          struct sw_table_stats stats;
          dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
          strncpy(ots->name, stats.name, sizeof ots->name);
@@@ -1410,7 -1460,7 +1463,7 @@@ static int port_stats_init(struct datap
  }
  
  static int port_stats_dump(struct datapath *dp, void *state,
 -                           struct buffer *buffer)
 +                           struct ofpbuf *buffer)
  {
      struct port_stats_state *s = state;
      int i;
          if (!p->netdev) {
              continue;
          }
 -        ops = buffer_put_uninit(buffer, sizeof *ops);
 +        ops = ofpbuf_put_uninit(buffer, sizeof *ops);
          ops->port_no = htons(port_no(dp, p));
          memset(ops->pad, 0, sizeof ops->pad);
          ops->rx_packets   = htonll(p->rx_packets);
@@@ -1448,9 -1498,6 +1501,9 @@@ static void port_stats_done(void *state
  }
  
  struct stats_type {
 +    /* Value for 'type' member of struct ofp_stats_request. */
 +    int type;
 +
      /* Minimum and maximum acceptable number of bytes in body member of
       * struct ofp_stats_request. */
      size_t min_body, max_body;
       * struct ofp_stats_reply.  On success, it should return 1 if it should be
       * called again later with another buffer, 0 if it is done, or a negative
       * errno value on failure. */
 -    int (*dump)(struct datapath *dp, void *state, struct buffer *buffer);
 +    int (*dump)(struct datapath *dp, void *state, struct ofpbuf *buffer);
  
      /* Cleans any state created by the init or dump functions.  May be null
       * if no cleanup is required. */
  };
  
  static const struct stats_type stats[] = {
 -    [OFPST_DESC] = {
 +    {
 +        OFPST_DESC,
          0,
          0,
          NULL,
          desc_stats_dump,
          NULL
      },
 -    [OFPST_FLOW] = {
 +    {
 +        OFPST_FLOW,
          sizeof(struct ofp_flow_stats_request),
          sizeof(struct ofp_flow_stats_request),
          flow_stats_init,
          flow_stats_dump,
          flow_stats_done
      },
 -    [OFPST_AGGREGATE] = {
 +    {
 +        OFPST_AGGREGATE,
          sizeof(struct ofp_aggregate_stats_request),
          sizeof(struct ofp_aggregate_stats_request),
          aggregate_stats_init,
          aggregate_stats_dump,
          aggregate_stats_done
      },
 -    [OFPST_TABLE] = {
 +    {
 +        OFPST_TABLE,
          0,
          0,
          NULL,
          table_stats_dump,
          NULL
      },
 -    [OFPST_PORT] = {
 +    {
 +        OFPST_PORT,
          0,
          0,
          port_stats_init,
@@@ -1530,7 -1572,7 +1583,7 @@@ stats_dump(struct datapath *dp, void *c
  {
      struct stats_dump_cb *cb = cb_;
      struct ofp_stats_reply *osr;
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
      int err;
  
      if (cb->done) {
  
      osr = make_openflow_reply(sizeof *osr, OFPT_STATS_REPLY, &cb->sender,
                                &buffer);
 -    osr->type = htons(cb->s - stats);
 +    osr->type = htons(cb->s->type);
      osr->flags = 0;
  
      err = cb->s->dump(dp, cb->state, buffer);
              cb->done = true;
          } else {
              /* Buffer might have been reallocated, so find our data again. */
 -            osr = buffer_at_assert(buffer, 0, sizeof *osr);
 +            osr = ofpbuf_at_assert(buffer, 0, sizeof *osr);
              osr->flags = ntohs(OFPSF_REPLY_MORE);
          }
          err2 = send_openflow_buffer(dp, buffer, &cb->sender);
@@@ -1579,27 -1621,23 +1632,27 @@@ recv_stats_request(struct datapath *dp
  {
      const struct ofp_stats_request *rq = oh;
      size_t rq_len = ntohs(rq->header.length);
 +    const struct stats_type *st;
      struct stats_dump_cb *cb;
      int type, body_len;
      int err;
  
      type = ntohs(rq->type);
 -    if (type >= ARRAY_SIZE(stats) || !stats[type].dump) {
 -        dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_STAT,
 -                          rq, rq_len);
 -        VLOG_WARN_RL(&rl, "received stats request of unknown type %d", type);
 -        return -EINVAL;
 +    for (st = stats; ; st++) {
 +        if (st >= &stats[ARRAY_SIZE(stats)]) {
 +            VLOG_WARN_RL(&rl, "received stats request of unknown type %d",
 +                         type);
 +            return -EINVAL;
 +        } else if (type == st->type) {
 +            break;
 +        }
      }
  
      cb = xmalloc(sizeof *cb);
      cb->done = false;
      cb->rq = xmemdup(rq, rq_len);
      cb->sender = *sender;
 -    cb->s = &stats[type];
 +    cb->s = st;
      cb->state = NULL;
      
      body_len = rq_len - offsetof(struct ofp_stats_request, body);
  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:
 +        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. */
  #define OVERWRITE_SECS  1
  
  struct packet_buffer {
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
      uint32_t cookie;
      time_t timeout;
  };
  static struct packet_buffer buffers[N_PKT_BUFFERS];
  static unsigned int buffer_idx;
  
 -uint32_t save_buffer(struct buffer *buffer)
 +uint32_t save_buffer(struct ofpbuf *buffer)
  {
      struct packet_buffer *p;
      uint32_t id;
          if (time_now() < p->timeout) { /* FIXME */
              return -1;
          } else {
 -            buffer_delete(p->buffer); 
 +            ofpbuf_delete(p->buffer); 
          }
      }
      /* Don't use maximum cookie value since the all-bits-1 id is
       * special. */
      if (++p->cookie >= (1u << PKT_COOKIE_BITS) - 1)
          p->cookie = 0;
 -    p->buffer = buffer_clone(buffer);      /* FIXME */
 +    p->buffer = ofpbuf_clone(buffer);      /* FIXME */
      p->timeout = time_now() + OVERWRITE_SECS; /* FIXME */
      id = buffer_idx | (p->cookie << PKT_BUFFER_BITS);
  
      return id;
  }
  
 -static struct buffer *retrieve_buffer(uint32_t id)
 +static struct ofpbuf *retrieve_buffer(uint32_t id)
  {
 -    struct buffer *buffer = NULL;
 +    struct ofpbuf *buffer = NULL;
      struct packet_buffer *p;
  
      p = &buffers[id & PKT_BUFFER_MASK];
@@@ -1773,7 -1813,7 +1826,7 @@@ static void discard_buffer(uint32_t id
  
      p = &buffers[id & PKT_BUFFER_MASK];
      if (p->cookie == id >> PKT_BUFFER_BITS) {
 -        buffer_delete(p->buffer);
 +        ofpbuf_delete(p->buffer);
          p->buffer = NULL;
      }
  }
diff --combined switch/switch-flow.c
@@@ -37,7 -37,7 +37,7 @@@
  #include <assert.h>
  #include <stdlib.h>
  #include <string.h>
 -#include "buffer.h"
 +#include "ofpbuf.h"
  #include "openflow.h"
  #include "packets.h"
  #include "timeval.h"
@@@ -171,16 -171,18 +171,18 @@@ flow_fill_match(struct ofp_match* to, c
  struct sw_flow *
  flow_alloc(int n_actions)
  {
+     struct sw_flow_actions *sfa;
      struct sw_flow *flow = malloc(sizeof *flow);
      if (!flow)
          return NULL;
  
-     flow->n_actions = n_actions;
-     flow->actions = malloc(n_actions * sizeof *flow->actions);
-     if (!flow->actions && n_actions > 0) {
+     sfa = malloc(n_actions * sizeof sfa->actions[0]);
+     if (!sfa) {
          free(flow);
          return NULL;
      }
+     sfa->n_actions = n_actions;
+     flow->sf_acts = sfa;
      return flow;
  }
  
@@@ -191,12 -193,31 +193,31 @@@ flow_free(struct sw_flow *flow
      if (!flow) {
          return; 
      }
-     if (flow->actions) {
-         free(flow->actions);
-     }
+     free(flow->sf_acts);
      free(flow);
  }
  
+ /* Copies 'actions' into a newly allocated structure for use by 'flow'
+  * and frees the structure that defined the previous actions. */
+ void flow_replace_acts(struct sw_flow *flow, const struct ofp_action *actions,
+         int n_actions)
+ {
+     struct sw_flow_actions *sfa;
+     int size = sizeof *sfa + (n_actions * sizeof sfa->actions[0]);
+     sfa = malloc(size);
+     if (unlikely(!sfa))
+         return;
+     sfa->n_actions = n_actions;
+     memcpy(sfa->actions, actions, n_actions * sizeof sfa->actions[0]);
+     free(flow->sf_acts);
+     flow->sf_acts = sfa;
+     return;
+ }
  /* Prints a representation of 'key' to the kernel log. */
  void
  print_flow(const struct sw_flow_key *key)
@@@ -238,7 -259,7 +259,7 @@@ bool flow_timeout(struct sw_flow *flow
      }
  }
  
 -void flow_used(struct sw_flow *flow, struct buffer *buffer)
 +void flow_used(struct sw_flow *flow, struct ofpbuf *buffer)
  {
      flow->used = time_now();
      flow->packet_count++;
diff --combined switch/switch-flow.h
@@@ -35,6 -35,7 +35,7 @@@
  #define SWITCH_FLOW_H 1
  
  #include <time.h>
+ #include "openflow.h"
  #include "flow.h"
  #include "list.h"
  
@@@ -48,6 -49,11 +49,11 @@@ struct sw_flow_key 
      uint32_t nw_dst_mask;       /* 1-bit in each significant nw_dst bit. */
  };
  
+ struct sw_flow_actions {
+     unsigned int n_actions;
+     struct ofp_action actions[0];
+ };
  struct sw_flow {
      struct sw_flow_key key;
  
      uint64_t byte_count;        /* Number of bytes seen. */
      uint8_t reason;             /* Reason flow expired (one of OFPER_*). */
  
+     struct sw_flow_actions *sf_acts;
      /* Private to table implementations. */
      struct list node;
      struct list iter_node;
      unsigned long int serial;
-     /* Actions (XXX probably most flows have only a single action). */
-     unsigned int n_actions;
-     struct ofp_action *actions;
  };
  
  int flow_matches_1wild(const struct sw_flow_key *, const struct sw_flow_key *);
@@@ -77,11 -81,13 +81,13 @@@ int flow_del_matches(const struct sw_fl
  struct sw_flow *flow_alloc(int n_actions);
  void flow_free(struct sw_flow *);
  void flow_deferred_free(struct sw_flow *);
+ void flow_deferred_free_acts(struct sw_flow_actions *);
+ void flow_replace_acts(struct sw_flow *, const struct ofp_action *, int);
  void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from);
  void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
  
  void print_flow(const struct sw_flow_key *);
  bool flow_timeout(struct sw_flow *flow);
 -void flow_used(struct sw_flow *flow, struct buffer *buffer);
 +void flow_used(struct sw_flow *flow, struct ofpbuf *buffer);
  
  #endif /* switch-flow.h */
diff --combined switch/table-hash.c
@@@ -37,9 -37,8 +37,9 @@@
  #include <stdlib.h>
  #include <string.h>
  #include "crc32.h"
 -#include "flow.h"
  #include "datapath.h"
 +#include "flow.h"
 +#include "switch-flow.h"
  
  struct sw_table_hash {
      struct sw_table swt;
@@@ -98,6 -97,35 +98,35 @@@ static int table_hash_insert(struct sw_
      return retval;
  }
  
+ static int table_hash_modify(struct sw_table *swt, 
+         const struct sw_flow_key *key,
+         const struct ofp_action *actions, int n_actions) 
+ {
+     struct sw_table_hash *th = (struct sw_table_hash *) swt;
+     unsigned int count = 0;
+     if (key->wildcards == 0) {
+         struct sw_flow **bucket = find_bucket(swt, key);
+         struct sw_flow *flow = *bucket;
+         if (flow && flow_matches_1wild(&flow->key, key)) {
+             flow_replace_acts(flow, actions, n_actions);
+             count = 1;
+         }
+     } else {
+         unsigned int i;
+         for (i = 0; i <= th->bucket_mask; i++) {
+             struct sw_flow **bucket = &th->buckets[i];
+             struct sw_flow *flow = *bucket;
+             if (flow && flow_matches_1wild(&flow->key, key)) {
+                 flow_replace_acts(flow, actions, n_actions);
+                 count++;
+             }
+         }
+     }
+     return count;
+ }
  /* Caller must update n_flows. */
  static void
  do_delete(struct sw_flow **bucket)
@@@ -235,6 -263,7 +264,7 @@@ struct sw_table *table_hash_create(unsi
      swt = &th->swt;
      swt->lookup = table_hash_lookup;
      swt->insert = table_hash_insert;
+     swt->modify = table_hash_modify;
      swt->delete = table_hash_delete;
      swt->timeout = table_hash_timeout;
      swt->destroy = table_hash_destroy;
@@@ -276,6 -305,15 +306,15 @@@ static int table_hash2_insert(struct sw
      return table_hash_insert(t2->subtable[1], flow);
  }
  
+ static int table_hash2_modify(struct sw_table *swt, 
+         const struct sw_flow_key *key,
+         const struct ofp_action *actions, int n_actions) 
+ {
+     struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
+     return (table_hash_modify(t2->subtable[0], key, actions, n_actions)
+             + table_hash_modify(t2->subtable[1], key, actions, n_actions));
+ }
  static int table_hash2_delete(struct sw_table *swt,
                                const struct sw_flow_key *key, 
                                uint16_t priority, int strict)
@@@ -360,6 -398,7 +399,7 @@@ struct sw_table *table_hash2_create(uns
      swt = &t2->swt;
      swt->lookup = table_hash2_lookup;
      swt->insert = table_hash2_insert;
+     swt->modify = table_hash2_modify;
      swt->delete = table_hash2_delete;
      swt->timeout = table_hash2_timeout;
      swt->destroy = table_hash2_destroy;
diff --combined switch/table-linear.c
@@@ -36,7 -36,6 +36,7 @@@
  #include <stdlib.h>
  #include "flow.h"
  #include "list.h"
 +#include "openflow.h"
  #include "switch-flow.h"
  #include "datapath.h"
  
@@@ -106,6 -105,23 +106,23 @@@ static int table_linear_insert(struct s
      return 1;
  }
  
+ static int table_linear_modify(struct sw_table *swt,
+                 const struct sw_flow_key *key,
+                 const struct ofp_action *actions, int n_actions)
+ {
+     struct sw_table_linear *tl = (struct sw_table_linear *) swt;
+     struct sw_flow *flow;
+     unsigned int count = 0;
+     LIST_FOR_EACH (flow, struct sw_flow, node, &tl->flows) {
+         if (flow_matches_1wild(&flow->key, key)) {
+             flow_replace_acts(flow, actions, n_actions);
+             count++;
+         }
+     }
+     return count;
+ }
  static void
  do_delete(struct sw_flow *flow) 
  {
@@@ -208,6 -224,7 +225,7 @@@ struct sw_table *table_linear_create(un
      swt = &tl->swt;
      swt->lookup = table_linear_lookup;
      swt->insert = table_linear_insert;
+     swt->modify = table_linear_modify;
      swt->delete = table_linear_delete;
      swt->timeout = table_linear_timeout;
      swt->destroy = table_linear_destroy;
diff --combined utilities/dpctl.c
  #include "openflow-netlink.h"
  #endif
  
 -#include "buffer.h"
  #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"
@@@ -101,24 -101,24 +101,24 @@@ int main(int argc, char *argv[]
      argc -= optind;
      argv += optind;
      if (argc < 1)
 -        fatal(0, "missing command name; use --help for help");
 +        ofp_fatal(0, "missing command name; use --help for help");
  
      for (p = all_commands; p->name != NULL; p++) {
          if (!strcmp(p->name, argv[0])) {
              int n_arg = argc - 1;
              if (n_arg < p->min_args)
 -                fatal(0, "'%s' command requires at least %d arguments",
 -                      p->name, p->min_args);
 +                ofp_fatal(0, "'%s' command requires at least %d arguments",
 +                          p->name, p->min_args);
              else if (n_arg > p->max_args)
 -                fatal(0, "'%s' command takes at most %d arguments",
 -                      p->name, p->max_args);
 +                ofp_fatal(0, "'%s' command takes at most %d arguments",
 +                          p->name, p->max_args);
              else {
                  p->handler(argc, argv);
                  exit(0);
              }
          }
      }
 -    fatal(0, "unknown command '%s'; use --help for help", argv[0]);
 +    ofp_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
  
      return 0;
  }
@@@ -149,8 -149,8 +149,8 @@@ parse_options(int argc, char *argv[]
          case 't':
              timeout = strtoul(optarg, NULL, 10);
              if (timeout <= 0) {
 -                fatal(0, "value %s on -t or --timeout is not at least 1",
 -                      optarg);
 +                ofp_fatal(0, "value %s on -t or --timeout is not at least 1",
 +                          optarg);
              } else {
                  time_alarm(timeout);
              }
@@@ -204,7 -204,8 +204,8 @@@ usage(void
             "  dump-aggregate SWITCH FLOW  print aggregate stats for FLOWs\n"
             "  add-flow SWITCH FLOW        add flow described by FLOW\n"
             "  add-flows SWITCH FILE       add flows from FILE\n"
-            "  del-flows SWITCH FLOW       delete matching FLOWs\n"
+            "  mod-flows SWITCH FLOW       modify actions of matching FLOWs\n"
+            "  del-flows SWITCH [FLOW]     delete matching FLOWs\n"
             "  monitor SWITCH              print packets received from SWITCH\n"
             "\nFor local datapaths, remote switches, and controllers:\n"
             "  probe VCONN                 probe whether VCONN is up\n"
@@@ -265,7 -266,7 +266,7 @@@ static void open_nl_vconn(const char *n
      if (strncmp(name, "nl:", 3)
          || strlen(name) < 4
          || name[strspn(name + 3, "0123456789") + 3]) {
 -        fatal(0, "%s: argument is not of the form \"nl:DP_ID\"", name);
 +        ofp_fatal(0, "%s: argument is not of the form \"nl:DP_ID\"", name);
      }
      run(dpif_open(atoi(name + 3), subscribe, dpif), "opening datapath");
  }
@@@ -298,8 -299,8 +299,8 @@@ static void add_del_ports(int argc UNUS
      for (i = 2; i < argc; i++) {
          int retval = function(&dp, argv[i]);
          if (retval) {
 -            error(retval, "failed to %s %s %s %s",
 -                  operation, argv[i], preposition, argv[1]);
 +            ofp_error(retval, "failed to %s %s %s %s",
 +                      operation, argv[i], preposition, argv[1]);
              failure = true;
          }
      }
@@@ -335,7 -336,7 +336,7 @@@ open_vconn(const char *name, struct vco
  }
  
  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)
  }
  
  static void
 -send_openflow_buffer(struct vconn *vconn, struct buffer *buffer)
 +send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer)
  {
      update_openflow_length(buffer);
      run(vconn_send_block(vconn, buffer), "failed to send packet to switch");
  }
  
  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);
      open_vconn(vconn_name, &vconn);
  static void
  dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
  {
 -    struct buffer *request;
 +    struct ofpbuf *request;
      make_openflow(sizeof(struct ofp_header), request_type, &request);
      dump_transaction(vconn_name, request);
  }
  
  static void
 -dump_stats_transaction(const char *vconn_name, struct buffer *request)
 +dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
  {
      uint32_t send_xid = ((struct ofp_header *) request->data)->xid;
      struct vconn *vconn;
      send_openflow_buffer(vconn, request);
      while (!done) {
          uint32_t recv_xid;
 -        struct buffer *reply;
 +        struct ofpbuf *reply;
  
          run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
          recv_xid = ((struct ofp_header *) reply->data)->xid;
  
              ofp_print(stdout, reply->data, reply->size, 1);
  
 -            osr = buffer_at(reply, 0, sizeof *osr);
 +            osr = ofpbuf_at(reply, 0, sizeof *osr);
              done = !osr || !(ntohs(osr->flags) & OFPSF_REPLY_MORE);
          } else {
              VLOG_DBG("received reply with xid %08"PRIx32" "
                       "!= expected %08"PRIx32, recv_xid, send_xid);
          }
 -        buffer_delete(reply);
 +        ofpbuf_delete(reply);
      }
      vconn_close(vconn);
  }
  static void
  dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
  {
 -    struct buffer *request;
 +    struct ofpbuf *request;
      alloc_stats_request(0, stats_type, &request);
      dump_stats_transaction(vconn_name, request);
  }
@@@ -424,27 -425,27 +425,27 @@@ do_status(int argc, char *argv[]
  {
      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(b, argv[2], strlen(argv[2]));
      }
      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);
@@@ -472,7 -473,7 +473,7 @@@ str_to_int(const char *str
      errno = 0;
      value = strtoul(str, &tail, 0);
      if (errno == EINVAL || errno == ERANGE || *tail) {
 -        fatal(0, "invalid numeric format %s", str);
 +        ofp_fatal(0, "invalid numeric format %s", str);
      }
      return value;
  }
@@@ -482,7 -483,7 +483,7 @@@ str_to_mac(const char *str, uint8_t mac
  {
      if (sscanf(str, "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8,
                 &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) {
 -        fatal(0, "invalid mac address %s", str);
 +        ofp_fatal(0, "invalid mac address %s", str);
      }
  }
  
@@@ -498,7 -499,7 +499,7 @@@ str_to_ip(const char *str_, uint32_t *i
      name = strtok_r(str, "//", &save_ptr);
      retval = name ? lookup_ip(name, &in_addr) : EINVAL;
      if (retval) {
 -        fatal(0, "%s: could not convert to IP address", str);
 +        ofp_fatal(0, "%s: could not convert to IP address", str);
      }
      *ip = in_addr.s_addr;
  
              /* Verify that the rest of the bits are 1-bits. */
              for (; i < 32; i++) {
                  if (!(nm & (1u << i))) {
 -                    fatal(0, "%s: %s is not a valid netmask", str, netmask);
 +                    ofp_fatal(0, "%s: %s is not a valid netmask",
 +                              str, netmask);
                  }
              }
          } else {
              int prefix = atoi(netmask);
              if (prefix <= 0 || prefix > 32) {
 -                fatal(0, "%s: network prefix bits not between 1 and 32", str);
 +                ofp_fatal(0, "%s: network prefix bits not between 1 and 32",
 +                          str);
              }
              n_wild = 32 - prefix;
          }
@@@ -596,7 -595,7 +597,7 @@@ str_to_action(char *str, struct ofp_act
          } else if (strspn(act, "0123456789") == strlen(act)) {
              port = str_to_int(act);
          } else {
 -            fatal(0, "Unknown action: %s", act);
 +            ofp_fatal(0, "Unknown action: %s", act);
          }
  
          if (port != OFPP_MAX) {
@@@ -697,13 -696,13 +698,13 @@@ str_to_flow(char *string, struct ofp_ma
      if (action) {
          char *act_str = strstr(string, "action");
          if (!act_str) {
 -            fatal(0, "must specify an action");
 +            ofp_fatal(0, "must specify an action");
          }
          *(act_str-1) = '\0';
  
          act_str = strchr(act_str, '=');
          if (!act_str) {
 -            fatal(0, "must specify an action");
 +            ofp_fatal(0, "must specify an action");
          }
  
          act_str++;
  
              value = strtok(NULL, ", \t\r\n");
              if (!value) {
 -                fatal(0, "field %s missing value", name);
 +                ofp_fatal(0, "field %s missing value", name);
              }
          
              if (table_idx && !strcmp(name, "table")) {
                      }
                  }
              } else {
 -                fatal(0, "unknown keyword %s", name);
 +                ofp_fatal(0, "unknown keyword %s", name);
              }
          }
      }
  static void do_dump_flows(int argc, char *argv[])
  {
      struct ofp_flow_stats_request *req;
 -    struct buffer *request;
 +    struct ofpbuf *request;
  
      req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
      str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0, 
  static void do_dump_aggregate(int argc, char *argv[])
  {
      struct ofp_aggregate_stats_request *req;
 -    struct buffer *request;
 +    struct ofpbuf *request;
  
      req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
      str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
  static void do_add_flow(int argc, char *argv[])
  {
      struct vconn *vconn;
 -    struct buffer *buffer;
 +    struct ofpbuf *buffer;
      struct ofp_flow_mod *ofm;
      uint16_t priority, idle_timeout, hard_timeout;
      size_t size;
      int n_actions = MAX_ADD_ACTS;
  
-     open_vconn(argv[1], &vconn);
      /* Parse and send. */
      size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
      ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
      ofm->priority = htons(priority);
      ofm->reserved = htonl(0);
  
 -    /* xxx Should we use the buffer library? */
 +    /* xxx Should we use the ofpbuf library? */
      buffer->size -= (MAX_ADD_ACTS - n_actions) * sizeof ofm->actions[0];
  
+     open_vconn(argv[1], &vconn);
      send_openflow_buffer(vconn, buffer);
      vconn_close(vconn);
  }
  static void do_add_flows(int argc, char *argv[])
  {
      struct vconn *vconn;
      FILE *file;
      char line[1024];
  
      file = fopen(argv[2], "r");
      if (file == NULL) {
 -        fatal(errno, "%s: open", argv[2]);
 +        ofp_fatal(errno, "%s: open", argv[2]);
      }
  
      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;
          ofm->priority = htons(priority);
          ofm->reserved = htonl(0);
  
 -        /* xxx Should we use the buffer library? */
 +        /* xxx Should we use the ofpbuf library? */
          buffer->size -= (MAX_ADD_ACTS - n_actions) * sizeof ofm->actions[0];
  
          send_openflow_buffer(vconn, buffer);
      fclose(file);
  }
  
- static void do_del_flows(int argc, char *argv[])
+ static void do_mod_flows(int argc, char *argv[])
  {
+     uint16_t idle_timeout, hard_timeout;
      struct vconn *vconn;
-     uint16_t priority;
 -    struct buffer *buffer;
++    struct ofpbuf *buffer;
+     struct ofp_flow_mod *ofm;
+     size_t size;
+     int n_actions = MAX_ADD_ACTS;
+     /* Parse and send. */
+     size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
+     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
+     str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &n_actions, 
+                 NULL, NULL, &idle_timeout, &hard_timeout);
+     ofm->command = htons(OFPFC_MODIFY);
+     ofm->idle_timeout = htons(idle_timeout);
+     ofm->hard_timeout = htons(hard_timeout);
+     ofm->buffer_id = htonl(UINT32_MAX);
+     ofm->priority = htons(0);
+     ofm->reserved = htonl(0);
+     /* xxx Should we use the buffer library? */
+     buffer->size -= (MAX_ADD_ACTS - n_actions) * sizeof ofm->actions[0];
  
      open_vconn(argv[1], &vconn);
 -    struct buffer *buffer;
+     send_openflow_buffer(vconn, buffer);
+     vconn_close(vconn);
+ }
+ static void do_del_flows(int argc, char *argv[])
+ {
+     struct vconn *vconn;
+     uint16_t priority;
 +    struct ofpbuf *buffer;
      struct ofp_flow_mod *ofm;
      size_t size;
  
      /* Parse and send. */
      size = sizeof *ofm;
      ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
      ofm->priority = htons(priority);
      ofm->reserved = htonl(0);
  
+     open_vconn(argv[1], &vconn);
      send_openflow_buffer(vconn, buffer);
      vconn_close(vconn);
  }
  
@@@ -920,10 -943,10 +945,10 @@@ do_monitor(int argc UNUSED, char *argv[
      }
      open_vconn(argv[1], &vconn);
      for (;;) {
 -        struct buffer *b;
 +        struct ofpbuf *b;
          run(vconn_recv_block(vconn, &b), "vconn_recv");
          ofp_print(stderr, b->data, b->size, 2);
 -        buffer_delete(b);
 +        ofpbuf_delete(b);
      }
  }
  
@@@ -936,24 -959,24 +961,24 @@@ 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);
      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);
  }
  
  static void
  do_mod_port(int argc, char *argv[])
  {
 -    struct buffer *request, *reply;
 +    struct ofpbuf *request, *reply;
      struct ofp_switch_features *osf;
      struct ofp_port_mod *opm;
      struct vconn *vconn;
          }
      }
      if (port_idx == n_ports) {
 -        fatal(0, "couldn't find monitored port: %s", argv[2]);
 +        ofp_fatal(0, "couldn't find monitored port: %s", argv[2]);
      }
  
      opm = make_openflow(sizeof(struct ofp_port_mod), OFPT_PORT_MOD, &request);
          opm->mask |= htonl(OFPPFL_NO_FLOOD);
          opm->desc.flags |= htonl(OFPPFL_NO_FLOOD);
      } else {
 -        fatal(0, "unknown mod-port command '%s'", argv[3]);
 +        ofp_fatal(0, "unknown mod-port command '%s'", argv[3]);
      }
  
      send_openflow_buffer(vconn, request);
  
 -    buffer_delete(reply);
 +    ofpbuf_delete(reply);
      vconn_close(vconn);
  }
  
@@@ -1038,13 -1061,13 +1063,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);
      }
  
      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,
          random_bytes(rq_hdr + 1, payload);
  
          gettimeofday(&start, NULL);
 -        run(vconn_transact(vconn, buffer_clone(request), &reply), "transact");
 +        run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact");
          gettimeofday(&end, NULL);
  
          rpy_hdr = reply->data;
                 reply->size - sizeof *rpy_hdr, argv[1], rpy_hdr->xid,
                     (1000*(double)(end.tv_sec - start.tv_sec))
                     + (.001*(end.tv_usec - start.tv_usec)));
 -        buffer_delete(request);
 -        buffer_delete(reply);
 +        ofpbuf_delete(request);
 +        ofpbuf_delete(reply);
      }
      vconn_close(vconn);
  }
@@@ -1088,7 -1111,7 +1113,7 @@@ do_benchmark(int argc, char *argv[]
  
      payload_size = atoi(argv[2]);
      if (payload_size > 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);
      }
      message_size = sizeof(struct ofp_header) + payload_size;
  
      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);
          memset(rq_hdr + 1, 0, payload_size);
          run(vconn_transact(vconn, request, &reply), "transact");
 -        buffer_delete(reply);
 +        ofpbuf_delete(reply);
      }
      gettimeofday(&end, NULL);
      vconn_close(vconn);
@@@ -1142,6 -1165,7 +1167,7 @@@ static struct command all_commands[] = 
      { "dump-aggregate", 1, 2, do_dump_aggregate },
      { "add-flow", 2, 2, do_add_flow },
      { "add-flows", 2, 2, do_add_flows },
+     { "mod-flows", 2, 2, do_mod_flows },
      { "del-flows", 1, 2, do_del_flows },
      { "dump-ports", 1, 1, do_dump_ports },
      { "mod-port", 3, 3, do_mod_port },