diff --git a/tests/oss-fuzz/automake.mk b/tests/oss-fuzz/automake.mk
index 2c506be3d..5bf7d0d7c 100644
--- a/tests/oss-fuzz/automake.mk
+++ b/tests/oss-fuzz/automake.mk
@@ -4,7 +4,8 @@ OSS_FUZZ_TARGETS = \
 	tests/oss-fuzz/ofp_print_target \
 	tests/oss-fuzz/expr_parse_target \
 	tests/oss-fuzz/odp_target \
-	tests/oss-fuzz/miniflow_target
+	tests/oss-fuzz/miniflow_target \
+	tests/oss-fuzz/ofctl_parse_target
 EXTRA_PROGRAMS += $(OSS_FUZZ_TARGETS)
 oss-fuzz-targets: $(OSS_FUZZ_TARGETS)
 
@@ -45,6 +46,12 @@ tests_oss_fuzz_miniflow_target_SOURCES = \
 tests_oss_fuzz_miniflow_target_LDADD = lib/libopenvswitch.la
 tests_oss_fuzz_miniflow_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++
 
+tests_oss_fuzz_ofctl_parse_target_SOURCES = \
+        tests/oss-fuzz/ofctl_parse_target.c \
+        tests/oss-fuzz/fuzzer.h
+tests_oss_fuzz_ofctl_parse_target_LDADD = lib/libopenvswitch.la
+tests_oss_fuzz_ofctl_parse_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++
+
 EXTRA_DIST += \
 	tests/oss-fuzz/config/flow_extract_target.options \
 	tests/oss-fuzz/config/json_parser_target.options \
@@ -52,6 +59,8 @@ EXTRA_DIST += \
 	tests/oss-fuzz/config/expr_parse_target.options \
 	tests/oss-fuzz/config/odp_target.options \
 	tests/oss-fuzz/config/miniflow_target.options \
+        tests/oss-fuzz/config/ofctl_parse_target.options \
 	tests/oss-fuzz/config/ovs.dict \
 	tests/oss-fuzz/config/expr.dict \
-	tests/oss-fuzz/config/odp.dict
\ No newline at end of file
+	tests/oss-fuzz/config/odp.dict \
+	tests/oss-fuzz/config/ofp-flow.dict
diff --git a/tests/oss-fuzz/config/ofctl_parse_target.options b/tests/oss-fuzz/config/ofctl_parse_target.options
new file mode 100644
index 000000000..6d67dd6ad
--- /dev/null
+++ b/tests/oss-fuzz/config/ofctl_parse_target.options
@@ -0,0 +1,3 @@
+[libfuzzer]
+close_fd_mask = 3
+dict = ofp-flow.dict
diff --git a/tests/oss-fuzz/config/ofp-flow.dict b/tests/oss-fuzz/config/ofp-flow.dict
new file mode 100644
index 000000000..01175e5eb
--- /dev/null
+++ b/tests/oss-fuzz/config/ofp-flow.dict
@@ -0,0 +1,45 @@
+")"
+"("
+"["
+"]"
+","
+"-"
+"0"
+":"
+"="
+"ADD"
+"DEL"
+"DEL_STRICT"
+"MOD"
+"MOD_STRICT"
+"\x00"
+"\x20"
+"\x3F"
+"actions"
+"add"
+"allow_hidden_fields"
+"cc"
+"check_overlap"
+"cookie"
+"delete"
+"delete_strict"
+"duration"
+"eth"
+"hard_age"
+"hard_timeout"
+"idle_age"
+"idle_timeout"
+"importance"
+"modify"
+"modify_strict"
+"n_bytes"
+"n_packets"
+"no_byte_counts"
+"no_packet_counts"
+"no_readonly_table"
+"out_group"
+"out_port"
+"priority"
+"reset_counts"
+"send_flow_rem"
+"table"
diff --git a/tests/oss-fuzz/ofctl_parse_target.c b/tests/oss-fuzz/ofctl_parse_target.c
new file mode 100644
index 000000000..29f89f9b5
--- /dev/null
+++ b/tests/oss-fuzz/ofctl_parse_target.c
@@ -0,0 +1,106 @@
+#include <config.h>
+#include "fuzzer.h"
+#include "openvswitch/ofp-flow.h"
+#include "ofp-version-opt.h"
+#include "ofproto/ofproto.h"
+#include "openflow/openflow.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vlog.h"
+#include "util.h"
+
+static void
+ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms,
+                    enum ofputil_protocol usable_protocols)
+{
+    enum ofputil_protocol protocol = 0;
+    char *usable_s;
+    size_t i;
+
+    usable_s = ofputil_protocols_to_string(usable_protocols);
+    printf("usable protocols: %s\n", usable_s);
+    free(usable_s);
+
+    if (!(usable_protocols & OFPUTIL_P_ANY)) {
+        printf("no usable protocol\n");
+    }
+    for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+        protocol = 1 << i;
+        if (protocol & usable_protocols & OFPUTIL_P_ANY) {
+            break;
+        }
+    }
+    ovs_assert(is_pow2(protocol));
+
+    printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol));
+
+    for (i = 0; i < n_fms; i++) {
+        struct ofputil_flow_mod *fm = &fms[i];
+        struct ofpbuf *msg;
+
+        msg = ofputil_encode_flow_mod(fm, protocol);
+        ofpbuf_delete(msg);
+
+        free(CONST_CAST(struct ofpact *, fm->ofpacts));
+        minimatch_destroy(&fm->match);
+    }
+}
+
+/* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints
+ * it back to stdout.  */
+static void
+ofctl_parse_flow(const char *input, int command)
+{
+    enum ofputil_protocol usable_protocols;
+    struct ofputil_flow_mod fm;
+    char *error;
+
+    error = parse_ofp_flow_mod_str(&fm, input, NULL, NULL,
+                                   command, &usable_protocols);
+    if (error) {
+        printf("Error encountered: %s\n", error);
+    }
+    ofctl_parse_flows__(&fm, 1, usable_protocols);
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    /* Bail out if we cannot construct at least a 1 char string.
+     * Reserve 1 byte to decide flow mod command.
+     *
+     * Here's the structure of data we expect
+     * |--Byte 1--|--Byte 2--|...|--Byte (size-1)--|
+     *
+     * where,
+     *
+     * Byte 1: Used to decide which ofp flow mod command to test
+     * Bytes 2--(size-1): The C string that is actually passed to
+     *                    ofctl_parse_flow() test API.
+     *
+     * This means that the fuzzed input is actually a C string of
+     * length = (size -2) with the terminal byte being the NUL
+     * character. Moreover, this string is expected to not contain
+     * a new-line character.
+     */
+    const char *stream = (const char *) data;
+    if (size < 3 || stream[size - 1] != '\0' || strchr(&stream[1], '\n') ||
+        strlen(&stream[1]) != size - 2) {
+        return 0;
+    }
+
+    /* Disable logging to avoid write to disk. */
+    static bool isInit = false;
+    if (!isInit) {
+        vlog_set_verbosity("off");
+        isInit = true;
+    }
+
+    /* Decide test parameters using first byte of fuzzed input. */
+    int command = (stream[0] % OFPFC_DELETE_STRICT) + 1;
+
+    /* Fuzz extended match parsing. */
+    const char *input = &stream[1];
+    ofctl_parse_flow(input, command);
+
+    return 0;
+}
