The command receives a flow string and an optional additional payload
and produces a hex-string representation of the corresponding frame.

The command is datapath-independent and may be useful in tests, where we
may need to produce hex frame payloads to compare observed frames
against.

Signed-off-by: Ihar Hrachyshka <ihrac...@redhat.com>
---
 ofproto/ofproto-dpif-trace.c | 70 ++++++++++++++++++++++++++++++++++++
 ofproto/ofproto-unixctl.man  | 36 ++++++++++++++++++-
 tests/ofproto-dpif.at        | 45 +++++++++++++++++++++++
 3 files changed, 150 insertions(+), 1 deletion(-)

diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
index 527e2f17e..2e1931a1f 100644
--- a/ofproto/ofproto-dpif-trace.c
+++ b/ofproto/ofproto-dpif-trace.c
@@ -469,6 +469,73 @@ free_ct_states(struct ovs_list *ct_states)
     }
 }
 
+static void
+ofproto_unixctl_hexify(struct unixctl_conn *conn, int argc,
+                       const char *argv[], void *aux OVS_UNUSED)
+{
+    ovs_assert(argc == 2 || argc == 3);
+
+    char *error = NULL;
+    struct dp_packet *packet = NULL;
+
+    uint8_t *l7 = NULL;
+    size_t l7_len = 0;
+
+    struct ofpbuf odp_key = { 0 };
+    struct ofpbuf odp_mask = { 0 };
+    ofpbuf_init(&odp_key, 0);
+    ofpbuf_init(&odp_mask, 0);
+
+    /* Extract additional payload, if passed */
+    if (argc == 3) {
+        struct dp_packet payload;
+        memset(&payload, 0, sizeof payload);
+        dp_packet_init(&payload, 0);
+        if (dp_packet_put_hex(&payload, argv[2], NULL)[0] != '\0') {
+            dp_packet_uninit(&payload);
+            error = xstrdup("Trailing garbage in payload data");
+            goto out;
+        }
+        l7_len = dp_packet_size(&payload);
+        l7 = dp_packet_steal_data(&payload);
+    }
+
+    /* Flow string to flow. */
+    /* `hexify` command is backer agnostic, hence port_names=NULL */
+    if (odp_flow_from_string(argv[1], NULL, &odp_key, &odp_mask, &error)) {
+        goto out;
+    }
+    struct flow flow;
+    if (odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow, &error)
+        == ODP_FIT_ERROR) {
+        goto out;
+    }
+
+    /* Flow to binary. */
+    packet = dp_packet_new(0);
+    flow_compose(packet, &flow, l7, l7_len);
+
+    /* Binary to hex string. */
+    struct ds result;
+    ds_init(&result);
+    for (int i = 0; i < dp_packet_size(packet); i++) {
+        uint8_t val = ((uint8_t *) dp_packet_data(packet))[i];
+        ds_put_format(&result, "%02"PRIx8, val);
+    }
+
+    unixctl_command_reply(conn, result.string);
+    ds_destroy(&result);
+out:
+    if (error) {
+        unixctl_command_reply_error(conn, error);
+        free(error);
+    }
+    dp_packet_delete(packet);
+    free(l7);
+    ofpbuf_uninit(&odp_mask);
+    ofpbuf_uninit(&odp_key);
+}
+
 static void
 ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
                       void *aux OVS_UNUSED)
@@ -876,6 +943,9 @@ ofproto_dpif_trace_init(void)
         "[-consistent] {[dp_name] odp_flow | bridge br_flow} [OPTIONS...] "
         "[-generate|packet] actions",
         2, INT_MAX, ofproto_unixctl_trace_actions, NULL);
+    unixctl_command_register(
+        "ofproto/hexify", "odp_flow [payload]",
+        1, 2, ofproto_unixctl_hexify, NULL);
 }
 
 void
diff --git a/ofproto/ofproto-unixctl.man b/ofproto/ofproto-unixctl.man
index 095afd57c..8dc68a38d 100644
--- a/ofproto/ofproto-unixctl.man
+++ b/ofproto/ofproto-unixctl.man
@@ -4,7 +4,7 @@ These commands manage the core OpenFlow switch implementation 
(called
 .
 .IP "\fBofproto/list\fR"
 Lists the names of the running ofproto instances.  These are the names
-that may be used on \fBofproto/trace\fR.
+that may be used on \fBofproto/trace\fR and \fBofproto/hexify\fR.
 .
 .IP "\fBofproto/trace\fR [\fIoptions\fR] [\fIdpname\fR] \fIodp_flow\fR 
[\fIpacket\fR]
 .IQ "\fBofproto/trace\fR [\fIoptions\fR] \fIbridge\fR \fIbr_flow\fR 
[\fIpacket\fR]]
@@ -230,3 +230,37 @@ ofproto/trace br in_port=1,arp,arp_op=2
 .fi
 .RE
 .RE
+
+.IQ "\fBofproto/hexify\fR \fIodp_flow\fR [\fIpayload\fR]"
+Generates a hex digits representation of a frame matching
+\fIodp_flow\fR. When \fIpayload\fR is passed, the additional payload
+is attached to the end of the generated frame representation.
+.
+.RS
+.IP \(bu
+\fIodp_flow\fR is a flow in the form printed by \fBovs\-dpctl\fR(8)'s
+\fBdump\-flows\fR command.
+.
+.IP \(bu
+The command is agnostic to the datapath, that's why it doesn't support
+some standard \fIodp_flow\fR features like using port names.
+.RE
+.
+.IP "Usage examples:"
+.RS 4
+.PP
+\fBGenerate a unicast IPv4 TCP ACK packet\fR
+.RS 4
+.nf
+ofproto/hexify eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),
+eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=6),
+tcp(src=8,dst=9),tcp_flags(ack)
+.RE
+.fi
+.PP
+\fBGenerate a unicast IPv4 TCP ACK packet with additional payload\fR
+.RS 4
+.nf
+ofproto/hexify eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),
+eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=6),
+tcp(src=8,dst=9),tcp_flags(ack) 010203abcdef
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index a39d0d3ae..e51e0f412 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -12041,3 +12041,48 @@ AT_CHECK([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap | 
wc -l`])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ofproto/hexify])
+OVS_VSWITCHD_START
+
+# check that invalid syntax generates an error: ethernet() instead of eth()
+flow_s="\
+ethernet(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),\
+ipv4(src=10.0.0.2,dst=10.0.0.1,proto=6,tos=0,ttl=64,frag=no),\
+tcp(src=8,dst=9),tcp_flags(ack)"
+AT_CHECK([ovs-appctl ofproto/hexify ${flow_s}], [2], [stdout], [stderr])
+AT_CHECK_UNQUOTED([tail -2 stderr], [0], [syntax error at ${flow_s}
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+# check that a simpler flow string is successfully hexified
+flow_s="eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800)"
+expected=505400000100505400000005080045000014000000000000baeb0000000000000000
+actual=$(ovs-appctl ofproto/hexify ${flow_s})
+AT_CHECK([test ${expected} = ${actual}])
+
+# check that a longer flow string is successfully hexified
+flow_s="\
+eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),\
+ipv4(src=10.0.0.2,dst=10.0.0.1,proto=6,tos=0,ttl=64,frag=no),\
+tcp(src=8,dst=9),tcp_flags(ack)"
+expected=50540000010050540000000508004500002800000000400666ce0a0000020a000001000800090000000000000000501000009bc10000
+actual=$(ovs-appctl ofproto/hexify ${flow_s})
+AT_CHECK([test ${expected} = ${actual}])
+
+# check that incomplete payload fails the command
+l7payload=$(printf 'd%.0s' {1..13})
+AT_CHECK([ovs-appctl ofproto/hexify ${flow_s} ${l7payload}], [2], [stdout], 
[stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Trailing garbage in payload data
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+# check that a properly sized payload is successfully appended
+l7payload=$(printf 'dead%.0s' {1..10})
+expected=50540000010050540000000508004500003c00000000400666ba0a0000020a00000100080009000000000000000050100000e8e20000deaddeaddeaddeaddeaddeaddeaddeaddeaddead
+actual=$(ovs-appctl ofproto/hexify ${flow_s} ${l7payload})
+AT_CHECK([test ${expected} = ${actual}])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
-- 
2.38.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to