From patchwork Fri Jul 12 13:47:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1959884 X-Patchwork-Delegate: i.maximets@samsung.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=An9le4uh; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WLCbd0CPlz1xpd for ; Fri, 12 Jul 2024 23:48:12 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id D4E8E60F96; Fri, 12 Jul 2024 13:48:10 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id p6yZmjYQyKQW; Fri, 12 Jul 2024 13:48:09 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 0600C606AE Authentication-Results: smtp3.osuosl.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=An9le4uh Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id 0600C606AE; Fri, 12 Jul 2024 13:48:09 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id CC15AC0A97; Fri, 12 Jul 2024 13:48:08 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 29FC2C0A96 for ; Fri, 12 Jul 2024 13:48:08 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 0D2BE6071C for ; Fri, 12 Jul 2024 13:48:08 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 2CFzSKlyB98j for ; Fri, 12 Jul 2024 13:48:06 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=170.10.129.124; helo=us-smtp-delivery-124.mimecast.com; envelope-from=dceara@redhat.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org 1F54C606AE Authentication-Results: smtp3.osuosl.org; dmarc=pass (p=none dis=none) header.from=redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 1F54C606AE Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by smtp3.osuosl.org (Postfix) with ESMTPS id 1F54C606AE for ; Fri, 12 Jul 2024 13:48:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1720792084; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Mh9DvJtlQ8sNDb4O59KjRvlqqihvRpMqKCy1jlozXjg=; b=An9le4uhgh3BJlkJecp3lc4m+qXalqvBSnvT6fOzsu/AsI1hGBr7ytFUe799VgZsllpmG/ ++z+aggJ8/kCoD7CUoVYKKu2PeBTtVsayGY18EE0crc43AJunJYajTaEigWZKKcdjllsG3 gB2YiPjZlpJysYLVvafG3G+CHUF24Rw= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-479-kRK3pDzFPkWPY57Y_vS19A-1; Fri, 12 Jul 2024 09:48:02 -0400 X-MC-Unique: kRK3pDzFPkWPY57Y_vS19A-1 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 7396A1954B33; Fri, 12 Jul 2024 13:48:00 +0000 (UTC) Received: from cecil-rh.redhat.com (unknown [10.39.192.95]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9898D1955E85; Fri, 12 Jul 2024 13:47:57 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Fri, 12 Jul 2024 15:47:55 +0200 Message-ID: <20240712134755.985581-1-dceara@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH v6] ofproto: Add ofproto/detrace command to map UFIDs to OpenFlow. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: i.maximets@ovn.org Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" It improves the debugging experience if we can easily get a list of OpenFlow rules and groups that contribute to the creation of a datapath flow. The suggested workflow is: a. dump datapath flows (along with UUIDs), this also prints the core IDs (PMD IDs) when applicable. $ ovs-appctl dpctl/dump-flows -m flow-dump from pmd on cpu core: 7 ufid:7460db8f..., recirc_id(0), .... b. dump related OpenFlow rules and groups: $ ovs-appctl ofproto/detrace ufid:7460db8f... pmd=7 cookie=0x12345678, table=0 priority=100,ip,in_port=2,nw_dst=10.0.0.2,actions=resubmit(,1) cookie=0x0, table=1 priority=200,actions=group:1 group_id=1,bucket=bucket_id:0,actions=ct(commit,table=2,nat(dst=20.0.0.2)) cookie=0x0, table=2 actions=output:1 The new command only shows rules and groups attached to ukeys that are in states UKEY_VISIBLE or UKEY_OPERATIONAL. That should be fine as all other ukeys should not be relevant for the use case presented above. This commit tries to mimic the output format of the ovs-ofctl dump-flows/dump-groups commands. Signed-off-by: Dumitru Ceara --- V6: - Removed group_dpif_format(). - Cleaned up test. V5: - Addressed Ilya's comments: - I addressed all from here https://mail.openvswitch.org/pipermail/ovs-dev/2024-June/415144.html except the one about adding -m/-mm. It felt OK to always dump statistics. V4: - Addressed Mike's comment: - use predictable port IDs. V3: - Addressed Mike's comments: - use ds_put_char() - make the test less flaky V2: - Addressed Adrian's comments: - check return value of populate_xcache() - use flow_stats_ds() (renamed to ofproto_rule_stats_ds()) instead of custom printing - move ukey->state check to caller - handle case when group bucket is not available - update test case to cover the point above --- NEWS | 2 + include/openvswitch/ofp-group.h | 14 +++ lib/ofp-group.c | 131 +++++++++++++++++------------ ofproto/ofproto-dpif-upcall.c | 51 +++++++++++ ofproto/ofproto-dpif-xlate-cache.c | 34 ++++++++ ofproto/ofproto-dpif-xlate-cache.h | 2 + ofproto/ofproto-provider.h | 2 + ofproto/ofproto.c | 11 +-- tests/ofproto-dpif.at | 56 ++++++++++++ tests/ofproto-macros.at | 14 ++- 10 files changed, 257 insertions(+), 60 deletions(-) diff --git a/NEWS b/NEWS index bd97ea3c5aa6..701195183b70 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,8 @@ Post-v3.3.0 or 'text' (by default). * Added new option [--pretty] to print JSON output in a readable fashion. * 'dpif/show' and 'list-commands' now support output in JSON format. + * Added 'ofproto/detrace' to output the set of OpenFlow rules and + groups that contributed to the creation of a specific datapath flow. - Userspace datapath: * Conntrack now supports 'random' flag for selecting ports in a range while natting and 'persistent' flag for selection of the IP address diff --git a/include/openvswitch/ofp-group.h b/include/openvswitch/ofp-group.h index cd7af0ebff9c..7cbb2f70f316 100644 --- a/include/openvswitch/ofp-group.h +++ b/include/openvswitch/ofp-group.h @@ -70,6 +70,10 @@ struct ofputil_bucket *ofputil_bucket_find(const struct ovs_list *, bool ofputil_bucket_check_duplicate_id(const struct ovs_list *); struct ofputil_bucket *ofputil_bucket_list_front(const struct ovs_list *); struct ofputil_bucket *ofputil_bucket_list_back(const struct ovs_list *); +void ofputil_bucket_format(struct ds *, const struct ofputil_bucket *, + enum ofp11_group_type, enum ofp_version, + const struct ofputil_port_map *, + const struct ofputil_table_map *); static inline bool ofputil_bucket_has_liveness(const struct ofputil_bucket *bucket) @@ -88,6 +92,8 @@ struct ofputil_group_props { void ofputil_group_properties_destroy(struct ofputil_group_props *); void ofputil_group_properties_copy(struct ofputil_group_props *to, const struct ofputil_group_props *from); +void ofputil_group_properties_format(const struct ofputil_group_props *, + struct ds *); /* Protocol-independent group_mod. */ struct ofputil_group_mod { uint16_t command; /* One of OFPGC15_*. */ @@ -199,6 +205,14 @@ enum ofperr ofputil_group_desc_format(struct ds *, const struct ofp_header *, enum ofperr ofputil_group_features_format(struct ds *, const struct ofp_header *); +/* Group formatting. */ +void ofputil_group_format(struct ds *s, uint32_t group_id, uint8_t type, + const struct ofputil_bucket *, + const struct ovs_list *p_buckets, + const struct ofputil_group_props *, + enum ofp_version, bool suppress_type, + const struct ofputil_port_map *, + const struct ofputil_table_map *); #ifdef __cplusplus } #endif diff --git a/lib/ofp-group.c b/lib/ofp-group.c index 737f48047b10..3edf1b01b372 100644 --- a/lib/ofp-group.c +++ b/lib/ofp-group.c @@ -1526,6 +1526,31 @@ ofputil_group_properties_destroy(struct ofputil_group_props *gp) free(gp->fields.values); } +void +ofputil_group_properties_format(const struct ofputil_group_props *gp, + struct ds *ds) +{ + if (!gp->selection_method[0]) { + return; + } + + ds_put_format(ds, ",selection_method=%s", gp->selection_method); + if (gp->selection_method_param) { + ds_put_format(ds, ",selection_method_param=%"PRIu64, + gp->selection_method_param); + } + + size_t n = bitmap_count1(gp->fields.used.bm, MFF_N_IDS); + if (n == 1) { + ds_put_cstr(ds, ",fields="); + oxm_format_field_array(ds, &gp->fields); + } else if (n > 1) { + ds_put_cstr(ds, ",fields("); + oxm_format_field_array(ds, &gp->fields); + ds_put_char(ds, ')'); + } +} + static enum ofperr parse_group_prop_ntr_selection_method(struct ofpbuf *payload, enum ofp11_group_type group_type, @@ -1813,16 +1838,45 @@ ofp_print_bucket_id(struct ds *s, const char *label, uint32_t bucket_id, ds_put_char(s, ','); } -static void -ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type, - const struct ovs_list *p_buckets, - const struct ofputil_group_props *props, - enum ofp_version ofp_version, bool suppress_type, - const struct ofputil_port_map *port_map, - const struct ofputil_table_map *table_map) +void +ofputil_bucket_format(struct ds * s, const struct ofputil_bucket *bucket, + enum ofp11_group_type type, enum ofp_version ofp_version, + const struct ofputil_port_map *port_map, + const struct ofputil_table_map *table_map) { - struct ofputil_bucket *bucket; + ds_put_cstr(s, "bucket="); + + ofp_print_bucket_id(s, "bucket_id:", bucket->bucket_id, ofp_version); + if (bucket->weight != (type == OFPGT11_SELECT ? 1 : 0)) { + ds_put_format(s, "weight:%"PRIu16",", bucket->weight); + } + if (bucket->watch_port != OFPP_NONE) { + ds_put_cstr(s, "watch_port:"); + ofputil_format_port(bucket->watch_port, port_map, s); + ds_put_char(s, ','); + } + if (bucket->watch_group != OFPG_ANY) { + ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group); + } + ds_put_cstr(s, "actions="); + struct ofpact_format_params fp = { + .port_map = port_map, + .table_map = table_map, + .s = s, + }; + ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp); +} + +void +ofputil_group_format(struct ds *s, uint32_t group_id, uint8_t type, + const struct ofputil_bucket *bucket, + const struct ovs_list *p_buckets, + const struct ofputil_group_props *props, + enum ofp_version ofp_version, bool suppress_type, + const struct ofputil_port_map *port_map, + const struct ofputil_table_map *table_map) +{ ds_put_format(s, "group_id=%"PRIu32, group_id); if (!suppress_type) { @@ -1831,57 +1885,24 @@ ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type, ds_put_format(s, ",type=%s", type_str[type > 4 ? 4 : type]); } - if (props->selection_method[0]) { - ds_put_format(s, ",selection_method=%s", props->selection_method); - if (props->selection_method_param) { - ds_put_format(s, ",selection_method_param=%"PRIu64, - props->selection_method_param); - } - - size_t n = bitmap_count1(props->fields.used.bm, MFF_N_IDS); - if (n == 1) { - ds_put_cstr(s, ",fields="); - oxm_format_field_array(s, &props->fields); - } else if (n > 1) { - ds_put_cstr(s, ",fields("); - oxm_format_field_array(s, &props->fields); - ds_put_char(s, ')'); - } - } + ofputil_group_properties_format(props, s); - if (!p_buckets) { + if (!bucket && !p_buckets) { return; } ds_put_char(s, ','); - LIST_FOR_EACH (bucket, list_node, p_buckets) { - ds_put_cstr(s, "bucket="); - - ofp_print_bucket_id(s, "bucket_id:", bucket->bucket_id, ofp_version); - if (bucket->weight != (type == OFPGT11_SELECT ? 1 : 0)) { - ds_put_format(s, "weight:%"PRIu16",", bucket->weight); - } - if (bucket->watch_port != OFPP_NONE) { - ds_put_cstr(s, "watch_port:"); - ofputil_format_port(bucket->watch_port, port_map, s); + if (bucket) { + ofputil_bucket_format(s, bucket, type, ofp_version, NULL, NULL); + } else { + LIST_FOR_EACH (bucket, list_node, p_buckets) { + ofputil_bucket_format(s, bucket, type, ofp_version, + port_map, table_map); ds_put_char(s, ','); } - if (bucket->watch_group != OFPG_ANY) { - ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group); - } - - ds_put_cstr(s, "actions="); - struct ofpact_format_params fp = { - .port_map = port_map, - .table_map = table_map, - .s = s, - }; - ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp); - ds_put_char(s, ','); + ds_chomp(s, ','); } - - ds_chomp(s, ','); } enum ofperr @@ -1901,8 +1922,9 @@ ofputil_group_desc_format(struct ds *s, const struct ofp_header *oh, ds_put_char(s, '\n'); ds_put_char(s, ' '); - ofp_print_group(s, gd.group_id, gd.type, &gd.buckets, &gd.props, - oh->version, false, port_map, table_map); + ofputil_group_format(s, gd.group_id, gd.type, NULL, &gd.buckets, + &gd.props, oh->version, false, + port_map, table_map); ofputil_uninit_group_desc(&gd); } } @@ -2368,8 +2390,9 @@ ofputil_group_mod_format__(struct ds *s, enum ofp_version ofp_version, gm->command_bucket_id, ofp_version); } - ofp_print_group(s, gm->group_id, gm->type, &gm->buckets, &gm->props, - ofp_version, bucket_command, port_map, table_map); + ofputil_group_format(s, gm->group_id, gm->type, NULL, &gm->buckets, + &gm->props, ofp_version, bucket_command, + port_map, table_map); } enum ofperr diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c index 83609ec62b63..2054f1c056e5 100644 --- a/ofproto/ofproto-dpif-upcall.c +++ b/ofproto/ofproto-dpif-upcall.c @@ -383,6 +383,7 @@ static void upcall_unixctl_disable_ufid(struct unixctl_conn *, int argc, const char *argv[], void *aux); static void upcall_unixctl_enable_ufid(struct unixctl_conn *, int argc, const char *argv[], void *aux); + static void upcall_unixctl_set_flow_limit(struct unixctl_conn *conn, int argc, const char *argv[], void *aux); static void upcall_unixctl_dump_wait(struct unixctl_conn *conn, int argc, @@ -394,6 +395,9 @@ static void upcall_unixctl_pause(struct unixctl_conn *conn, int argc, static void upcall_unixctl_resume(struct unixctl_conn *conn, int argc, const char *argv[], void *aux); +static void upcall_unixctl_ofproto_detrace(struct unixctl_conn *, int argc, + const char *argv[], void *aux); + static struct udpif_key *ukey_create_from_upcall(struct upcall *, struct flow_wildcards *); static int ukey_create_from_dpif_flow(const struct udpif *, @@ -470,6 +474,8 @@ udpif_init(void) upcall_unixctl_pause, NULL); unixctl_command_register("revalidator/resume", NULL, 0, 0, upcall_unixctl_resume, NULL); + unixctl_command_register("ofproto/detrace", "UFID PMD-ID", 1, 2, + upcall_unixctl_ofproto_detrace, NULL); ovsthread_once_done(&once); } } @@ -3310,6 +3316,51 @@ upcall_unixctl_resume(struct unixctl_conn *conn, int argc OVS_UNUSED, unixctl_command_reply(conn, ""); } +static void +upcall_unixctl_ofproto_detrace(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) +{ + unsigned int pmd_id = NON_PMD_CORE_ID; + const char *key_s = argv[1]; + ovs_u128 ufid; + + if (odp_ufid_from_string(key_s, &ufid) <= 0) { + unixctl_command_reply_error(conn, "failed to parse ufid"); + return; + } + + if (argc == 3) { + const char *pmd_str = argv[2]; + if (!ovs_scan(pmd_str, "pmd=%d", &pmd_id)) { + unixctl_command_reply_error(conn, + "Invalid pmd argument format. " + "Expecting 'pmd=PMD-ID'"); + return; + } + } + + struct ds ds = DS_EMPTY_INITIALIZER; + struct udpif *udpif; + + LIST_FOR_EACH (udpif, list_node, &all_udpifs) { + struct udpif_key *ukey = ukey_lookup(udpif, &ufid, pmd_id); + if (!ukey) { + continue; + } + + ovs_mutex_lock(&ukey->mutex); + /* It only makes sense to format rules for ukeys that are (still) + * in use. */ + if ((ukey->state == UKEY_VISIBLE || ukey->state == UKEY_OPERATIONAL) + && ukey->xcache) { + xlate_xcache_format(&ds, ukey->xcache); + } + ovs_mutex_unlock(&ukey->mutex); + } + unixctl_command_reply(conn, ds_cstr(&ds)); + ds_destroy(&ds); +} + /* Flows are sorted in the following order: * netdev, flow state (offloaded/kernel path), flow_pps_rate. diff --git a/ofproto/ofproto-dpif-xlate-cache.c b/ofproto/ofproto-dpif-xlate-cache.c index 2e1fcb3a6f7a..c6d935cf0ae5 100644 --- a/ofproto/ofproto-dpif-xlate-cache.c +++ b/ofproto/ofproto-dpif-xlate-cache.c @@ -301,3 +301,37 @@ xlate_cache_steal_entries(struct xlate_cache *dst, struct xlate_cache *src) memcpy(p, src_entries->data, src_entries->size); ofpbuf_clear(src_entries); } + +void +xlate_xcache_format(struct ds *s, const struct xlate_cache *xcache) +{ + struct ofpbuf entries = xcache->entries; + struct xc_entry *entry; + struct ofgroup *ofg; + + XC_ENTRY_FOR_EACH (entry, &entries) { + switch (entry->type) { + case XC_RULE: + ofproto_rule_stats_ds(s, &entry->rule->up, true); + break; + case XC_GROUP: + ofg = &entry->group.group->up; + ofputil_group_format(s, ofg->group_id, ofg->type, + entry->group.bucket, &ofg->buckets, + &ofg->props, OFP15_VERSION, + false, NULL, NULL); + break; + case XC_TABLE: + case XC_BOND: + case XC_NETDEV: + case XC_NETFLOW: + case XC_MIRROR: + case XC_LEARN: + case XC_NORMAL: + case XC_FIN_TIMEOUT: + case XC_TNL_NEIGH: + case XC_TUNNEL_HEADER: + break; + } + } +} diff --git a/ofproto/ofproto-dpif-xlate-cache.h b/ofproto/ofproto-dpif-xlate-cache.h index 0fc6d2ea60ce..e701734d796a 100644 --- a/ofproto/ofproto-dpif-xlate-cache.h +++ b/ofproto/ofproto-dpif-xlate-cache.h @@ -151,4 +151,6 @@ void xlate_cache_uninit(struct xlate_cache *); void xlate_cache_delete(struct xlate_cache *); void xlate_cache_steal_entries(struct xlate_cache *, struct xlate_cache *); +void xlate_xcache_format(struct ds *, const struct xlate_cache *); + #endif /* ofproto-dpif-xlate-cache.h */ diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 83c509fcf804..4f42cc02df64 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -450,6 +450,8 @@ void ofproto_rule_ref(struct rule *); bool ofproto_rule_try_ref(struct rule *); void ofproto_rule_unref(struct rule *); +void ofproto_rule_stats_ds(struct ds *, struct rule *, bool offload_stats); + static inline const struct rule_actions * rule_get_actions(const struct rule *); static inline bool rule_is_table_miss(const struct rule *); static inline bool rule_is_hidden(const struct rule *); diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 21c6a1d8257d..8ad5712f5fc6 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -4844,9 +4844,9 @@ handle_flow_stats_request(struct ofconn *ofconn, return 0; } -static void -flow_stats_ds(struct ofproto *ofproto, struct rule *rule, struct ds *results, - bool offload_stats) +void +ofproto_rule_stats_ds(struct ds *results, struct rule *rule, + bool offload_stats) { struct pkt_stats stats; const struct rule_actions *actions; @@ -4875,7 +4875,8 @@ flow_stats_ds(struct ofproto *ofproto, struct rule *rule, struct ds *results, ds_put_format(results, "n_offload_bytes=%"PRIu64", ", stats.n_offload_bytes); } - cls_rule_format(&rule->cr, ofproto_get_tun_tab(ofproto), NULL, results); + cls_rule_format(&rule->cr, ofproto_get_tun_tab(rule->ofproto), NULL, + results); ds_put_char(results, ','); ds_put_cstr(results, "actions="); @@ -4897,7 +4898,7 @@ ofproto_get_all_flows(struct ofproto *p, struct ds *results, struct rule *rule; CLS_FOR_EACH (rule, cr, &table->cls) { - flow_stats_ds(p, rule, results, offload_stats); + ofproto_rule_stats_ds(results, rule, offload_stats); } } } diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 30ef0468c8d1..6fd74a206d1b 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -12176,3 +12176,59 @@ AT_CHECK([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap | wc -l`]) OVS_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([ofproto-dpif - Dump OF rules corresponding to UFID]) +OVS_VSWITCHD_START + +add_of_ports br0 1 2 3 + +dnl Add some OpenFlow rules and groups. +AT_DATA([groups.txt], [dnl +group_id=1,type=select,selection_method=dp_hash,bucket=bucket_id:0,weight:100,actions=ct(commit,table=2,nat(dst=20.0.0.2)) +group_id=2,type=all,bucket=resubmit(,3),bucket=resubmit(,4) +]) +AT_DATA([flows.txt], [dnl +table=0,priority=100,cookie=0x12345678,in_port=p1,ip,nw_dst=10.0.0.2,actions=resubmit(,1) +table=1,priority=200,ip,actions=group:1 +table=2,ip,actions=group:2 +table=3,ip,actions=p2 +table=4,ip,actions=p3 +]) +AT_CHECK([ovs-ofctl add-groups br0 groups.txt]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6),tcp(src=1,dst=2)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6),tcp(src=1,dst=2)']) +ovs-appctl revalidator/wait +ovs-appctl revalidator/pause + +AT_CHECK([ovs-appctl dpctl/dump-flows | strip_used | strip_stats | strip_duration | strip_dp_hash | sort], [0], [dnl +flow-dump from the main thread: +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.0.0.2,frag=no), packets:0, bytes:0, used:0.0s, actions:hash(l4(0)),recirc(0x1) +recirc_id(0x1),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ct(commit,nat(dst=20.0.0.2)),recirc(0x2) +recirc_id(0x2),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:2,3 +]) + +ufid=$(ovs-appctl dpctl/dump-flows -m filter='recirc_id(0)' | parse_ufid) +AT_CHECK([ovs-appctl ofproto/detrace $ufid | ofctl_strip], [0], [dnl +cookie=0x12345678, n_packets=2, n_bytes=236, priority=100,ip,in_port=1,nw_dst=10.0.0.2,actions=resubmit(,1) +table_id=1, n_packets=2, n_bytes=236, priority=200,ip,actions=group:1 +]) + +ufid=$(ovs-appctl dpctl/dump-flows -m filter='recirc_id(0x1)' | parse_ufid) +AT_CHECK([ovs-appctl ofproto/detrace $ufid | ofctl_strip], [0], [dnl +group_id=1,type=select,selection_method=dp_hash,bucket=bucket_id:0,weight:100,actions=ct(commit,table=2,nat(dst=20.0.0.2)) +]) + +ufid=$(ovs-appctl dpctl/dump-flows -m filter='recirc_id(0x2)' | parse_ufid) +AT_CHECK([ovs-appctl ofproto/detrace $ufid | ofctl_strip], [0], [dnl +table_id=2, n_packets=2, n_bytes=236, ip,actions=group:2 +table_id=3, n_packets=2, n_bytes=236, ip,actions=output:2 +table_id=4, n_packets=2, n_bytes=236, ip,actions=output:3 +group_id=2,type=all,bucket=bucket_id:0,actions=resubmit(,3),bucket=bucket_id:1,actions=resubmit(,4) +]) + +ovs-appctl revalidator/resume + +OVS_VSWITCHD_STOP +AT_CLEANUP diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at index c22fb3c79c3f..c27d96177b64 100644 --- a/tests/ofproto-macros.at +++ b/tests/ofproto-macros.at @@ -9,7 +9,9 @@ s/ duration=[0-9.]*s,// s/ cookie=0x0,// s/ table=0,// s/ n_packets=0,// +s/ n_offload_packets=0,// s/ n_bytes=0,// +s/ n_offload_bytes=0,// s/ idle_age=[0-9]*,// s/ hard_age=[0-9]*,// s/dp_hash=0x[0-9a-f]*\//dp_hash=0x0\// @@ -130,7 +132,7 @@ strip_used () { # Removes all 'duration=...' to make output easier to compare. strip_duration () { - sed 's/duration=[[0-9]]*\.[[0-9]]*s,//' + sed 's/duration=[[0-9.]]*s,//' } # Strips 'ufid:...' from output, to make it easier to compare. @@ -140,6 +142,10 @@ strip_ufid () { s/ufid:[[-0-9a-f]]* //' } +parse_ufid () { + grep -o 'ufid:[[-0-9a-f]]*' +} + # Strips packets: and bytes: from output strip_stats () { sed 's/packets:[[0-9]]*/packets:0/ @@ -169,6 +175,12 @@ strip_recirc() { s/recirc_id=[[x0-9]]*/recirc_id=/ s/recirc([[x0-9]]*)/recirc()/' } + +# Strips dp_hash from output. +strip_dp_hash() { + sed 's/dp_hash([[0-9a-fx/]]*),//' +} + m4_divert_pop([PREPARE_TESTS]) m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m'])