added targets to test links
[sliver-openvswitch.git] / utilities / ovs-ofctl.c
index b2470e6..899ce4e 100644 (file)
@@ -98,7 +98,7 @@ struct sort_criterion {
 static struct sort_criterion *criteria;
 static size_t n_criteria, allocated_criteria;
 
-static const struct command all_commands[];
+static const struct command *get_all_commands(void);
 
 static void usage(void) NO_RETURN;
 static void parse_options(int argc, char *argv[]);
@@ -113,7 +113,7 @@ main(int argc, char *argv[])
     set_program_name(argv[0]);
     parse_options(argc, argv);
     signal(SIGPIPE, SIG_IGN);
-    run_command(argc - optind, argv + optind, all_commands);
+    run_command(argc - optind, argv + optind, get_all_commands());
     return 0;
 }
 
@@ -305,7 +305,9 @@ usage(void)
            "  probe TARGET                probe whether TARGET is up\n"
            "  ping TARGET [N]             latency of N-byte echos\n"
            "  benchmark TARGET N COUNT    bandwidth of COUNT N-byte echos\n"
-           "where SWITCH or TARGET is an active OpenFlow connection method.\n",
+           "SWITCH or TARGET is an active OpenFlow connection method.\n"
+           "\nOther commands:\n"
+           "  ofp-parse FILE              print messages read from FILE\n",
            program_name, program_name);
     vconn_usage(true, false, false);
     daemon_usage();
@@ -361,7 +363,7 @@ open_vconn_socket(const char *name, struct vconn **vconnp)
                        vconnp);
     if (error && error != ENOENT) {
         ovs_fatal(0, "%s: failed to open socket (%s)", name,
-                  strerror(error));
+                  ovs_strerror(error));
     }
     free(vconn_name);
 
@@ -412,7 +414,7 @@ open_vconn__(const char *name, enum open_target target,
     error = vconn_connect_block(*vconnp);
     if (error) {
         ovs_fatal(0, "%s: failed to connect to socket (%s)", name,
-                  strerror(error));
+                  ovs_strerror(error));
     }
 
     ofp_version = vconn_get_version(*vconnp);
@@ -866,8 +868,14 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate,
     enum ofputil_protocol usable_protocols, protocol;
     struct ofputil_flow_stats_request fsr;
     struct vconn *vconn;
+    char *error;
+
+    error = parse_ofp_flow_stats_request_str(&fsr, aggregate,
+                                             argc > 2 ? argv[2] : "");
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
 
-    parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
     usable_protocols = ofputil_flow_stats_request_usable_protocols(&fsr);
 
     protocol = open_vconn(argv[1], &vconn);
@@ -1087,8 +1095,12 @@ ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
 {
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
+    char *error;
 
-    parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
+    error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
     ofctl_flow_mod__(argv[1], fms, n_fms);
     free(fms);
 }
@@ -1100,7 +1112,12 @@ ofctl_flow_mod(int argc, char *argv[], uint16_t command)
         ofctl_flow_mod_file(argc, argv, command);
     } else {
         struct ofputil_flow_mod fm;
-        parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
+        char *error;
+
+        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command);
+        if (error) {
+            ovs_fatal(0, "%s", error);
+        }
         ofctl_flow_mod__(argv[1], &fm, 1);
     }
 }
@@ -1233,7 +1250,7 @@ ofctl_send(struct unixctl_conn *conn, int argc,
         error = vconn_send_block(vconn, msg);
         if (error) {
             ofpbuf_delete(msg);
-            ds_put_format(&reply, "%s\n", strerror(error));
+            ds_put_format(&reply, "%s\n", ovs_strerror(error));
             ok = false;
         } else {
             ds_put_cstr(&reply, "sent\n");
@@ -1270,7 +1287,7 @@ ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED,
     error = vconn_send_block(aux->vconn, msg);
     if (error) {
         ofpbuf_delete(msg);
-        unixctl_command_reply_error(conn, strerror(error));
+        unixctl_command_reply_error(conn, ovs_strerror(error));
     } else {
         aux->conn = conn;
     }
@@ -1284,7 +1301,7 @@ ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED,
 
     fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666);
     if (fd < 0) {
-        unixctl_command_reply_error(conn, strerror(errno));
+        unixctl_command_reply_error(conn, ovs_strerror(errno));
         return;
     }
 
@@ -1439,8 +1456,12 @@ ofctl_monitor(int argc, char *argv[])
         } else if (!strncmp(arg, "watch:", 6)) {
             struct ofputil_flow_monitor_request fmr;
             struct ofpbuf *msg;
+            char *error;
 
-            parse_flow_monitor_request(&fmr, arg + 6);
+            error = parse_flow_monitor_request(&fmr, arg + 6);
+            if (error) {
+                ovs_fatal(0, "%s", error);
+            }
 
             msg = ofpbuf_new(0);
             ofputil_append_flow_monitor_request(&fmr, msg);
@@ -1538,10 +1559,14 @@ ofctl_packet_out(int argc, char *argv[])
     struct ofputil_packet_out po;
     struct ofpbuf ofpacts;
     struct vconn *vconn;
+    char *error;
     int i;
 
     ofpbuf_init(&ofpacts, 64);
-    parse_ofpacts(argv[3], &ofpacts);
+    error = parse_ofpacts(argv[3], &ofpacts);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
 
     po.buffer_id = UINT32_MAX;
     po.in_port = str_to_port_no(argv[1], argv[2]);
@@ -1673,6 +1698,56 @@ ofctl_set_frags(int argc OVS_UNUSED, char *argv[])
     vconn_close(vconn);
 }
 
+static void
+ofctl_ofp_parse(int argc OVS_UNUSED, char *argv[])
+{
+    const char *filename = argv[1];
+    struct ofpbuf b;
+    FILE *file;
+
+    file = !strcmp(filename, "-") ? stdin : fopen(filename, "r");
+    if (file == NULL) {
+        ovs_fatal(errno, "%s: open", filename);
+    }
+
+    ofpbuf_init(&b, 65536);
+    for (;;) {
+        struct ofp_header *oh;
+        size_t length, tail_len;
+        void *tail;
+        size_t n;
+
+        ofpbuf_clear(&b);
+        oh = ofpbuf_put_uninit(&b, sizeof *oh);
+        n = fread(oh, 1, sizeof *oh, file);
+        if (n == 0) {
+            break;
+        } else if (n < sizeof *oh) {
+            ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
+        }
+
+        length = ntohs(oh->length);
+        if (length < sizeof *oh) {
+            ovs_fatal(0, "%s: %zu-byte message is too short for OpenFlow",
+                      filename, length);
+        }
+
+        tail_len = length - sizeof *oh;
+        tail = ofpbuf_put_uninit(&b, tail_len);
+        n = fread(tail, 1, tail_len, file);
+        if (n < tail_len) {
+            ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
+        }
+
+        ofp_print(stdout, b.data, b.size, verbosity + 2);
+    }
+    ofpbuf_uninit(&b);
+
+    if (file != stdin) {
+        fclose(file);
+    }
+}
+
 static void
 ofctl_ping(int argc, char *argv[])
 {
@@ -1866,11 +1941,13 @@ fte_free_all(struct classifier *cls)
     struct cls_cursor cursor;
     struct fte *fte, *next;
 
+    ovs_rwlock_wrlock(&cls->rwlock);
     cls_cursor_init(&cursor, cls, NULL);
     CLS_CURSOR_FOR_EACH_SAFE (fte, next, rule, &cursor) {
         classifier_remove(cls, &fte->rule);
         fte_free(fte);
     }
+    ovs_rwlock_unlock(&cls->rwlock);
     classifier_destroy(cls);
 }
 
@@ -1889,7 +1966,9 @@ fte_insert(struct classifier *cls, const struct match *match,
     cls_rule_init(&fte->rule, match, priority);
     fte->versions[index] = version;
 
+    ovs_rwlock_wrlock(&cls->rwlock);
     old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
+    ovs_rwlock_unlock(&cls->rwlock);
     if (old) {
         fte_version_free(old->versions[index]);
         fte->versions[!index] = old->versions[!index];
@@ -1905,6 +1984,7 @@ static enum ofputil_protocol
 read_flows_from_file(const char *filename, struct classifier *cls, int index)
 {
     enum ofputil_protocol usable_protocols;
+    int line_number;
     struct ds s;
     FILE *file;
 
@@ -1915,11 +1995,16 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
 
     ds_init(&s);
     usable_protocols = OFPUTIL_P_ANY;
-    while (!ds_get_preprocessed_line(&s, file)) {
+    line_number = 0;
+    while (!ds_get_preprocessed_line(&s, file, &line_number)) {
         struct fte_version *version;
         struct ofputil_flow_mod fm;
+        char *error;
 
-        parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
+        error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s));
+        if (error) {
+            ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
+        }
 
         version = xmalloc(sizeof *version);
         version->cookie = fm.new_cookie;
@@ -2050,6 +2135,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
     fm.cookie = htonll(0);
     fm.cookie_mask = htonll(0);
     fm.new_cookie = version->cookie;
+    fm.modify_cookie = true;
     fm.table_id = 0xff;
     fm.command = command;
     fm.idle_timeout = version->idle_timeout;
@@ -2092,6 +2178,7 @@ ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
     list_init(&requests);
 
     /* Delete flows that exist on the switch but not in the file. */
+    ovs_rwlock_rdlock(&cls.rwlock);
     cls_cursor_init(&cursor, &cls, NULL);
     CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
         struct fte_version *file_ver = fte->versions[FILE_IDX];
@@ -2115,6 +2202,7 @@ ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
             fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests);
         }
     }
+    ovs_rwlock_unlock(&cls.rwlock);
     transact_multiple_noreply(vconn, &requests);
     vconn_close(vconn);
 
@@ -2156,6 +2244,7 @@ ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
     ds_init(&a_s);
     ds_init(&b_s);
 
+    ovs_rwlock_rdlock(&cls.rwlock);
     cls_cursor_init(&cursor, &cls, NULL);
     CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
         struct fte_version *a = fte->versions[0];
@@ -2175,6 +2264,7 @@ ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
             }
         }
     }
+    ovs_rwlock_unlock(&cls.rwlock);
 
     ds_destroy(&a_s);
     ds_destroy(&b_s);
@@ -2232,8 +2322,12 @@ static void
 ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
 {
     struct ofputil_flow_mod fm;
+    char *error;
 
-    parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
+    error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
     ofctl_parse_flows__(&fm, 1);
 }
 
@@ -2244,8 +2338,12 @@ ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
 {
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
+    char *error;
 
-    parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
+    error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
     ofctl_parse_flows__(fms, n_fms);
     free(fms);
 }
@@ -2297,7 +2395,7 @@ ofctl_parse_nxm__(bool oxm)
             ofpbuf_init(&nx_match, 0);
             if (oxm) {
                 match_len = oxm_put_match(&nx_match, &match);
-                out = oxm_match_to_string(nx_match.data, match_len);
+                out = oxm_match_to_string(&nx_match, match_len);
             } else {
                 match_len = nx_put_match(&nx_match, &match,
                                          cookie, cookie_mask);
@@ -2367,7 +2465,7 @@ ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of10_out;
         struct ofpbuf of10_in;
         struct ofpbuf ofpacts;
@@ -2431,7 +2529,7 @@ ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
     ds_init(&in);
     ds_init(&expout);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf match_in, match_expout;
         struct ofp10_match match_out;
         struct ofp10_match match_normal;
@@ -2503,7 +2601,7 @@ ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf match_in;
         struct ofp11_match match_out;
         struct match match;
@@ -2552,7 +2650,7 @@ ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of11_out;
         struct ofpbuf of11_in;
         struct ofpbuf ofpacts;
@@ -2610,7 +2708,7 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of11_out;
         struct ofpbuf of11_in;
         struct ofpbuf ofpacts;
@@ -2637,9 +2735,15 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         /* Convert to ofpacts. */
         ofpbuf_init(&ofpacts, 0);
         size = of11_in.size;
-        error = ofpacts_pull_openflow11_instructions(
-            &of11_in, of11_in.size, table_id ? atoi(table_id) : 0,
-            &ofpacts);
+        error = ofpacts_pull_openflow11_instructions(&of11_in, of11_in.size,
+                                                     &ofpacts);
+        if (!error) {
+            /* Verify actions. */
+            struct flow flow;
+            memset(&flow, 0, sizeof flow);
+            error = ofpacts_check(ofpacts.data, ofpacts.size, &flow, OFPP_MAX,
+                                  table_id ? atoi(table_id) : 0);
+        }
         if (error) {
             printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
             ofpbuf_uninit(&ofpacts);
@@ -2693,6 +2797,7 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     struct match of11_match;
 
     enum ofperr error;
+    char *error_s;
 
     match_init_catchall(&match);
     match.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
@@ -2702,7 +2807,10 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
     printf("%s -> ", string_s);
     fflush(stdout);
-    parse_ofp_str(&fm, -1, string_s, false);
+    error_s = parse_ofp_str(&fm, -1, string_s);
+    if (error_s) {
+        ovs_fatal(0, "%s", error_s);
+    }
     printf("%04"PRIx16"/%04"PRIx16"\n",
            ntohs(fm.match.flow.vlan_tci),
            ntohs(fm.match.wc.masks.vlan_tci));
@@ -2727,7 +2835,7 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     /* Convert to and from OXM. */
     ofpbuf_init(&nxm, 0);
     nxm_match_len = oxm_put_match(&nxm, &match);
-    nxm_s = oxm_match_to_string(nxm.data, nxm_match_len);
+    nxm_s = oxm_match_to_string(&nxm, nxm_match_len);
     error = oxm_pull_match(&nxm, &nxm_match);
     printf("OXM: %s -> ", nxm_s);
     if (error) {
@@ -2884,6 +2992,7 @@ static const struct command all_commands[] = {
     { "mod-port", 3, 3, ofctl_mod_port },
     { "get-frags", 1, 1, ofctl_get_frags },
     { "set-frags", 2, 2, ofctl_set_frags },
+    { "ofp-parse", 1, 1, ofctl_ofp_parse },
     { "probe", 1, 1, ofctl_probe },
     { "ping", 1, 2, ofctl_ping },
     { "benchmark", 3, 3, ofctl_benchmark },
@@ -2908,3 +3017,8 @@ static const struct command all_commands[] = {
 
     { NULL, 0, 0, NULL },
 };
+
+static const struct command *get_all_commands(void)
+{
+    return all_commands;
+}