2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00
ovs/ofproto/ofproto-dpif-xlate-cache.c
Dumitru Ceara 600125b2c3 ofproto: Add ofproto/detrace command to map UFIDs to OpenFlow.
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 <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2024-07-15 19:12:52 +02:00

338 lines
9.1 KiB
C

/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */
#include <config.h>
#include "ofproto/ofproto-dpif-xlate-cache.h"
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <net/if.h>
#include <sys/socket.h>
#include "bfd.h"
#include "bitmap.h"
#include "bond.h"
#include "bundle.h"
#include "byte-order.h"
#include "connmgr.h"
#include "coverage.h"
#include "dp-packet.h"
#include "dpif.h"
#include "learn.h"
#include "mac-learning.h"
#include "netdev-vport.h"
#include "ofproto/ofproto-dpif-mirror.h"
#include "ofproto/ofproto-dpif-xlate.h"
#include "ofproto/ofproto-dpif.h"
#include "ofproto/ofproto-provider.h"
#include "openvswitch/dynamic-string.h"
#include "openvswitch/vlog.h"
#include "ovs-router.h"
#include "packets.h"
#include "tnl-neigh-cache.h"
#include "util.h"
VLOG_DEFINE_THIS_MODULE(ofproto_xlate_cache);
void
xlate_cache_init(struct xlate_cache *xcache)
{
ofpbuf_init(&xcache->entries, 120);
}
struct xlate_cache *
xlate_cache_new(void)
{
struct xlate_cache *xcache = xmalloc(sizeof *xcache);
xlate_cache_init(xcache);
return xcache;
}
struct xc_entry *
xlate_cache_add_entry(struct xlate_cache *xcache, enum xc_type type)
{
struct xc_entry *entry;
entry = ofpbuf_put_zeros(&xcache->entries, sizeof *entry);
entry->type = type;
return entry;
}
static void
xlate_cache_netdev(struct xc_entry *entry, const struct dpif_flow_stats *stats)
{
if (entry->dev.tx) {
netdev_vport_inc_tx(entry->dev.tx, stats);
}
if (entry->dev.rx) {
netdev_vport_inc_rx(entry->dev.rx, stats);
}
if (entry->dev.bfd) {
bfd_account_rx(entry->dev.bfd, stats);
}
}
/* Push stats and perform side effects of flow translation. */
void
xlate_push_stats_entry(struct xc_entry *entry,
struct dpif_flow_stats *stats, bool offloaded)
{
struct eth_addr dmac;
switch (entry->type) {
case XC_TABLE:
ofproto_dpif_credit_table_stats(entry->table.ofproto,
entry->table.id,
entry->table.match
? stats->n_packets : 0,
entry->table.match
? 0 : stats->n_packets);
break;
case XC_RULE:
rule_dpif_credit_stats(entry->rule, stats, offloaded);
break;
case XC_BOND:
bond_account(entry->bond.bond, entry->bond.flow,
entry->bond.vid, stats->n_bytes);
break;
case XC_NETDEV:
xlate_cache_netdev(entry, stats);
break;
case XC_NETFLOW:
netflow_flow_update(entry->nf.netflow, entry->nf.flow,
entry->nf.iface, stats);
break;
case XC_MIRROR:
mirror_update_stats(entry->mirror.mbridge,
entry->mirror.mirrors,
stats->n_packets, stats->n_bytes);
break;
case XC_LEARN: {
enum ofperr error;
error = ofproto_flow_mod_learn(entry->learn.ofm, true,
entry->learn.limit, NULL, stats->used);
if (error) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "xcache LEARN action execution failed.");
}
break;
}
case XC_NORMAL:
xlate_mac_learning_update(entry->normal.ofproto,
entry->normal.in_port,
entry->normal.dl_src,
entry->normal.vlan,
entry->normal.is_gratuitous_arp);
break;
case XC_FIN_TIMEOUT:
if (stats->tcp_flags & (TCP_FIN | TCP_RST)) {
ofproto_rule_reduce_timeouts(&entry->fin.rule->up, entry->fin.idle,
entry->fin.hard);
}
break;
case XC_GROUP:
group_dpif_credit_stats(entry->group.group, entry->group.bucket,
stats);
break;
case XC_TNL_NEIGH:
/* Lookup neighbor to avoid timeout. */
tnl_neigh_lookup(entry->tnl_neigh_cache.br_name,
&entry->tnl_neigh_cache.d_ipv6, &dmac);
break;
case XC_TUNNEL_HEADER:
if (entry->tunnel_hdr.operation == ADD) {
stats->n_bytes += stats->n_packets * entry->tunnel_hdr.hdr_size;
} else {
stats->n_bytes -= stats->n_packets * entry->tunnel_hdr.hdr_size;
}
break;
default:
OVS_NOT_REACHED();
}
}
void
xlate_push_stats(struct xlate_cache *xcache,
struct dpif_flow_stats *stats, bool offloaded)
{
if (!stats->n_packets) {
return;
}
struct xc_entry *entry;
struct ofpbuf entries = xcache->entries;
XC_ENTRY_FOR_EACH (entry, &entries) {
xlate_push_stats_entry(entry, stats, offloaded);
}
}
static void
xlate_dev_unref(struct xc_entry *entry)
{
if (entry->dev.tx) {
netdev_close(entry->dev.tx);
}
if (entry->dev.rx) {
netdev_close(entry->dev.rx);
}
if (entry->dev.bfd) {
bfd_unref(entry->dev.bfd);
}
}
static void
xlate_cache_clear_netflow(struct netflow *netflow, struct flow *flow)
{
netflow_flow_clear(netflow, flow);
netflow_unref(netflow);
free(flow);
}
void
xlate_cache_clear_entry(struct xc_entry *entry)
{
switch (entry->type) {
case XC_TABLE:
ofproto_unref(&(entry->table.ofproto->up));
break;
case XC_RULE:
ofproto_rule_unref(&entry->rule->up);
break;
case XC_BOND:
free(entry->bond.flow);
bond_unref(entry->bond.bond);
break;
case XC_NETDEV:
xlate_dev_unref(entry);
break;
case XC_NETFLOW:
xlate_cache_clear_netflow(entry->nf.netflow, entry->nf.flow);
break;
case XC_MIRROR:
mbridge_unref(entry->mirror.mbridge);
break;
case XC_LEARN:
ofproto_flow_mod_uninit(entry->learn.ofm);
free(entry->learn.ofm);
break;
case XC_NORMAL:
ofproto_unref(&(entry->normal.ofproto->up));
break;
case XC_FIN_TIMEOUT:
/* 'u.fin.rule' is always already held as a XC_RULE, which
* has already released it's reference above. */
break;
case XC_GROUP:
ofproto_group_unref(&entry->group.group->up);
break;
case XC_TNL_NEIGH:
break;
case XC_TUNNEL_HEADER:
break;
default:
OVS_NOT_REACHED();
}
}
void
xlate_cache_clear(struct xlate_cache *xcache)
{
if (!xcache) {
return;
}
struct xc_entry *entry;
struct ofpbuf entries = xcache->entries;
XC_ENTRY_FOR_EACH (entry, &entries) {
xlate_cache_clear_entry(entry);
}
ofpbuf_clear(&xcache->entries);
}
void
xlate_cache_uninit(struct xlate_cache *xcache)
{
if (!xcache) {
return;
}
xlate_cache_clear(xcache);
ofpbuf_uninit(&xcache->entries);
}
void
xlate_cache_delete(struct xlate_cache *xcache)
{
xlate_cache_uninit(xcache);
free(xcache);
}
/* Append all the entries in src into dst and remove them from src.
* The caller must own both xc-caches to use this function.
* The 'src' entries are not freed in this function as its owned by caller.
*/
void
xlate_cache_steal_entries(struct xlate_cache *dst, struct xlate_cache *src)
{
if (!dst || !src) {
return;
}
struct ofpbuf *src_entries = &src->entries;
struct ofpbuf *dst_entries = &dst->entries;
void *p;
p = ofpbuf_put_uninit(dst_entries, src_entries->size);
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;
}
}
}