openflow-1.1+: OFPT_TABLE_MOD (part 1)
authorAndy Zhou <azhou@nicira.com>
Sat, 7 Sep 2013 10:02:32 +0000 (03:02 -0700)
committerBen Pfaff <blp@nicira.com>
Sat, 7 Sep 2013 18:01:33 +0000 (11:01 -0700)
Added infrastructure to support Openflow OFPT_TABLE_MOD message. This patch
does not include the flexible table miss handling code that is necessary to
support the semantics specified in OFPT_TABLE_MOD messages.

Current flow miss behavior continues to conform to Openflow 1.0.  Future
commits to add more flexible table miss support are needed to fully support
OPFT_TABLE_MOD for Openflow-1.1+.

Signed-off-by: Andy Zhou <azhou@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
13 files changed:
include/openflow/openflow-1.1.h
lib/learning-switch.c
lib/ofp-msgs.h
lib/ofp-parse.c
lib/ofp-parse.h
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
lib/rconn.c
ofproto/ofproto.c
tests/ofp-print.at
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c

index a2309b1..de88ffa 100644 (file)
@@ -447,11 +447,11 @@ OFP_ASSERT(sizeof(struct ofp11_table_mod) == 8);
    These flags are used in ofp_table_stats messages to describe the current
    configuration and in ofp_table_mod messages to configure table behavior.  */
 enum ofp11_table_config {
-    OFPTC11_TABLE_MISS_CONTROLLER = 0,    /* Send to controller. */
+    OFPTC11_TABLE_MISS_CONTROLLER = 0 << 0, /* Send to controller. */
     OFPTC11_TABLE_MISS_CONTINUE = 1 << 0, /* Continue to the next table in the
                                              pipeline (OpenFlow 1.0
                                              behavior). */
-    OFPTC11_TABLE_MISS_DROP = 1 << 1,     /* Drop the packet. */
+    OFPTC11_TABLE_MISS_DROP = 2 << 0,     /* Drop the packet. */
     OFPTC11_TABLE_MISS_MASK = 3
 };
 
index c587930..76ec3e1 100644 (file)
@@ -348,6 +348,7 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MOD:
     case OFPTYPE_GROUP_MOD:
     case OFPTYPE_PORT_MOD:
+    case OFPTYPE_TABLE_MOD:
     case OFPTYPE_BARRIER_REQUEST:
     case OFPTYPE_BARRIER_REPLY:
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
index 47256de..bfc84f3 100644 (file)
@@ -185,6 +185,9 @@ enum ofpraw {
     /* OFPT 1.1+ (16): struct ofp11_port_mod. */
     OFPRAW_OFPT11_PORT_MOD,
 
+    /* OFPT 1.1+ (17): struct ofp11_table_mod. */
+    OFPRAW_OFPT11_TABLE_MOD,
+
     /* OFPT 1.0 (18): void. */
     OFPRAW_OFPT10_BARRIER_REQUEST,
     /* OFPT 1.1+ (20): void. */
@@ -466,6 +469,7 @@ enum ofptype {
     OFPTYPE_GROUP_MOD,           /* OFPRAW_OFPT11_GROUP_MOD. */
     OFPTYPE_PORT_MOD,            /* OFPRAW_OFPT10_PORT_MOD.
                                   * OFPRAW_OFPT11_PORT_MOD. */
+    OFPTYPE_TABLE_MOD,           /* OFPRAW_OFPT11_TABLE_MOD. */
 
     /* Barrier messages. */
     OFPTYPE_BARRIER_REQUEST,     /* OFPRAW_OFPT10_BARRIER_REQUEST.
index ad3981c..a61beb9 100644 (file)
@@ -1706,6 +1706,47 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
     return error;
 }
 
+/* Convert 'table_id' and 'flow_miss_handling' (as described for the
+ * "mod-table" command in the ovs-ofctl man page) into 'tm' for sending the
+ * specified table_mod 'command' to a switch.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
+parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
+                    const char *flow_miss_handling,
+                    enum ofputil_protocol *usable_protocols)
+{
+    /* Table mod requires at least OF 1.1. */
+    *usable_protocols = OFPUTIL_P_OF11_UP;
+
+    if (!strcasecmp(table_id, "all")) {
+        tm->table_id = 255;
+    } else {
+        char *error = str_to_u8(table_id, "table_id", &tm->table_id);
+        if (error) {
+            return error;
+        }
+    }
+
+    if (strcmp(flow_miss_handling, "controller") == 0) {
+        tm->config = OFPTC11_TABLE_MISS_CONTROLLER;
+    } else if (strcmp(flow_miss_handling, "continue") == 0) {
+        tm->config = OFPTC11_TABLE_MISS_CONTINUE;
+    } else if (strcmp(flow_miss_handling, "drop") == 0) {
+        tm->config = OFPTC11_TABLE_MISS_DROP;
+    } else {
+        return xasprintf("invalid flow_miss_handling %s", flow_miss_handling);
+    }
+
+    if (tm->table_id == 0xfe && tm->config == OFPTC11_TABLE_MISS_CONTINUE) {
+        return xstrdup("last table's flow miss handling can not be continue");
+    }
+
+    return NULL;
+}
+
+
 /* Opens file 'file_name' and reads each line as a flow_mod of the specified
  * type (one of OFPFC_*).  Stores each flow_mod in '*fm', an array allocated
  * on the caller's behalf, and the number of flow_mods in '*n_fms'.
index 1c3228a..47ba036 100644 (file)
@@ -31,6 +31,7 @@ struct ofputil_flow_monitor_request;
 struct ofputil_flow_stats_request;
 struct ofputil_group_mod;
 struct ofputil_meter_mod;
+struct ofputil_table_mod;
 enum ofputil_protocol;
 
 char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
@@ -41,6 +42,12 @@ char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
                              uint16_t command,
                              enum ofputil_protocol *usable_protocols)
     WARN_UNUSED_RESULT;
+
+char *parse_ofp_table_mod(struct ofputil_table_mod *,
+                          const char *table_id, const char *flow_miss_handling,
+                          enum ofputil_protocol *usable_protocols)
+    WARN_UNUSED_RESULT;
+
 char *parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
                               struct ofputil_flow_mod **fms, size_t *n_fms,
                               enum ofputil_protocol *usable_protocols)
index b280a37..c0553af 100644 (file)
@@ -964,6 +964,49 @@ ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
     }
 }
 
+static void
+ofp_print_table_miss_config(struct ds *string, const uint32_t config)
+{
+    uint32_t table_miss_config = config & OFPTC11_TABLE_MISS_MASK;
+
+    switch (table_miss_config) {
+    case OFPTC11_TABLE_MISS_CONTROLLER:
+        ds_put_cstr(string, "controller\n");
+        break;
+    case OFPTC11_TABLE_MISS_CONTINUE:
+        ds_put_cstr(string, "continue\n");
+        break;
+    case OFPTC11_TABLE_MISS_DROP:
+        ds_put_cstr(string, "drop\n");
+        break;
+    default:
+        ds_put_cstr(string, "Unknown\n");
+        break;
+    }
+}
+
+static void
+ofp_print_table_mod(struct ds *string, const struct ofp_header *oh)
+{
+    struct ofputil_table_mod pm;
+    enum ofperr error;
+
+    error = ofputil_decode_table_mod(oh, &pm);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
+
+    if (pm.table_id == 0xff) {
+        ds_put_cstr(string, " table_id: ALL_TABLES");
+    } else {
+        ds_put_format(string, " table_id=%"PRIu8, pm.table_id);
+    }
+
+    ds_put_cstr(string, ", flow_miss_config=");
+    ofp_print_table_miss_config(string, pm.config);
+}
+
 static void
 ofp_print_meter_flags(struct ds *s, uint16_t flags)
 {
@@ -2431,6 +2474,10 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_port_mod(string, oh);
         break;
 
+    case OFPTYPE_TABLE_MOD:
+        ofp_print_table_mod(string, oh);
+        break;
+
     case OFPTYPE_METER_MOD:
         ofp_print_meter_mod(string, oh);
         break;
index 8ac9186..23c7136 100644 (file)
@@ -3660,7 +3660,7 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps,
     ofpmsg_update_length(b);
     return b;
 }
-\f
+
 /* ofputil_port_mod */
 
 /* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
@@ -3742,7 +3742,66 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
         break;
     }
+    default:
+        NOT_REACHED();
+    }
+
+    return b;
+}
+
+/* ofputil_table_mod */
+
+/* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
+ * '*pm'.  Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_table_mod(const struct ofp_header *oh,
+                         struct ofputil_table_mod *pm)
+{
+    enum ofpraw raw;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    if (raw == OFPRAW_OFPT11_TABLE_MOD) {
+        const struct ofp11_table_mod *otm = b.data;
+
+        pm->table_id = otm->table_id;
+        pm->config = ntohl(otm->config);
+    } else {
+        return OFPERR_OFPBRC_BAD_TYPE;
+    }
 
+    return 0;
+}
+
+/* Converts the abstract form of a "table mod" message in '*pm' into an OpenFlow
+ * message suitable for 'protocol', and returns that encoded form in a buffer
+ * owned by the caller. */
+struct ofpbuf *
+ofputil_encode_table_mod(const struct ofputil_table_mod *pm,
+                        enum ofputil_protocol protocol)
+{
+    enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
+    struct ofpbuf *b;
+
+    switch (ofp_version) {
+    case OFP10_VERSION: {
+        ovs_fatal(0, "table mod needs OpenFlow 1.1 or later "
+                     "(\'-O OpenFlow11\')");
+        break;
+    }
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
+        struct ofp11_table_mod *otm;
+
+        b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0);
+        otm = ofpbuf_put_zeros(b, sizeof *otm);
+        otm->table_id = pm->table_id;
+        otm->config = htonl(pm->config);
+        break;
+    }
     default:
         NOT_REACHED();
     }
index eae8ed5..0ca483c 100644 (file)
@@ -565,6 +565,17 @@ enum ofperr ofputil_decode_port_mod(const struct ofp_header *,
 struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *,
                                        enum ofputil_protocol);
 
+/* Abstract ofp_table_mod. */
+struct ofputil_table_mod {
+    uint8_t table_id;         /* ID of the table, 0xff indicates all tables. */
+    uint32_t config;
+};
+
+enum ofperr ofputil_decode_table_mod(const struct ofp_header *,
+                                    struct ofputil_table_mod *);
+struct ofpbuf *ofputil_encode_table_mod(const struct ofputil_table_mod *,
+                                       enum ofputil_protocol);
+
 /* Meter band configuration for all supported band types. */
 struct ofputil_meter_band {
     uint16_t type;
index 17cbd82..64cc6d0 100644 (file)
@@ -1156,6 +1156,7 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_FLOW_MOD:
     case OFPTYPE_GROUP_MOD:
     case OFPTYPE_PORT_MOD:
+    case OFPTYPE_TABLE_MOD:
     case OFPTYPE_METER_MOD:
     case OFPTYPE_BARRIER_REQUEST:
     case OFPTYPE_BARRIER_REPLY:
index 6900e99..4d33de7 100644 (file)
@@ -5144,6 +5144,26 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     }
 }
 
+static enum ofperr
+handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    struct ofputil_table_mod tm;
+    enum ofperr error;
+
+    error = reject_slave_controller(ofconn);
+    if (error) {
+        return error;
+    }
+
+    error = ofputil_decode_table_mod(oh, &tm);
+    if (error) {
+        return error;
+    }
+
+    /* XXX Actual table mod support is not implemented yet. */
+    return 0;
+}
+
 static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 {
@@ -5182,6 +5202,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_GROUP_MOD:
         return handle_group_mod(ofconn, oh);
 
+    case OFPTYPE_TABLE_MOD:
+        return handle_table_mod(ofconn, oh);
+
     case OFPTYPE_METER_MOD:
         return handle_meter_mod(ofconn, oh);
 
index 4a62793..52b84ef 100644 (file)
@@ -978,6 +978,33 @@ OFPT_PORT_MOD (OF1.3) (xid=0x3):port: 3: addr:50:54:00:00:00:01
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_TABLE_MOD - OF1.1])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+02 11 00 10 00 00 00 02 02 00 00 00 00 00 00 02 \
+" 3], [0], [dnl
+OFPT_TABLE_MOD (OF1.1) (xid=0x2): table_id=2, flow_miss_config=drop
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_TABLE_MOD - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 11 00 10 00 00 00 02 02 00 00 00 00 00 00 01 \
+" 3], [0], [dnl
+OFPT_TABLE_MOD (OF1.2) (xid=0x2): table_id=2, flow_miss_config=continue
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_TABLE_MOD - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 11 00 10 00 00 00 02 02 00 00 00 00 00 00 00 \
+" 3], [0], [dnl
+OFPT_TABLE_MOD (OF1.3) (xid=0x2): table_id=2, flow_miss_config=controller
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_DESC request])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100000000"], [0], [dnl
index 4859ab9..3141eff 100644 (file)
@@ -213,6 +213,31 @@ Prints to the console statistics for the specified \fIgroups in the
 groups are printed.  See \fBGroup Syntax\fR, below, for the syntax of
 \fIgroups\fR.
 .
+.IP "\fBmod\-table \fIswitch\fR \fItable_id\fR  \fIflow_miss_handling\fR"
+An OpenFlow 1.0 switch looks up each packet that arrives at the switch
+in table 0, then in table 1 if there is no match in table 0, then in
+table 2, and so on until the packet finds a match in some table.
+Finally, if no match was found, the switch sends the packet to the
+controller
+.IP
+OpenFlow 1.1 and later offer more flexibility.  This command
+configures the flow table miss handling configuration for table
+\fItable_id\fR in \fIswitch\fR.  \fItable_id\fR may be an OpenFlow
+table number between 0 and 254, inclusive, or the keyword \fBALL\fR to
+modify all tables.  \fIflow_miss_handling\fR may be any one of the
+following:
+.RS
+.IP \fBdrop\fR
+Drop the packet.
+.IP \fBcontinue\fR
+Continue to the next table in the pipeline.  (This is how an OpenFlow
+1.0 switch always handles packets that do not match any flow, in
+tables other than the last one.)
+.IP \fBcontroller\fR
+Send to controller.  (This is how an OpenFlow 1.0 switch always
+handles packets that do not match any flow in the last table.)
+.RE
+.
 .SS "OpenFlow Switch Flow Table Commands"
 .
 These commands manage the flow table in an OpenFlow switch.  In each
index 9294752..95bf1bf 100644 (file)
@@ -281,6 +281,7 @@ usage(void)
            "  dump-desc SWITCH            print switch description\n"
            "  dump-tables SWITCH          print table stats\n"
            "  mod-port SWITCH IFACE ACT   modify port behavior\n"
+           "  mod-table SWITCH MOD        modify flow table behavior\n"
            "  get-frags SWITCH            print fragment handling behavior\n"
            "  set-frags SWITCH FRAG_MODE  set fragment handling behavior\n"
            "  dump-ports SWITCH [PORT]    print port statistics\n"
@@ -1663,6 +1664,42 @@ found:
     vconn_close(vconn);
 }
 
+static void
+ofctl_mod_table(int argc OVS_UNUSED, char *argv[])
+{
+    enum ofputil_protocol protocol, usable_protocols;
+    struct ofputil_table_mod tm;
+    struct vconn *vconn;
+    char *error;
+    int i;
+
+    error = parse_ofp_table_mod(&tm, argv[2], argv[3], &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+
+    protocol = open_vconn(argv[1], &vconn);
+    if (!(protocol & usable_protocols)) {
+        for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+            enum ofputil_protocol f = 1 << i;
+            if (f != protocol
+                && f & usable_protocols
+                && try_set_protocol(vconn, f, &protocol)) {
+                protocol = f;
+                break;
+            }
+        }
+    }
+
+    if (!(protocol & usable_protocols)) {
+        char *usable_s = ofputil_protocols_to_string(usable_protocols);
+        ovs_fatal(0, "Switch does not support table mod message(%s)", usable_s);
+    }
+
+    transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
+    vconn_close(vconn);
+}
+
 static void
 ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
 {
@@ -3152,6 +3189,7 @@ static const struct command all_commands[] = {
     { "dump-ports", 1, 2, ofctl_dump_ports },
     { "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
     { "mod-port", 3, 3, ofctl_mod_port },
+    { "mod-table", 3, 3, ofctl_mod_table },
     { "get-frags", 1, 1, ofctl_get_frags },
     { "set-frags", 2, 2, ofctl_set_frags },
     { "ofp-parse", 1, 1, ofctl_ofp_parse },