Implement new fragment handling policy.
[openvswitch] / utilities / ovs-ofctl.c
index 7511769421093711fa353d4eb9999e0cc9871728..ce9723b91eec68bc07b83f6a3917c888f4651429 100644 (file)
@@ -55,6 +55,10 @@ VLOG_DEFINE_THIS_MODULE(ofctl);
 /* --strict: Use strict matching for flow mod commands? */
 static bool strict;
 
+/* --readd: If ture, on replace-flows, re-add even flows that have not changed
+ * (to reset flow counters). */
+static bool readd;
+
 /* -F, --flow-format: Flow format to use.  Either one of NXFF_* to force a
  * particular flow format or -1 to let ovs-ofctl choose intelligently. */
 static int preferred_flow_format = -1;
@@ -82,11 +86,13 @@ parse_options(int argc, char *argv[])
 {
     enum {
         OPT_STRICT = UCHAR_MAX + 1,
+        OPT_READD,
         VLOG_OPTION_ENUMS
     };
     static struct option long_options[] = {
         {"timeout", required_argument, NULL, 't'},
         {"strict", no_argument, NULL, OPT_STRICT},
+        {"readd", no_argument, NULL, OPT_READD},
         {"flow-format", required_argument, NULL, 'F'},
         {"more", no_argument, NULL, 'm'},
         {"help", no_argument, NULL, 'h'},
@@ -132,13 +138,17 @@ parse_options(int argc, char *argv[])
             usage();
 
         case 'V':
-            OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION);
+            ovs_print_version(OFP_VERSION, OFP_VERSION);
             exit(EXIT_SUCCESS);
 
         case OPT_STRICT:
             strict = true;
             break;
 
+        case OPT_READD:
+            readd = true;
+            break;
+
         VLOG_OPTION_HANDLERS
         STREAM_SSL_OPTION_HANDLERS
 
@@ -162,6 +172,8 @@ 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"
+           "  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"
            "  dump-flows SWITCH           print all flow entries\n"
            "  dump-flows SWITCH FLOW      print matching FLOWs\n"
@@ -172,6 +184,7 @@ usage(void)
            "  add-flows SWITCH FILE       add flows from FILE\n"
            "  mod-flows SWITCH FLOW       modify actions of matching FLOWs\n"
            "  del-flows SWITCH [FLOW]     delete matching FLOWs\n"
+           "  replace-flows SWITCH FILE   replace flows with those in FILE\n"
            "  monitor SWITCH [MISSLEN]    print packets received from SWITCH\n"
            "\nFor OpenFlow switches and controllers:\n"
            "  probe VCONN                 probe whether VCONN is up\n"
@@ -183,6 +196,7 @@ usage(void)
     vlog_usage();
     printf("\nOther options:\n"
            "  --strict                    use strict match for flow commands\n"
+           "  --readd                     replace flows that haven't changed\n"
            "  -F, --flow-format=FORMAT    force particular flow format\n"
            "  -m, --more                  be more verbose printing OpenFlow\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
@@ -232,7 +246,7 @@ open_vconn__(const char *name, const char *default_suffix,
     free(datapath_name);
     free(datapath_type);
 
-    if (strstr(name, ":")) {
+    if (strchr(name, ':')) {
         run(vconn_open_block(name, OFP_VERSION, vconnp),
             "connecting to %s", name);
     } else if (!stat(name, &s) && S_ISSOCK(s.st_mode)) {
@@ -246,7 +260,7 @@ open_vconn__(const char *name, const char *default_suffix,
         }
         open_vconn_socket(socket_name, vconnp);
     } else {
-        ovs_fatal(0, "%s is not a valid connection method", name);
+        ovs_fatal(0, "%s is not a bridge or a socket", name);
     }
 
     free(bridge_path);
@@ -260,14 +274,14 @@ open_vconn(const char *name, struct vconn **vconnp)
 }
 
 static void *
-alloc_stats_request(size_t body_len, uint16_t type, struct ofpbuf **bufferp)
+alloc_stats_request(size_t rq_len, uint16_t type, struct ofpbuf **bufferp)
 {
     struct ofp_stats_msg *rq;
-    rq = make_openflow((offsetof(struct ofp_stats_msg, body)
-                        + body_len), OFPT_STATS_REQUEST, bufferp);
+
+    rq = make_openflow(rq_len, OFPT_STATS_REQUEST, bufferp);
     rq->type = htons(type);
     rq->flags = htons(0);
-    return rq->body;
+    return rq;
 }
 
 static void
@@ -333,13 +347,15 @@ static void
 dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
 {
     struct ofpbuf *request;
-    alloc_stats_request(0, stats_type, &request);
+    alloc_stats_request(sizeof(struct ofp_stats_msg), stats_type, &request);
     dump_stats_transaction(vconn_name, request);
 }
 
 /* Sends 'request', which should be a request that only has a reply if an error
  * occurs, and waits for it to succeed or fail.  If an error does occur, prints
- * it and exits with an error. */
+ * it and exits with an error.
+ *
+ * Destroys all of the 'requests'. */
 static void
 transact_multiple_noreply(struct vconn *vconn, struct list *requests)
 {
@@ -360,7 +376,9 @@ transact_multiple_noreply(struct vconn *vconn, struct list *requests)
 
 /* Sends 'request', which should be a request that only has a reply if an error
  * occurs, and waits for it to succeed or fail.  If an error does occur, prints
- * it and exits with an error. */
+ * it and exits with an error.
+ *
+ * Destroys 'request'. */
 static void
 transact_noreply(struct vconn *vconn, struct ofpbuf *request)
 {
@@ -371,6 +389,44 @@ transact_noreply(struct vconn *vconn, struct ofpbuf *request)
     transact_multiple_noreply(vconn, &requests);
 }
 
+static void
+fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
+{
+    struct ofp_switch_config *config;
+    struct ofp_header *header;
+    struct ofpbuf *request;
+    struct ofpbuf *reply;
+
+    make_openflow(sizeof(struct ofp_header), OFPT_GET_CONFIG_REQUEST,
+                  &request);
+    run(vconn_transact(vconn, request, &reply),
+        "talking to %s", vconn_get_name(vconn));
+
+    header = reply->data;
+    if (header->type != OFPT_GET_CONFIG_REPLY ||
+        header->length != htons(sizeof *config)) {
+        ovs_fatal(0, "%s: bad reply to config request", vconn_get_name(vconn));
+    }
+
+    config = reply->data;
+    *config_ = *config;
+}
+
+static void
+set_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
+{
+    struct ofp_switch_config *config;
+    struct ofp_header save_header;
+    struct ofpbuf *request;
+
+    config = make_openflow(sizeof *config, OFPT_SET_CONFIG, &request);
+    save_header = config->header;
+    *config = *config_;
+    config->header = save_header;
+
+    transact_noreply(vconn, request);
+}
+
 static void
 do_show(int argc OVS_UNUSED, char *argv[])
 {
@@ -522,7 +578,7 @@ static void
 do_dump_flows__(int argc, char *argv[], bool aggregate)
 {
     enum nx_flow_format min_flow_format, flow_format;
-    struct flow_stats_request fsr;
+    struct ofputil_flow_stats_request fsr;
     struct ofpbuf *request;
     struct vconn *vconn;
 
@@ -658,7 +714,7 @@ do_flow_mod__(int argc, char *argv[], uint16_t command)
     flow_mod_table_id = false;
 
     parse_ofp_flow_mod_str(&requests, &flow_format, &flow_mod_table_id,
-                           argc > 2 ? argv[2] : "", command);
+                           argc > 2 ? argv[2] : "", command, false);
     check_final_format_for_flow_mod(flow_format);
 
     open_vconn(argv[1], &vconn);
@@ -708,13 +764,11 @@ do_monitor(int argc, char *argv[])
 
     open_vconn(argv[1], &vconn);
     if (argc > 2) {
-        int miss_send_len = atoi(argv[2]);
-        struct ofp_switch_config *osc;
-        struct ofpbuf *buf;
+        struct ofp_switch_config config;
 
-        osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf);
-        osc->miss_send_len = htons(miss_send_len);
-        transact_noreply(vconn, buf);
+        fetch_switch_config(vconn, &config);
+        config.miss_send_len = htons(atoi(argv[2]));
+        set_switch_config(vconn, &config);
     }
     monitor_vconn(vconn);
 }
@@ -794,6 +848,51 @@ do_mod_port(int argc OVS_UNUSED, char *argv[])
     vconn_close(vconn);
 }
 
+static void
+do_get_frags(int argc OVS_UNUSED, char *argv[])
+{
+    struct ofp_switch_config config;
+    struct vconn *vconn;
+
+    open_vconn(argv[1], &vconn);
+    fetch_switch_config(vconn, &config);
+    puts(ofputil_frag_handling_to_string(ntohs(config.flags)));
+    vconn_close(vconn);
+}
+
+static void
+do_set_frags(int argc OVS_UNUSED, char *argv[])
+{
+    struct ofp_switch_config config;
+    enum ofp_config_flags mode;
+    struct vconn *vconn;
+    ovs_be16 flags;
+
+    if (!ofputil_frag_handling_from_string(argv[2], &mode)) {
+        ovs_fatal(0, "%s: unknown fragment handling mode", argv[2]);
+    }
+
+    open_vconn(argv[1], &vconn);
+    fetch_switch_config(vconn, &config);
+    flags = htons(mode) | (config.flags & htons(~OFPC_FRAG_MASK));
+    if (flags != config.flags) {
+        /* Set the configuration. */
+        config.flags = flags;
+        set_switch_config(vconn, &config);
+
+        /* Then retrieve the configuration to see if it really took.  OpenFlow
+         * doesn't define error reporting for bad modes, so this is all we can
+         * do. */
+        fetch_switch_config(vconn, &config);
+        if (flags != config.flags) {
+            ovs_fatal(0, "%s: setting fragment handling mode failed (this "
+                      "switch probably doesn't support mode \"%s\")",
+                      argv[1], ofputil_frag_handling_to_string(mode));
+        }
+    }
+    vconn_close(vconn);
+}
+
 static void
 do_ping(int argc, char *argv[])
 {
@@ -951,8 +1050,7 @@ fte_version_print(const struct fte_version *version)
     }
 
     ds_init(&s);
-    ofp_print_actions(&s, (const struct ofp_action_header *) version->actions,
-                      version->n_actions * sizeof *version->actions);
+    ofp_print_actions(&s, version->actions, version->n_actions);
     printf(" %s\n", ds_cstr(&s));
     ds_destroy(&s);
 }
@@ -1030,20 +1128,18 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
     min_flow_format = NXFF_OPENFLOW10;
     while (!ds_get_preprocessed_line(&s, file)) {
         struct fte_version *version;
+        struct ofputil_flow_mod fm;
         enum nx_flow_format min_ff;
-        struct ofpbuf actions;
-        struct flow_mod fm;
 
-        ofpbuf_init(&actions, 64);
-        parse_ofp_str(&fm, &actions, ds_cstr(&s));
+        parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
 
         version = xmalloc(sizeof *version);
         version->cookie = fm.cookie;
         version->idle_timeout = fm.idle_timeout;
         version->hard_timeout = fm.hard_timeout;
         version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF_EMERG);
-        version->n_actions = actions.size / sizeof *version->actions;
-        version->actions = ofpbuf_steal_data(&actions);
+        version->actions = fm.actions;
+        version->n_actions = fm.n_actions;
 
         min_ff = ofputil_min_flow_format(&fm.cr);
         min_flow_format = MAX(min_flow_format, min_ff);
@@ -1067,7 +1163,7 @@ static void
 read_flows_from_switch(struct vconn *vconn, enum nx_flow_format flow_format,
                        struct classifier *cls, int index)
 {
-    struct flow_stats_request fsr;
+    struct ofputil_flow_stats_request fsr;
     struct ofpbuf *request;
     ovs_be32 send_xid;
     bool done;
@@ -1143,7 +1239,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
                   enum nx_flow_format flow_format, struct list *packets)
 {
     const struct fte_version *version = fte->versions[index];
-    struct flow_mod fm;
+    struct ofputil_flow_mod fm;
     struct ofpbuf *ofm;
 
     fm.cr = fte->rule;
@@ -1207,7 +1303,8 @@ do_replace_flows(int argc OVS_UNUSED, char *argv[])
         struct fte_version *file_ver = fte->versions[FILE_IDX];
         struct fte_version *sw_ver = fte->versions[SWITCH_IDX];
 
-        if (file_ver && (!sw_ver || !fte_version_equals(sw_ver, file_ver))) {
+        if (file_ver
+            && (readd || !sw_ver || !fte_version_equals(sw_ver, file_ver))) {
             fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, flow_format,
                               &requests);
         }
@@ -1308,7 +1405,7 @@ do_parse_flow(int argc OVS_UNUSED, char *argv[])
 
     list_init(&packets);
     parse_ofp_flow_mod_str(&packets, &flow_format, &flow_mod_table_id,
-                           argv[1], OFPFC_ADD);
+                           argv[1], OFPFC_ADD, false);
     print_packet_list(&packets);
 }
 
@@ -1390,7 +1487,8 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
             puts(out);
             free(out);
         } else {
-            printf("nx_pull_match() returned error %x\n", error);
+            printf("nx_pull_match() returned error %x (%s)\n", error,
+                   ofputil_error_to_string(error));
         }
 
         ofpbuf_uninit(&nx_match);
@@ -1431,6 +1529,8 @@ static const struct command all_commands[] = {
     { "diff-flows", 2, 2, do_diff_flows },
     { "dump-ports", 1, 2, do_dump_ports },
     { "mod-port", 3, 3, do_mod_port },
+    { "get-frags", 1, 1, do_get_frags },
+    { "set-frags", 2, 2, do_set_frags },
     { "probe", 1, 1, do_probe },
     { "ping", 1, 2, do_ping },
     { "benchmark", 3, 3, do_benchmark },