On 10/19/23 16:11, Ihar Hrachyshka wrote:
> The command reads a flow string and an optional additional payload on
> stdin and produces a hex-string representation of the corresponding
> frame on stdout. It may receive more than a single input line.
> 
> The command may be useful in tests, where we may need to produce hex
> frame payloads to compare observed frames against.
> 
> As an example of the tool use, a single test case is converted to it.
> The test uses both normal and --bad-csum behavior of the tool,
> confirming it works as advertised.
> 
> Signed-off-by: Ihar Hrachyshka <ihrac...@redhat.com>
> ---
> v1: - initial version.
> v2: - convert from appctl to ovstest.
>     - add --bad-csum support.
>     - drop separate test case and man for the tool.
> v3: - fix build warnings on restricted ovs_be16 ++.
> ---
>  lib/flow.c                   | 16 +++++-
>  lib/flow.h                   |  2 +-
>  lib/netdev-dummy.c           |  4 +-
>  ofproto/ofproto-dpif-trace.c |  2 +-
>  ofproto/ofproto-dpif.c       |  4 +-
>  tests/dpif-netdev.at         | 45 ++++++++--------
>  tests/test-odp.c             | 99 +++++++++++++++++++++++++++++++++++-
>  utilities/ovs-ofctl.c        |  2 +-
>  8 files changed, 140 insertions(+), 34 deletions(-)
> 
> diff --git a/lib/flow.c b/lib/flow.c
> index fe226cf0f..cd7832710 100644
> --- a/lib/flow.c
> +++ b/lib/flow.c
> @@ -3306,6 +3306,8 @@ packet_expand(struct dp_packet *p, const struct flow 
> *flow, size_t size)
>   * (This is useful only for testing, obviously, and the packet isn't really
>   * valid.  Lots of fields are just zeroed.)
>   *
> + * If 'bad_csum' is true, the final IP checksum is invalid.
> + *
>   * For packets whose protocols can encapsulate arbitrary L7 payloads, 'l7' 
> and
>   * 'l7_len' determine that payload:
>   *
> @@ -3318,7 +3320,7 @@ packet_expand(struct dp_packet *p, const struct flow 
> *flow, size_t size)
>   *      from 'l7'. */
>  void
>  flow_compose(struct dp_packet *p, const struct flow *flow,
> -             const void *l7, size_t l7_len)
> +             const void *l7, size_t l7_len, bool bad_csum)
>  {
>      /* Add code to this function (or its callees) for emitting new fields or
>       * protocols.  (This isn't essential, so it can be skipped for initial
> @@ -3370,7 +3372,17 @@ flow_compose(struct dp_packet *p, const struct flow 
> *flow,
>          /* Checksum has already been zeroed by put_zeros call. */
>          ip->ip_csum = csum(ip, sizeof *ip);
> 
> -        dp_packet_ol_set_ip_csum_good(p);
> +        if (bad_csum) {
> +            /* Internet checksum is a sum complement to zero, so any other
> +             * value will result in an invalid checksum. Here, we flip one
> +             * bit.
> +             */
> +            ip->ip_csum ^= (OVS_FORCE ovs_be16) 0x1;
> +            dp_packet_ip_checksum_bad(p);
> +        } else {
> +            dp_packet_ol_set_ip_csum_good(p);
> +        }
> +
>          pseudo_hdr_csum = packet_csum_pseudoheader(ip);
>          flow_compose_l4_csum(p, flow, pseudo_hdr_csum);
>      } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
> diff --git a/lib/flow.h b/lib/flow.h
> index a9d026e1c..75a9be3c1 100644
> --- a/lib/flow.h
> +++ b/lib/flow.h
> @@ -127,7 +127,7 @@ void flow_set_mpls_bos(struct flow *, int idx, uint8_t 
> stack);
>  void flow_set_mpls_lse(struct flow *, int idx, ovs_be32 lse);
> 
>  void flow_compose(struct dp_packet *, const struct flow *,
> -                  const void *l7, size_t l7_len);
> +                  const void *l7, size_t l7_len, bool bad_csum);
>  void packet_expand(struct dp_packet *, const struct flow *, size_t size);
> 
>  bool parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t 
> *nw_proto,
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 1a54add87..9ead449e1 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -1758,7 +1758,7 @@ eth_from_flow_str(const char *s, size_t packet_size,
> 
>      packet = dp_packet_new(0);
>      if (packet_size) {
> -        flow_compose(packet, flow, NULL, 0);
> +        flow_compose(packet, flow, NULL, 0, false);
>          if (dp_packet_size(packet) < packet_size) {
>              packet_expand(packet, flow, packet_size);
>          } else if (dp_packet_size(packet) > packet_size){
> @@ -1766,7 +1766,7 @@ eth_from_flow_str(const char *s, size_t packet_size,
>              packet = NULL;
>          }
>      } else {
> -        flow_compose(packet, flow, NULL, 64);
> +        flow_compose(packet, flow, NULL, 64, false);
>      }
> 
>      ofpbuf_uninit(&odp_key);
> diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
> index 527e2f17e..b86e7fe07 100644
> --- a/ofproto/ofproto-dpif-trace.c
> +++ b/ofproto/ofproto-dpif-trace.c
> @@ -440,7 +440,7 @@ parse_flow_and_packet(int argc, const char *argv[],
>      if (generate_packet) {
>          /* Generate a packet, as requested. */
>          packet = dp_packet_new(0);
> -        flow_compose(packet, flow, l7, l7_len);
> +        flow_compose(packet, flow, l7, l7_len, false);
>      } else if (packet) {
>          /* Use the metadata from the flow and the packet argument to
>           * reconstruct the flow. */
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index ba5706f6a..9e8faf829 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -1255,7 +1255,7 @@ check_ct_eventmask(struct dpif_backer *backer)
> 
>      /* Compose a dummy UDP packet. */
>      dp_packet_init(&packet, 0);
> -    flow_compose(&packet, &flow, NULL, 64);
> +    flow_compose(&packet, &flow, NULL, 64, false);
> 
>      /* Execute the actions.  On older datapaths this fails with EINVAL, on
>       * newer datapaths it succeeds. */
> @@ -1348,7 +1348,7 @@ check_ct_timeout_policy(struct dpif_backer *backer)
> 
>      /* Compose a dummy UDP packet. */
>      dp_packet_init(&packet, 0);
> -    flow_compose(&packet, &flow, NULL, 64);
> +    flow_compose(&packet, &flow, NULL, 64, false);
> 
>      /* Execute the actions.  On older datapaths this fails with EINVAL, on
>       * newer datapaths it succeeds. */
> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> index 85119fb81..4a3c2b53c 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -746,19 +746,25 @@ OVS_VSWITCHD_START(
>  # Modify the ip_dst addr to force changing the IP csum.
>  AT_CHECK([ovs-ofctl add-flow br1 
> in_port=p1,actions=mod_nw_dst:192.168.1.1,output:p2])
> 
> +flow_s="\
> +eth(src=8a:bf:7e:2f:05:84,dst=0a:8f:39:4f:e0:73),eth_type(0x0800),\
> +ipv4(src=192.168.123.2,dst=192.168.123.1,proto=6,tos=0,ttl=64,frag=no),\
> +tcp(src=54392,dst=5201),tcp_flags(ack)"
> +
> +good_frame=$(echo ${flow_s} | ovstest test-odp hexify-keys)
> +
>  # Check if no offload remains ok.
>  AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap])
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=false])
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=false])
> -AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
> -0a8f394fe0738abf7e2f058408004500003433e0400040068f8fc0a87b02c0a87b01d4781451a962ad5417ed297b801000e547fd00000101080a2524d2345c7fe1c4
> -])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${good_frame}])
> 
>  # Checksum should change to 0x990 with ip_dst changed to 192.168.1.1
>  # by the datapath while processing the packet.
> +flow_expected=$(echo ${flow_s} | sed 's/192.168.123.1/192.168.1.1/g')
> +good_expected=$(echo ${flow_expected} | ovstest test-odp hexify-keys)
>  AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
> -AT_CHECK([tail -n 1 p2.pcap.txt], [0], [dnl
> -0a8f394fe0738abf7e2f058408004500003433e0400040060990c0a87b02c0a80101d4781451a962ad5417ed297b801000e5c1fd00000101080a2524d2345c7fe1c4
> +AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${good_expected}
>  ])
> 
>  # Check if packets entering the datapath with csum offloading
> @@ -766,12 +772,9 @@ AT_CHECK([tail -n 1 p2.pcap.txt], [0], [dnl
>  # in the datapath and not by the netdev.
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=false])
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=true])
> -AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
> -0a8f394fe0738abf7e2f058408004500003433e0400040068f8fc0a87b02c0a87b01d4781451a962ad5417ed297b801000e547fd00000101080a2524d2345c7fe1c4
> -])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${good_frame}])
>  AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
> -AT_CHECK([tail -n 1 p2.pcap.txt], [0], [dnl
> -0a8f394fe0738abf7e2f058408004500003433e0400040060990c0a87b02c0a80101d4781451a962ad5417ed297b801000e5c1fd00000101080a2524d2345c7fe1c4
> +AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${good_expected}
>  ])
> 
>  # Check if packets entering the datapath with csum offloading
> @@ -779,36 +782,30 @@ AT_CHECK([tail -n 1 p2.pcap.txt], [0], [dnl
>  # by the datapath.
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=true])
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=true])
> -AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
> -0a8f394fe0738abf7e2f058408004500003433e0400040068f8fc0a87b02c0a87b01d4781451a962ad5417ed297b801000e547fd00000101080a2524d2345c7fe1c4
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${good_frame}
>  ])
>  AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
> -AT_CHECK([tail -n 1 p2.pcap.txt], [0], [dnl
> -0a8f394fe0738abf7e2f058408004500003433e0400040060990c0a87b02c0a80101d4781451a962ad5417ed297b801000e5c1fd00000101080a2524d2345c7fe1c4
> +AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${good_expected}
>  ])
> 
>  # Push a packet with bad csum and offloading disabled to check
>  # if the datapath updates the csum, but does not fix the issue.
> +bad_frame=$(echo ${flow_s} | ovstest test-odp hexify-keys --bad-csum)
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=false])
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=false])
> -AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
> -0a8f394fe0738abf7e2f058408004500003433e0400040068f03c0a87b02c0a87b01d4781451a962ad5417ed297b801000e547fd00000101080a2524d2345c7fe1c4
> -])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
>  AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
> -AT_CHECK([tail -n 1 p2.pcap.txt], [0], [dnl
> -0a8f394fe0738abf7e2f058408004500003433e0400040060904c0a87b02c0a80101d4781451a962ad5417ed297b801000e5c1fd00000101080a2524d2345c7fe1c4
> +bad_expected=$(echo ${flow_expected} | ovstest test-odp hexify-keys 
> --bad-csum)
> +AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${bad_expected}
>  ])
> 
>  # Push a packet with bad csum and offloading enabled to check
>  # if the driver updates and fixes the csum.
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=true])
>  AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=true])
> -AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
> -0a8f394fe0738abf7e2f058408004500003433e0400040068f03c0a87b02c0a87b01d4781451a962ad5417ed297b801000e547fd00000101080a2524d2345c7fe1c4
> -])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
>  AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
> -AT_CHECK([tail -n 1 p2.pcap.txt], [0], [dnl
> -0a8f394fe0738abf7e2f058408004500003433e0400040060990c0a87b02c0a80101d4781451a962ad5417ed297b801000e5c1fd00000101080a2524d2345c7fe1c4
> +AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${good_expected}
>  ])
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
> diff --git a/tests/test-odp.c b/tests/test-odp.c
> index 0ddfd4070..073e1031a 100644
> --- a/tests/test-odp.c
> +++ b/tests/test-odp.c
> @@ -19,6 +19,7 @@
>  #include "odp-util.h"
>  #include <stdio.h>
>  #include "openvswitch/dynamic-string.h"
> +#include "dp-packet.h"
>  #include "flow.h"
>  #include "openvswitch/match.h"
>  #include "openvswitch/ofpbuf.h"
> @@ -155,6 +156,96 @@ parse_actions(void)
>      return 0;
>  }
> 
> +static int
> +hexify_keys(bool bad_csum)
> +{
> +    int exit_code = 0;
> +    struct ds in;
> +
> +    uint8_t *l7 = NULL;
> +    size_t l7_len = 0;
> +
> +    char *error = NULL;
> +    struct dp_packet *packet = NULL;
> +
> +    ds_init(&in);
> +    vlog_set_levels_from_string_assert("odp_util:console:dbg");
> +    while (!ds_get_test_line(&in, stdin)) {
> +        const char *line = ds_cstr(&in);
> +
> +        struct flow flow;
> +        struct ofpbuf odp_key;
> +        struct ofpbuf odp_mask;
> +        ofpbuf_init(&odp_key, 0);
> +        ofpbuf_init(&odp_mask, 0);
> +
> +        /* Handle trailing hex payload after a space char, if present. */
> +        const char *first_space = strchr(line, ' ');
> +        if (first_space) {
> +            const char *hex_payload = first_space + 1;
> +            struct dp_packet payload;
> +            memset(&payload, 0, sizeof payload);
> +            dp_packet_init(&payload, 0);
> +            if (dp_packet_put_hex(&payload, hex_payload, NULL)[0] != '\0') {
> +                dp_packet_uninit(&payload);
> +                error = xstrdup("Trailing garbage in payload data");
> +                goto next;
> +            }
> +            l7_len = dp_packet_size(&payload);
> +            l7 = dp_packet_steal_data(&payload);
> +            ds_truncate(&in, first_space - line);
> +        }
> +
> +        /* Parse flow string. */
> +        if (odp_flow_from_string(
> +                ds_cstr(&in), NULL, &odp_key, &odp_mask, &error)) {
> +            goto next;
> +        }
> +        if (odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow, &error)
> +            == ODP_FIT_ERROR) {
> +            goto next;
> +        }
> +
> +        /* Flow to binary. */
> +        packet = dp_packet_new(0);
> +        flow_compose(packet, &flow, l7, l7_len, bad_csum);
> +
> +        /* Binary to hex string. */
> +        struct ds result = DS_EMPTY_INITIALIZER;
> +        for (int i = 0; i < dp_packet_size(packet); i++) {
> +            uint8_t val = ((uint8_t *) dp_packet_data(packet))[i];
> +            /* Don't use ds_put_hex because it adds 0x prefix as well as
> +             * it doesn't guarantee even number of payload characters, which 
> is
> +             * important elsewhere (e.g. in `receive` command. */
> +            ds_put_format(&result, "%02" PRIx8, val);
> +        }
> +
> +        puts(ds_cstr(&result));
> +next:
> +        ds_destroy(&result);
> +        if (error) {
> +            printf("%s", error);
> +            free(error);
> +            exit_code = 1;
> +        }
> +
> +        dp_packet_delete(packet);
> +        packet = NULL;
> +        free(l7);
> +        l7 = NULL;
> +
> +        ofpbuf_uninit(&odp_mask);
> +        ofpbuf_uninit(&odp_key);
> +        ds_destroy(&in);
> +
> +        if (exit_code) {
> +            break;
> +        }
> +    }
> +
> +    return exit_code;
> +}
> +
>  static int
>  parse_filter(char *filter_parse)
>  {
> @@ -247,8 +338,14 @@ test_odp_main(int argc, char *argv[])
>          exit_code = parse_actions();
>      } else if (argc == 3 && !strcmp(argv[1], "parse-filter")) {
>          exit_code =parse_filter(argv[2]);
> +    } else if (argc == 2 && !strcmp(argv[1], "hexify-keys")) {
> +        exit_code = hexify_keys(false);
> +    } else if (argc == 3 && !strcmp(argv[1], "hexify-keys") &&
> +            !strcmp(argv[2], "--bad-csum")) {
> +        exit_code = hexify_keys(true);
>      } else {
> -        ovs_fatal(0, "usage: %s parse-keys | parse-wc-keys | parse-actions", 
> argv[0]);
> +        ovs_fatal(0, "usage: %s parse-keys | parse-wc-keys | parse-actions | 
> "
> +                     "hexify-keys [--bad-csum]", argv[0]);
>      }
> 
>      exit(exit_code);
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index 24d0941cf..31da8c2d4 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -4989,7 +4989,7 @@ ofctl_compose_packet(struct ovs_cmdl_context *ctx)

Hmm.  This is interesting.  Looks like we have already a commnd with mostly
the same behavior, except that it is using OpenFlow format insted of datapath
flow format.  Can we just use/extend ovs-ofctl compose-packet?

It supports pcap output, so we can use ovs-pcap tool to get the hex bytes
in a way they can be fed into netdev-dummy/receive.  (We may want to extend
ovs-pcap to allow reading from stdin to make that processeasier.)

Instead of
  eth(src=8a:bf:7e:2f:05:84,dst=0a:8f:39:4f:e0:73),eth_type(0x0800),\
  ipv4(src=192.168.123.2,dst=192.168.123.1,proto=6,tos=0,ttl=64,frag=no),\
  tcp(src=54392,dst=5201),tcp_flags(ack)"

We'll just need to write OpenFlow:
   eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,dl_type=0x0800,\
   nw_src=192.168.123.2,nw_dst=192.168.123.1,nw_proto=6,nw_ttl=64,nw_frag=no,\
   tp_src=8,tp_dst=80,tcp_flags=ack
or
   tcp,eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\
   nw_src=192.168.123.2,nw_dst=192.168.123.1,nw_ttl=64,nw_frag=no,\
   tp_src=8,tp_dst=80,tcp_flags=ack

What do you think?  Is there a specific reson for using odp format over OF?

Best regards, Ilya Maximets.
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to