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