diff --git a/Dockerfile b/Dockerfile index b5f5dc81..fd7db80f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -166,6 +166,7 @@ WORKDIR / COPY --from=builder \ /workspace/build/src/dpservice-bin \ /workspace/build/tools/dump/dpservice-dump \ +/workspace/build/tools/inspect/dpservice-inspect \ /workspace/build/cli/dpservice-cli/dpservice-cli \ /workspace/build/cli/dpservice-exporter/dpservice-exporter \ /workspace/hack/prepare.sh \ diff --git a/docs/deployment/README.md b/docs/deployment/README.md index 3232cf13..f18bd7dc 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -8,4 +8,9 @@ For development, direct use of dp-service is covered by the [development section ## Command-line tools All tool binaries are designed to be prefixed with `dpservice-` to enable the operator to simply type `dps` for list of possible tools. -The provided Docker image contains `dpservice-bin` as the main process (already started by being the entrypoint), `dpservice-cli` to gRPC communication with the main process, and `dpservice-dump` to provide a way to see the actual traffic handled by dp-service. Also included is `dpservice-exporter`, a Prometheus exporter that can export various statistics about dpservice (interface stats, NAT port usage, hash table fullness, ...) +The provided Docker image contains: + - `dpservice-bin`, the main process (already started by being the entrypoint) + - `dpservice-cli`, gRPC client connecting to the main process, [documentation](../../cli/dpservice-cli/docs/) + - `dpservice-dump`, tool to provide a way to see the actual traffic handled by dp-service, [documentation](dpservice-dump.md) + - `dpservice-inspect`, tool to view internal state of dp-service, [documentation](dpservice-inspect.md) + - `dpservice-exporter`, a Prometheus exporter that can export various statistics about dpservice (interface stats, NAT port usage, hash table fullness, ...) diff --git a/docs/deployment/dpservice-dump.md b/docs/deployment/dpservice-dump.md index 910cbfa2..450722e9 100644 --- a/docs/deployment/dpservice-dump.md +++ b/docs/deployment/dpservice-dump.md @@ -11,6 +11,7 @@ Always make sure that the tool detaches cleanly (i.e. prints out `Graphtrace suc ## Examples `dpservice-dump` prints all ingress/egress packets processed by dp-service. + `dpservice-dump --drops` also prints dropped packets. -`dpservice-dump --nodes` also prints packets as they are [going through the graph](../concepts/graphtrace.md) +`dpservice-dump --nodes` also prints packets as they are [going through the graph](../concepts/graphtrace.md) diff --git a/docs/deployment/dpservice-inspect.md b/docs/deployment/dpservice-inspect.md new file mode 100644 index 00000000..2edf3274 --- /dev/null +++ b/docs/deployment/dpservice-inspect.md @@ -0,0 +1,19 @@ +# Dataplane Service Internal Inspection Tool +`dpservice-inspect` is a tool to see internal state of dp-service. Currently, only hash-tables are accessible. + +## Command-line Options +All options are described in `dpservice-inspect --help`, see [the markdown version of it](help_dpservice-inspect.md) + +## Disclaimer +As this tool attaches to a live packet-processing dp-service, use it with caution. It should not cause performance degradation in packet-processing since the tool only reads shared-memory in a separate process. + +## Examples +`dpservice-inspect` prints all supported hash-tables that can be viewed. + +`dpservice-inspect -t ` prints the number of entries in a given table + +`dpservice-inspect -t
--dump` prints the contents of the table + +You can choose the output format using `-o`. + +> By default, this tool uses `-1` as the NUMA socket. In practice dp-service will be utilizing NUMA and you need to specify it via `-s`. diff --git a/docs/deployment/help_dpservice-inspect.md b/docs/deployment/help_dpservice-inspect.md new file mode 100644 index 00000000..90ea19d2 --- /dev/null +++ b/docs/deployment/help_dpservice-inspect.md @@ -0,0 +1,13 @@ +# Command-line Options + +| Option | Argument | Description | Choices | +|--------|----------|-------------|---------| +| -h, --help | None | display this help and exit | | +| -v, --version | None | display version and exit | | +| -o, --output-format | FORMAT | format of the output | 'human' (default), 'table', 'csv' or 'json' | +| -t, --table | NAME | hash table to choose | 'list' (default), 'conntrack', 'dnat', 'iface', 'lb', 'lb_id', 'portmap', 'portoverload', 'snat', 'vnf', 'vnf_rev' or 'vni' | +| -s, --socket | NUMBER | NUMA socket to use | | +| --dump | None | dump table contents | | + +> This file has been generated by dp_conf_generate.py. As such it should fully reflect the output of `--help`. + diff --git a/include/dp_flow.h b/include/dp_flow.h index a3764e41..1398a105 100644 --- a/include/dp_flow.h +++ b/include/dp_flow.h @@ -20,6 +20,7 @@ extern "C" { #endif +#define DP_FLOW_TABLE_NAME "conntrack_table" // arbitrary big number #define DP_FLOW_TABLE_MAX 850000 diff --git a/include/dp_iface.h b/include/dp_iface.h index 095150a9..bb92593d 100644 --- a/include/dp_iface.h +++ b/include/dp_iface.h @@ -10,6 +10,8 @@ extern "C" { #endif +#define DP_IFACE_TABLE_NAME "interface_table" + int dp_ifaces_init(int socket_id); void dp_ifaces_free(void); diff --git a/include/dp_lb.h b/include/dp_lb.h index 8292e42c..1652e687 100644 --- a/include/dp_lb.h +++ b/include/dp_lb.h @@ -11,6 +11,8 @@ extern "C" { #include "dp_flow.h" #include "grpc/dp_grpc_responder.h" +#define DP_LB_TABLE_NAME "loadbalancer_table" +#define DP_LB_ID_TABLE_NAME "loadbalancer_id_table" #define DP_LB_TABLE_MAX 256 #define DP_LB_MAX_IPS_PER_VIP 64 /* Needs to be a prime number at least 2xDP_LB_MAX_IPS_PER_VIP for a uniform distribution */ diff --git a/include/dp_nat.h b/include/dp_nat.h index a62605d8..2542dce9 100644 --- a/include/dp_nat.h +++ b/include/dp_nat.h @@ -16,6 +16,11 @@ extern "C" { #endif +#define DP_NAT_DNAT_TABLE_NAME "dnat_table" +#define DP_NAT_SNAT_TABLE_NAME "snat_table" +#define DP_NAT_PORTMAP_TABLE_NAME "nat_portmap_table" +#define DP_NAT_PORTOVERLOAD_TABLE_NAME "nat_portoverload_table" + #define DP_NETWORK_NAT_ALL_VNI 0 struct nat_key { diff --git a/include/dp_util.h b/include/dp_util.h index 086f3b69..58a800c7 100644 --- a/include/dp_util.h +++ b/include/dp_util.h @@ -53,6 +53,12 @@ int dp_get_dev_info(uint16_t port_id, struct rte_eth_dev_info *dev_info, char if int dp_get_num_of_vfs(void); +static __rte_always_inline +int dp_get_jhash_table_full_name(const char *name, int socket_id, char *dest, size_t dest_size) +{ + return snprintf(dest, dest_size, "%s_%d", name, socket_id); +} + struct rte_hash *dp_create_jhash_table(int capacity, size_t key_len, const char *name, int socket_id); void dp_free_jhash_table(struct rte_hash *table); diff --git a/include/dp_vnf.h b/include/dp_vnf.h index 4fbc9d79..5f195ca9 100644 --- a/include/dp_vnf.h +++ b/include/dp_vnf.h @@ -13,6 +13,9 @@ extern "C" { #endif +#define DP_VNF_TABLE_NAME "vnf_table" +#define DP_VNF_REVERSE_TABLE_NAME "reverse_vnf_table" + #define DP_VNF_MATCH_ALL_PORT_IDS 0xFFFF // forward declaration as 'struct dp_grpc_responder' needs some definitions from here diff --git a/include/dp_vni.h b/include/dp_vni.h index b9c47fc7..2e17ec3f 100644 --- a/include/dp_vni.h +++ b/include/dp_vni.h @@ -20,6 +20,8 @@ extern "C" { extern struct rte_hash *vni_handle_tbl; +#define DP_VNI_TABLE_NAME "vni_table" + #define DP_VNI_MAX_TABLE_SIZE 512 // Protect array access diff --git a/src/dp_flow.c b/src/dp_flow.c index 2ce2bc89..7205cd5c 100644 --- a/src/dp_flow.c +++ b/src/dp_flow.c @@ -27,7 +27,7 @@ static bool offload_mode_enabled = 0; int dp_flow_init(int socket_id) { flow_table = dp_create_jhash_table(DP_FLOW_TABLE_MAX, sizeof(struct flow_key), - "conntrack_table", socket_id); + DP_FLOW_TABLE_NAME, socket_id); if (!flow_table) return DP_ERROR; diff --git a/src/dp_iface.c b/src/dp_iface.c index 6c616302..55a5194c 100644 --- a/src/dp_iface.c +++ b/src/dp_iface.c @@ -9,7 +9,7 @@ static struct rte_hash *iface_id_table = NULL; int dp_ifaces_init(int socket_id) { iface_id_table = dp_create_jhash_table(DP_MAX_PORTS, DP_IFACE_ID_MAX_LEN, - "interface_table", socket_id); + DP_IFACE_TABLE_NAME, socket_id); if (!iface_id_table) return DP_ERROR; diff --git a/src/dp_lb.c b/src/dp_lb.c index 12b47c65..65d8ddd7 100644 --- a/src/dp_lb.c +++ b/src/dp_lb.c @@ -22,12 +22,12 @@ static struct rte_hash *id_map_lb_tbl = NULL; int dp_lb_init(int socket_id) { lb_table = dp_create_jhash_table(DP_LB_TABLE_MAX, sizeof(struct lb_key), - "loadbalancer_table", socket_id); + DP_LB_TABLE_NAME, socket_id); if (!lb_table) return DP_ERROR; id_map_lb_tbl = dp_create_jhash_table(DP_LB_TABLE_MAX, DP_LB_ID_MAX_LEN, - "loadbalancer_id_table", socket_id); + DP_LB_ID_TABLE_NAME, socket_id); if (!id_map_lb_tbl) return DP_ERROR; diff --git a/src/dp_nat.c b/src/dp_nat.c index 4cfa6324..c882cf58 100644 --- a/src/dp_nat.c +++ b/src/dp_nat.c @@ -40,23 +40,23 @@ static uint64_t dp_nat_full_log_delay; int dp_nat_init(int socket_id) { ipv4_snat_tbl = dp_create_jhash_table(DP_NAT_TABLE_MAX, sizeof(struct nat_key), - "snat_table", socket_id); + DP_NAT_SNAT_TABLE_NAME, socket_id); if (!ipv4_snat_tbl) return DP_ERROR; ipv4_dnat_tbl = dp_create_jhash_table(DP_NAT_TABLE_MAX, sizeof(struct nat_key), - "dnat_table", socket_id); + DP_NAT_DNAT_TABLE_NAME, socket_id); if (!ipv4_dnat_tbl) return DP_ERROR; ipv4_netnat_portmap_tbl = dp_create_jhash_table(DP_FLOW_TABLE_MAX, sizeof(struct netnat_portmap_key), - "nat_portmap_table", socket_id); + DP_NAT_PORTMAP_TABLE_NAME, socket_id); if (!ipv4_netnat_portmap_tbl) return DP_ERROR; ipv4_netnat_portoverload_tbl = dp_create_jhash_table(DP_FLOW_TABLE_MAX, sizeof(struct netnat_portoverload_tbl_key), - "nat_portoverload_table", socket_id); + DP_NAT_PORTOVERLOAD_TABLE_NAME, socket_id); if (!ipv4_netnat_portoverload_tbl) return DP_ERROR; diff --git a/src/dp_util.c b/src/dp_util.c index 789ad805..a9126bf1 100644 --- a/src/dp_util.c +++ b/src/dp_util.c @@ -144,8 +144,8 @@ struct rte_hash *dp_create_jhash_table(int capacity, size_t key_len, const char else hash_func = rte_jhash; - if ((unsigned int)snprintf(full_name, sizeof(full_name), "%s_%d", name, socket_id) >= RTE_HASH_NAMESIZE) { - DPS_LOG_ERR("jhash table name is too long", DP_LOG_NAME(full_name)); + if (DP_FAILED(dp_get_jhash_table_full_name(name, socket_id, full_name, sizeof(full_name)))) { + DPS_LOG_ERR("jhash table name is too long", DP_LOG_NAME(name)); return NULL; } diff --git a/src/dp_vnf.c b/src/dp_vnf.c index 643b19f7..2f22ab85 100644 --- a/src/dp_vnf.c +++ b/src/dp_vnf.c @@ -19,12 +19,12 @@ static struct rte_hash *vnf_value_tbl = NULL; int dp_vnf_init(int socket_id) { vnf_handle_tbl = dp_create_jhash_table(DP_VNF_MAX_TABLE_SIZE, sizeof(union dp_ipv6), - "vnf_table", socket_id); + DP_VNF_TABLE_NAME, socket_id); if (!vnf_handle_tbl) return DP_ERROR; vnf_value_tbl = dp_create_jhash_table(DP_VNF_MAX_TABLE_SIZE, sizeof(struct dp_vnf), - "reverse_vnf_table", socket_id); + DP_VNF_REVERSE_TABLE_NAME, socket_id); if (!vnf_value_tbl) { dp_free_jhash_table(vnf_handle_tbl); return DP_ERROR; diff --git a/src/dp_vni.c b/src/dp_vni.c index 00fc7c49..f94aa8df 100644 --- a/src/dp_vni.c +++ b/src/dp_vni.c @@ -11,7 +11,7 @@ struct rte_hash *vni_handle_tbl = NULL; int dp_vni_init(int socket_id) { vni_handle_tbl = dp_create_jhash_table(DP_VNI_MAX_TABLE_SIZE, sizeof(struct dp_vni_key), - "vni_table", socket_id); + DP_VNI_TABLE_NAME, socket_id); if (!vni_handle_tbl) return DP_ERROR; diff --git a/tools/inspect/common_ip.c b/tools/inspect/common_ip.c new file mode 100644 index 00000000..80bbb658 --- /dev/null +++ b/tools/inspect/common_ip.c @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "common_ip.h" + +#include +#include + +char str_proto[16]; + +const char *get_str_ipproto(uint8_t proto) +{ + switch (proto) { + case IPPROTO_IP: + return "ip"; + case IPPROTO_ICMP: + return "icmp"; + case IPPROTO_IPIP: + return "ipip"; + case IPPROTO_TCP: + return "tcp"; + case IPPROTO_UDP: + return "udp"; + case IPPROTO_IPV6: + return "ipv6"; + default: + snprintf(str_proto, sizeof(str_proto), "%u", proto); + return str_proto; + } +} diff --git a/tools/inspect/common_ip.h b/tools/inspect/common_ip.h new file mode 100644 index 00000000..44442fc2 --- /dev/null +++ b/tools/inspect/common_ip.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __COMMON_IP_H__ +#define __COMMON_IP_H__ + +#include + +const char *get_str_ipproto(uint8_t proto); + +#endif diff --git a/tools/inspect/common_vnf.c b/tools/inspect/common_vnf.c new file mode 100644 index 00000000..3bc6f787 --- /dev/null +++ b/tools/inspect/common_vnf.c @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "common_vnf.h" + +const char *get_str_vnftype(enum dp_vnf_type type) +{ + switch (type) { + case DP_VNF_TYPE_UNDEFINED: + return "none"; + case DP_VNF_TYPE_LB_ALIAS_PFX: + return "lb_pfx"; + case DP_VNF_TYPE_ALIAS_PFX: + return "pfx"; + case DP_VNF_TYPE_LB: + return "lb"; + case DP_VNF_TYPE_VIP: + return "vip"; + case DP_VNF_TYPE_NAT: + return "nat"; + case DP_VNF_TYPE_INTERFACE_IP: + return "iface"; + } + return "?"; +} diff --git a/tools/inspect/common_vnf.h b/tools/inspect/common_vnf.h new file mode 100644 index 00000000..d45bfa50 --- /dev/null +++ b/tools/inspect/common_vnf.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __COMMON_VNF_H__ +#define __COMMON_VNF_H__ + +#include "dp_vnf.h" + +const char *get_str_vnftype(enum dp_vnf_type type); + +#endif diff --git a/tools/inspect/dp_conf.json b/tools/inspect/dp_conf.json new file mode 100644 index 00000000..7fa025ba --- /dev/null +++ b/tools/inspect/dp_conf.json @@ -0,0 +1,43 @@ +{ + "header": "opts.h", + "source": "opts.c", + "markdown": "../../docs/deployment/help_dpservice-inspect.md", + "options": [ + { + "shopt": "o", + "lgopt": "output-format", + "arg": "FORMAT", + "help": "format of the output", + "var": "output_format", + "type": "enum", + "choices": [ "human", "table", "csv", "json" ], + "default": "human" + }, + { + "shopt": "t", + "lgopt": "table", + "arg": "NAME", + "help": "hash table to choose", + "var": "table", + "type": "enum", + "choices": [ "list", "conntrack", "dnat", "iface", "lb", "lb_id", "portmap", "portoverload", "snat", "vnf", "vnf_rev", "vni" ], + "default": "list" + }, + { + "shopt": "s", + "lgopt": "socket", + "arg": "NUMBER", + "help": "NUMA socket to use", + "var": "numa_socket", + "type": "int", + "default": -1 + }, + { + "lgopt": "dump", + "help": "dump table contents", + "var": "dump", + "type": "bool", + "default": "false" + } + ] +} diff --git a/tools/inspect/inspect.c b/tools/inspect/inspect.c new file mode 100644 index 00000000..ba412dd1 --- /dev/null +++ b/tools/inspect/inspect.c @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "inspect.h" + +#include + +#include "dp_error.h" +#include "dp_ipaddr.h" +#include "dp_util.h" + +// HACK HACK HACK to make including dp_ipaddr.h work +const union dp_ipv6 *dp_conf_get_underlay_ip(void); +const union dp_ipv6 *dp_conf_get_underlay_ip(void) +{ + return &dp_empty_ipv6; +} + + +static int dp_dump_table(const struct rte_hash *htable, int (*dumpfunc)(const void *key, const void *val), enum dp_inspect_output_format format) +{ + uint32_t iter = 0; + void *val = NULL; + const void *key; + bool first = true; + int ret; + + if (format == DP_INSPECT_OUTPUT_FORMAT_JSON) + printf("[\n"); + + while ((ret = rte_hash_iterate(htable, (const void **)&key, (void **)&val, &iter)) != -ENOENT) { + if (DP_FAILED(ret)) { + fprintf(stderr, "Iterating table failed with %d\n", ret); + return ret; + } + if (format == DP_INSPECT_OUTPUT_FORMAT_JSON) { + if (unlikely(first)) { + first = false; + printf("\t"); + } else { + printf(",\n\t"); + } + } + if (DP_FAILED(dumpfunc(key, val))) { + fprintf(stderr, "Dumping table failed with %d\n", ret); + return ret; + } + } + + if (format == DP_INSPECT_OUTPUT_FORMAT_JSON) + printf("\n]\n"); + + return DP_OK; +} + +static int dp_count_table(const char *full_name, const struct rte_hash *htable, enum dp_inspect_output_format format) +{ + int32_t count = rte_hash_count(htable); + + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + printf("Table '%s' has %u entries\n", full_name, count); + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + printf("%*s ENTRIES\n%s %u\n", -(int)strlen(full_name), "TABLE", full_name, count); + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + printf("TABLE,ENTRIES\n%s,%u\n", full_name, count); + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + printf("{ \"table\": \"%s\", \"entries\": %u }\n", full_name, count); + break; + } + return DP_OK; +} + +int dp_inspect_table(const struct dp_inspect_spec *spec, int numa_socket, enum dp_inspect_mode mode, enum dp_inspect_output_format format) +{ + struct rte_hash *htable; + char full_name[RTE_HASH_NAMESIZE]; + int ret = DP_OK; + + if (DP_FAILED(dp_get_jhash_table_full_name(spec->table_name, numa_socket, full_name, sizeof(full_name)))) { + fprintf(stderr, "jhash table name '%s' is too long\n", spec->table_name); + return DP_ERROR; + } + + htable = rte_hash_find_existing(full_name); + if (!htable) { + fprintf(stderr, "Table '%s' not found\n", full_name); + return DP_ERROR; + } + + switch (mode) { + case DP_INSPECT_COUNT: + ret = dp_count_table(full_name, htable, format); + break; + case DP_INSPECT_DUMP: + if (spec->header) + printf(spec->header); + ret = dp_dump_table(htable, spec->dump_func, format); + if (format == DP_INSPECT_OUTPUT_FORMAT_TABLE && spec->header) + printf(spec->header); + break; + } + + return ret; +} diff --git a/tools/inspect/inspect.h b/tools/inspect/inspect.h new file mode 100644 index 00000000..20819562 --- /dev/null +++ b/tools/inspect/inspect.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __INSPECT_H__ +#define __INSPECT_H__ + +enum dp_inspect_mode { + DP_INSPECT_COUNT, + DP_INSPECT_DUMP, +}; + +enum dp_inspect_output_format { + DP_INSPECT_OUTPUT_FORMAT_HUMAN, + DP_INSPECT_OUTPUT_FORMAT_TABLE, + DP_INSPECT_OUTPUT_FORMAT_CSV, + DP_INSPECT_OUTPUT_FORMAT_JSON, +}; + +struct dp_inspect_spec { + const char *table_name; + int (*dump_func)(const void *key, const void *val); + const char *header; +}; + +int dp_inspect_table(const struct dp_inspect_spec *spec, int numa_socket, enum dp_inspect_mode mode, enum dp_inspect_output_format format); + +#endif diff --git a/tools/inspect/inspect_conntrack.c b/tools/inspect/inspect_conntrack.c new file mode 100644 index 00000000..3e6bc398 --- /dev/null +++ b/tools/inspect/inspect_conntrack.c @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "inspect_conntrack.h" + +#include +#include "dp_error.h" +#include "dp_flow.h" + +#include "common_ip.h" +#include "common_vnf.h" + +static const char *g_conntrack_format; + +static const char *get_str_state(enum dp_flow_tcp_state state) +{ + switch (state) { + case DP_FLOW_TCP_STATE_NONE: + return "none"; + case DP_FLOW_TCP_STATE_NEW_SYN: + return "syn"; + case DP_FLOW_TCP_STATE_NEW_SYNACK: + return "synack"; + case DP_FLOW_TCP_STATE_ESTABLISHED: + return "est"; + case DP_FLOW_TCP_STATE_FINWAIT: + return "finwai"; + case DP_FLOW_TCP_STATE_RST_FIN: + return "rstfin"; + } + return "?"; +}; + +static int dp_inspect_conntrack(const void *key, const void *val) +{ + const struct flow_key *flow_key = key; + const struct flow_value *flow_val = val; + + char src[INET6_ADDRSTRLEN]; + char dst[INET6_ADDRSTRLEN]; + + uint64_t hz = rte_get_tsc_hz(); + uint64_t age = (rte_rdtsc() - flow_val->timestamp) / hz; + + DP_IPADDR_TO_STR(&flow_key->l3_src, src); + DP_IPADDR_TO_STR(&flow_key->l3_dst, dst); + + printf(g_conntrack_format, + get_str_vnftype(flow_key->vnf_type), + flow_key->vni, + get_str_ipproto(flow_key->proto), + src, flow_key->src.port_src, + dst, flow_key->port_dst, + flow_val->created_port_id, + get_str_state(flow_val->l4_state.tcp.state), + flow_val->flow_flags, + flow_val->aged, + age / 3600, age / 60 % 60, age % 60, + flow_val->timeout_value, + rte_atomic32_read(&flow_val->ref_count.refcount) + ); + + return DP_OK; +} + + +int dp_inspect_init_conntrack(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_FLOW_TABLE_NAME; + out_spec->dump_func = dp_inspect_conntrack; + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_conntrack_format = "type: %6s, vni: %3u, proto: %4s, src: %15s:%-5u, dst: %15s:%-5u, port_id: %3u, " + "state: %6s, flags: 0x%02x, aged: %d, age: %02lu:%02lu:%02lu, timeout: %5u, ref_count: %u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "TYPE VNI PROTO SOURCE DESTINATION PORT_ID STATE FLAGS AGED AGE TIMEOUT REF_COUNT\n"; + g_conntrack_format = "%-6s %3u %-5s %15s:%-5u %15s:%-5u %7u %-6s 0x%02x %d %02lu:%02lu:%02lu %7u %9u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "TYPE,VNI,PROTO,SOURCE,DESTINATION,PORT_ID,STATE,FLAGS,AGED,AGE,TIMEOUT,REF_COUNT\n"; + g_conntrack_format = "%s,%u,%s,%s:%u,%s:%u,%u,%s,0x%02x,%d,%02lu:%02lu:%02lu,%u,%u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_conntrack_format = "{ \"type\": \"%s\", \"vni\": %u, \"proto\": \"%s\", \"src\": \"%s:%u\", \"dst\": \"%s:%u\", " + "\"port_id\": %u, \"state\": \"%s\", \"flags\": \"0x%02x\", \"aged\": %d, \"age\": \"%02lu:%02lu:%02lu\", " + "\"timeout\": %u, \"ref_count\": %u }"; + break; + } + return DP_OK; +} diff --git a/tools/inspect/inspect_conntrack.h b/tools/inspect/inspect_conntrack.h new file mode 100644 index 00000000..3dd33844 --- /dev/null +++ b/tools/inspect/inspect_conntrack.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __INSPECT_CONNTRACK_H__ +#define __INSPECT_CONNTRACK_H__ + +#include "inspect.h" + +int dp_inspect_init_conntrack(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +#endif diff --git a/tools/inspect/inspect_iface.c b/tools/inspect/inspect_iface.c new file mode 100644 index 00000000..ea56ac9d --- /dev/null +++ b/tools/inspect/inspect_iface.c @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "inspect_iface.h" + +#include + +#include "dp_error.h" +#include "dp_iface.h" + +static const char *g_iface_format; + +static int dp_inspect_iface(const void *key, const void *val) +{ + const char *iface_id = key; + const struct dp_port *iface_port = val; + + printf(g_iface_format, + DP_IFACE_ID_MAX_LEN, iface_id, + iface_port + ); + return DP_OK; +} + + +int dp_inspect_init_iface(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_IFACE_TABLE_NAME; + out_spec->dump_func = dp_inspect_iface; + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_iface_format = "iface_id: %.*s, port: %p(private)\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "IFACE_ID PORT\n"; + g_iface_format = "%-36.*s %p(private)\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "IFACE_ID,PORT\n"; + g_iface_format = "%.*s,%p\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_iface_format = "{ \"iface_id\": \"%.*s\", \"port\": \"%p (private)\" }"; + break; + } + return DP_OK; +} diff --git a/tools/inspect/inspect_iface.h b/tools/inspect/inspect_iface.h new file mode 100644 index 00000000..ff6f29e3 --- /dev/null +++ b/tools/inspect/inspect_iface.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __INSPECT_IFACE_H__ +#define __INSPECT_IFACE_H__ + +#include "inspect.h" + +int dp_inspect_init_iface(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +#endif diff --git a/tools/inspect/inspect_lb.c b/tools/inspect/inspect_lb.c new file mode 100644 index 00000000..f16a3f2b --- /dev/null +++ b/tools/inspect/inspect_lb.c @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "inspect_lb.h" + +#include + +#include "dp_error.h" +#include "dp_ipaddr.h" +#include "dp_lb.h" + +static const char *g_lb_format; + +static int dp_inspect_lb(const void *key, const void *val) +{ + const struct lb_key *lb_key = key; + const struct lb_value *lb_val = val; + + char ip[INET6_ADDRSTRLEN]; + + DP_IPADDR_TO_STR(&lb_key->ip, ip); + printf(g_lb_format, + DP_LB_ID_MAX_LEN, lb_val->lb_id, + lb_key->vni, + ip + ); + return DP_OK; +} + +static int dp_inspect_lb_id(const void *key, const void *val) +{ + const char *lb_id = key; + const struct lb_key *lb_key = val; + + char ip[INET6_ADDRSTRLEN]; + + DP_IPADDR_TO_STR(&lb_key->ip, ip); + printf(g_lb_format, + DP_LB_ID_MAX_LEN, lb_id, + lb_key->vni, + ip + ); + return DP_OK; +} + + +static void setup_format(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_lb_format = "lb_id: %.*s, vni: %3u, ip: %15s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "LB_ID VNI IP\n"; + g_lb_format = "%-36.*s %3u %s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "LB_ID,VNI,IP\n"; + g_lb_format = "%.*s,%u,%s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_lb_format = "{ \"lb_id\": \"%.*s\", \"vni\": %u, \"ip\": \"%s\" }"; + break; + } +} + +int dp_inspect_init_lb(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_LB_TABLE_NAME; + out_spec->dump_func = dp_inspect_lb; + setup_format(out_spec, format); + return DP_OK; +} + +int dp_inspect_init_lb_id(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_LB_ID_TABLE_NAME; + out_spec->dump_func = dp_inspect_lb_id; + setup_format(out_spec, format); + return DP_OK; +} diff --git a/tools/inspect/inspect_lb.h b/tools/inspect/inspect_lb.h new file mode 100644 index 00000000..bc74109f --- /dev/null +++ b/tools/inspect/inspect_lb.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __INSPECT_LB_H__ +#define __INSPECT_LB_H__ + +#include "inspect.h" + +int dp_inspect_init_lb(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +int dp_inspect_init_lb_id(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +#endif diff --git a/tools/inspect/inspect_nat.c b/tools/inspect/inspect_nat.c new file mode 100644 index 00000000..253abb82 --- /dev/null +++ b/tools/inspect/inspect_nat.c @@ -0,0 +1,205 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "inspect_nat.h" + +#include + +#include "dp_error.h" +#include "dp_nat.h" + +#include "common_ip.h" + +static const char *g_dnat_format; +static const char *g_snat_format; +static const char *g_portmap_format; +static const char *g_portoverload_format; + +static int dp_inspect_dnat(const void *key, const void *val) +{ + const struct nat_key *nat_key = key; + const struct dnat_data *dnat_data = val; + + char ip[INET_ADDRSTRLEN]; + char vip[INET_ADDRSTRLEN]; + + DP_IPV4_TO_STR(nat_key->ip, vip); + DP_IPV4_TO_STR(dnat_data->dnat_ip, ip); + printf(g_dnat_format, + vip, + nat_key->vni, + ip + ); + return DP_OK; +} + +static int dp_inspect_snat(const void *key, const void *val) +{ + const struct nat_key *nat_key = key; + const struct snat_data *snat_data = val; + + char ip[INET_ADDRSTRLEN]; + char vip_ip[INET_ADDRSTRLEN]; + char nat_ip[INET_ADDRSTRLEN]; + char ul_vip[INET6_ADDRSTRLEN]; + char ul_nat[INET6_ADDRSTRLEN]; + + DP_IPV4_TO_STR(nat_key->ip, ip); + DP_IPV4_TO_STR(snat_data->vip_ip, vip_ip); + DP_IPV4_TO_STR(snat_data->nat_ip, nat_ip); + DP_IPV6_TO_STR(&snat_data->ul_vip_ip6, ul_vip); + DP_IPV6_TO_STR(&snat_data->ul_nat_ip6, ul_nat); + printf(g_snat_format, + nat_key->vni, + ip, + vip_ip, + nat_ip, + snat_data->nat_port_range[0], + snat_data->nat_port_range[1], + ul_vip, + ul_nat + ); + return DP_OK; +} + +static int dp_inspect_portmap(const void *key, const void *val) +{ + const struct netnat_portmap_key *portmap_key = key; + const struct netnat_portmap_data *portmap_data = val; + + char src_ip[INET6_ADDRSTRLEN]; + char nat_ip[INET_ADDRSTRLEN]; + + DP_IPADDR_TO_STR(&portmap_key->src_ip, src_ip); + DP_IPV4_TO_STR(portmap_data->nat_ip, nat_ip); + printf(g_portmap_format, + portmap_key->vni, + src_ip, + portmap_key->iface_src_port, + nat_ip, + portmap_data->nat_port, + portmap_data->flow_cnt + ); + return DP_OK; +} + +static int dp_inspect_portoverload(const void *key, const void *val) +{ + const struct netnat_portoverload_tbl_key *pkey = key; + + char nat_ip[INET6_ADDRSTRLEN]; + char dst_ip[INET6_ADDRSTRLEN]; + + (void)val; // apparently no data here + + DP_IPV4_TO_STR(pkey->nat_ip, nat_ip); + DP_IPV4_TO_STR(pkey->dst_ip, dst_ip); + printf(g_portoverload_format, + nat_ip, pkey->nat_port, + dst_ip, pkey->dst_port, + get_str_ipproto(pkey->l4_type) + ); + return DP_OK; +} + + +int dp_inspect_init_dnat(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_NAT_DNAT_TABLE_NAME; + out_spec->dump_func = dp_inspect_dnat; + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_dnat_format = "vip: %15s, vni: %3u, ip: %15s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "VIP VNI IP\n"; + g_dnat_format = "%-15s %3u %-15s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "VIP,VNI,IP\n"; + g_dnat_format = "%s,%u,%s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_dnat_format = "{ \"vip\": \"%s\", \"vni\": %u, \"ip\": \"%s\" }"; + break; + } + return DP_OK; +} + +int dp_inspect_init_snat(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_NAT_SNAT_TABLE_NAME; + out_spec->dump_func = dp_inspect_snat; + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_snat_format = "vni: %3u, ip: %15s, vip_ip: %15s, nat_ip: %15s, min_port: %5u, max_port: %5u, ul_vip: %s, ul_nat: %s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "VNI IP VIP_IP NAT_IP MIN_PORT MAX_PORT UL_VIP UL_NAT\n"; + g_snat_format = "%3u %-15s %-15s %-15s %8u %8u %-39s %-39s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "VNI,IP,VIP_IP,NAT_IP,MIN_PORT,MAX_PORT,UL_VIP,UL_NAT\n"; + g_snat_format = "%u,%s,%s,%s,%u,%u,%s,%s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_snat_format = "{ \"vni\": %u, \"ip\": \"%s\", \"vip_ip\": \"%s\", \"nat_ip\": \"%s\", \"min_port\": %u, \"max_port\": %u, " + "\"ul_vip\": \"%s\", \"ul_nat\": \"%s\" }"; + break; + } + return DP_OK; +} + +int dp_inspect_init_portmap(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_NAT_PORTMAP_TABLE_NAME; + out_spec->dump_func = dp_inspect_portmap; + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_portmap_format = "vni: %3u, src_ip: %15s, src_port: %5u, nat_ip: %15s, nat_port: %5u, flows: %u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "VNI SRC_IP SRC_PORT NAT_IP NAT_PORT FLOWS\n"; + g_portmap_format = "%3u %-15s %8u %-15s %8u %5u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "VNI,SRC_IP,SRC_PORT,NAT_IP,NAT_PORT,FLOWS\n"; + g_portmap_format = "%u,%s,%u,%s,%u,%u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_portmap_format = "{ \"vni\": %u, \"src_ip\": \"%s\", \"src_port\": %u, \"nat_ip\": \"%s\", \"nat_port\": %u, \"flows\": %u }"; + break; + } + return DP_OK; +} + +int dp_inspect_init_portoverload(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_NAT_PORTOVERLOAD_TABLE_NAME; + out_spec->dump_func = dp_inspect_portoverload; + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_portoverload_format = "nat_ip: %15s, nat_port: %5u, dst_ip: %15s, dst_port: %5u, proto: %4s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "NAT_IP NAT_PORT DST_IP DST_PORT PROTO\n"; + g_portoverload_format = "%-15s %8u %-15s %8u %-5s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "NAT_IP,NAT_PORT,DST_IP,DST_PORT,PROTO\n"; + g_portoverload_format = "%s,%u,%s,%u,%s\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_portoverload_format = "{ \"nat_ip\": \"%s\", \"nat_port\": %u, \"dst_ip\": \"%s\", \"dst_port\": %u, \"proto\": \"%s\" }"; + break; + } + return DP_OK; +} diff --git a/tools/inspect/inspect_nat.h b/tools/inspect/inspect_nat.h new file mode 100644 index 00000000..2dea7c59 --- /dev/null +++ b/tools/inspect/inspect_nat.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __INSPECT_NAT_H__ +#define __INSPECT_NAT_H__ + +#include "inspect.h" + +int dp_inspect_init_dnat(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +int dp_inspect_init_snat(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +int dp_inspect_init_portmap(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +int dp_inspect_init_portoverload(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +#endif diff --git a/tools/inspect/inspect_vnf.c b/tools/inspect/inspect_vnf.c new file mode 100644 index 00000000..3e4f07cc --- /dev/null +++ b/tools/inspect/inspect_vnf.c @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "inspect_vnf.h" + +#include + +#include "dp_error.h" +#include "dp_ipaddr.h" + +#include "common_vnf.h" + +static const char *g_vnf_format; + +static void print_vnf(const union dp_ipv6 *ul_addr6, const struct dp_vnf *vnf) +{ + char ul[INET6_ADDRSTRLEN]; + char ol[INET6_ADDRSTRLEN]; + + DP_IPV6_TO_STR(ul_addr6, ul); + DP_IPADDR_TO_STR(&vnf->alias_pfx.ol, ol); + + printf(g_vnf_format, + ul, + get_str_vnftype(vnf->type), + vnf->vni, + vnf->port_id, + ol, vnf->alias_pfx.length + ); +} + +static int dp_inspect_vnf(const void *key, const void *val) +{ + print_vnf(key, val); + return DP_OK; +} + +static int dp_inspect_vnf_rev(const void *key, const void *val) +{ + print_vnf(val, key); + return DP_OK; +} + + +static void setup_format(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_vnf_format = "ul: %39s, type: %6s, vni: %3u, port_id: %3u, prefix: %15s, length: %2u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "UNDERLAY_IP TYPE VNI PORT_ID PREFIX LENGTH\n"; + g_vnf_format = "%-39s %-6s %3u %7u %-15s %6u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "UNDERLAY_IP,TYPE,VNI,PORT_ID,PREFIX,LENGTH\n"; + g_vnf_format = "%s,%s,%u,%u,%s,%u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_vnf_format = "{ \"ul\": \"%s\", \"type\": \"%s\", \"vni\": %u, \"port_id\": %u, \"prefix\": \"%s\", \"length\": %u }"; + break; + } +} + +int dp_inspect_init_vnf(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_VNF_TABLE_NAME; + out_spec->dump_func = dp_inspect_vnf; + setup_format(out_spec, format); + return DP_OK; +} + +int dp_inspect_init_vnf_rev(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_VNF_REVERSE_TABLE_NAME; + out_spec->dump_func = dp_inspect_vnf_rev; + setup_format(out_spec, format); + return DP_OK; +} diff --git a/tools/inspect/inspect_vnf.h b/tools/inspect/inspect_vnf.h new file mode 100644 index 00000000..fc6f2d59 --- /dev/null +++ b/tools/inspect/inspect_vnf.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __INSPECT_VNF_H__ +#define __INSPECT_VNF_H__ + +#include "inspect.h" + +int dp_inspect_init_vnf(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +int dp_inspect_init_vnf_rev(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +#endif diff --git a/tools/inspect/inspect_vni.c b/tools/inspect/inspect_vni.c new file mode 100644 index 00000000..ba75bb25 --- /dev/null +++ b/tools/inspect/inspect_vni.c @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include "inspect_vni.h" + +#include + +#include "dp_error.h" +#include "dp_vni.h" + +static const char *g_vni_format; + +static int dp_inspect_vni(const void *key, const void *val) +{ + const struct dp_vni_key *vni_key = key; + const struct dp_vni_data *vni_data = val; + + printf(g_vni_format, + vni_key->vni, + vni_data->vni, + vni_data->socket_id, + vni_data->ipv4[DP_SOCKETID(vni_data->socket_id)], + vni_data->ipv6[DP_SOCKETID(vni_data->socket_id)], + rte_atomic32_read(&vni_data->ref_count.refcount) + ); + return DP_OK; +} + + +int dp_inspect_init_vni(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format) +{ + out_spec->table_name = DP_VNI_TABLE_NAME; + out_spec->dump_func = dp_inspect_vni; + switch (format) { + case DP_INSPECT_OUTPUT_FORMAT_HUMAN: + out_spec->header = NULL; + g_vni_format = "vni: %3d, data_vni: %3d, socket: %d, rib: %p, rib6: %p, ref_count: %u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_TABLE: + out_spec->header = "VNI DATA_VNI SOCKET RIB RIB6 REF_COUNT\n"; + g_vni_format = "%3d %8d %6d %18p %18p %9u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_CSV: + out_spec->header = "VNI,DATA_VNI,SOCKET,RIB,RIB6,REF_COUNT\n"; + g_vni_format = "%d,%d,%d,%p,%p,%u\n"; + break; + case DP_INSPECT_OUTPUT_FORMAT_JSON: + out_spec->header = NULL; + g_vni_format = "{ \"vni\": %d, \"data_vni\": %d, \"socket\": %d, \"rib\": \"%p\", \"rib6\": \"%p\", \"ref_count\": %u }"; + break; + } + return DP_OK; +} diff --git a/tools/inspect/inspect_vni.h b/tools/inspect/inspect_vni.h new file mode 100644 index 00000000..490b6840 --- /dev/null +++ b/tools/inspect/inspect_vni.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __INSPECT_VNI_H__ +#define __INSPECT_VNI_H__ + +#include "inspect.h" + +int dp_inspect_init_vni(struct dp_inspect_spec *out_spec, enum dp_inspect_output_format format); + +#endif diff --git a/tools/inspect/main.c b/tools/inspect/main.c new file mode 100644 index 00000000..f55b6ee9 --- /dev/null +++ b/tools/inspect/main.c @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +#include "dp_error.h" +#include "dp_version.h" + +#include "inspect.h" +#include "inspect_conntrack.h" +#include "inspect_iface.h" +#include "inspect_lb.h" +#include "inspect_nat.h" +#include "inspect_vnf.h" +#include "inspect_vni.h" + +// generated definitions for getopt(), +// generated storage variables and +// generated getters for such variables +#include "opts.h" +#include "opts.c" + +// EAL needs writable arguments (both the string and the array!) +// therefore convert them from literals and remember them for freeing later +static const char *eal_arg_strings[] = { + "dpservice-inspect", // this binary (not used, can actually be any string) + "--proc-type=secondary", // connect to the primary process (dpservice-bin) instead + "--no-pci", // do not try to use any hardware + "--log-level=6", // hide DPDK's informational messages (level 7) +}; + +static char *eal_args_mem[RTE_DIM(eal_arg_strings)]; +static char *eal_args[RTE_DIM(eal_args_mem)]; + +static int eal_init(void) +{ + for (size_t i = 0; i < RTE_DIM(eal_arg_strings); ++i) { + eal_args[i] = eal_args_mem[i] = strdup(eal_arg_strings[i]); + if (!eal_args[i]) { + fprintf(stderr, "Cannot allocate EAL arguments\n"); + for (size_t j = 0; j < RTE_DIM(eal_args_mem); ++j) + free(eal_args_mem[j]); + return DP_ERROR; + } + } + return rte_eal_init(RTE_DIM(eal_args), eal_args); +} + +static void eal_cleanup(void) +{ + rte_eal_cleanup(); + for (size_t i = 0; i < RTE_DIM(eal_args_mem); ++i) + free(eal_args_mem[i]); +} + + +static void list_tables(enum dp_conf_output_format format) +{ + const char *format_string; + bool first = true; + + switch (format) { + case DP_CONF_OUTPUT_FORMAT_HUMAN: + printf("Supported tables (-t argument):\n"); + format_string = " %s\n"; + break; + case DP_CONF_OUTPUT_FORMAT_TABLE: + case DP_CONF_OUTPUT_FORMAT_CSV: + printf("NAME\n"); + format_string = "%s\n"; + break; + case DP_CONF_OUTPUT_FORMAT_JSON: + printf("[\n"); + format_string = "\t\"%s\""; + break; + } + // table_choices is from conf.c + // start at 1 - skip the "list" option + for (size_t i = 1; i < RTE_DIM(table_choices); ++i) { + if (format == DP_CONF_OUTPUT_FORMAT_JSON) { + if (first) + first = false; + else + printf(",\n"); + } + printf(format_string, table_choices[i]); + } + + if (format == DP_CONF_OUTPUT_FORMAT_JSON) + printf("\n]\n"); +} + +static int dp_inspect_init(enum dp_conf_table selected_table, enum dp_inspect_output_format format, struct dp_inspect_spec *out_spec) +{ + switch (selected_table) { + case DP_CONF_TABLE_LIST: + break; + case DP_CONF_TABLE_CONNTRACK: + return dp_inspect_init_conntrack(out_spec, format); + case DP_CONF_TABLE_DNAT: + return dp_inspect_init_dnat(out_spec, format); + case DP_CONF_TABLE_IFACE: + return dp_inspect_init_iface(out_spec, format); + case DP_CONF_TABLE_LB: + return dp_inspect_init_lb(out_spec, format); + case DP_CONF_TABLE_LB_ID: + return dp_inspect_init_lb_id(out_spec, format); + case DP_CONF_TABLE_PORTMAP: + return dp_inspect_init_portmap(out_spec, format); + case DP_CONF_TABLE_PORTOVERLOAD: + return dp_inspect_init_portoverload(out_spec, format); + case DP_CONF_TABLE_SNAT: + return dp_inspect_init_snat(out_spec, format); + case DP_CONF_TABLE_VNF: + return dp_inspect_init_vnf(out_spec, format); + case DP_CONF_TABLE_VNF_REV: + return dp_inspect_init_vnf_rev(out_spec, format); + case DP_CONF_TABLE_VNI: + return dp_inspect_init_vni(out_spec, format); + } + out_spec->table_name = NULL; + return DP_OK; +} + +// unfortunately it's pretty hard to include the opts.h in the right place, thus this conversion +static enum dp_inspect_output_format get_format(enum dp_conf_output_format format) +{ + switch (format) { + case DP_CONF_OUTPUT_FORMAT_HUMAN: + return DP_INSPECT_OUTPUT_FORMAT_HUMAN; + case DP_CONF_OUTPUT_FORMAT_TABLE: + return DP_INSPECT_OUTPUT_FORMAT_TABLE; + case DP_CONF_OUTPUT_FORMAT_CSV: + return DP_INSPECT_OUTPUT_FORMAT_CSV; + case DP_CONF_OUTPUT_FORMAT_JSON: + return DP_INSPECT_OUTPUT_FORMAT_JSON; + } + return DP_INSPECT_OUTPUT_FORMAT_TABLE; +} + + +static void dp_argparse_version(void) +{ + printf("DP Service version %s\n", DP_SERVICE_VERSION); +} + +int main(int argc, char **argv) +{ + struct dp_inspect_spec spec; + int ret; + + switch (dp_conf_parse_args(argc, argv)) { + case DP_CONF_RUNMODE_ERROR: + return EXIT_FAILURE; + case DP_CONF_RUNMODE_EXIT: + return EXIT_SUCCESS; + case DP_CONF_RUNMODE_NORMAL: + break; + } + + ret = eal_init(); + if (DP_FAILED(ret)) { + fprintf(stderr, "Cannot init EAL %s\n", dp_strerror_verbose(ret)); + return EXIT_FAILURE; + } + + ret = dp_inspect_init(dp_conf_get_table(), get_format(dp_conf_get_output_format()), &spec); + if (!DP_FAILED(ret)) { + if (!spec.table_name) + list_tables(dp_conf_get_output_format()); + else + ret = dp_inspect_table(&spec, dp_conf_get_numa_socket(), + dp_conf_is_dump() ? DP_INSPECT_DUMP : DP_INSPECT_COUNT, + get_format(dp_conf_get_output_format())); + } + + eal_cleanup(); + + return DP_FAILED(ret) ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/tools/inspect/meson.build b/tools/inspect/meson.build new file mode 100644 index 00000000..9fee7877 --- /dev/null +++ b/tools/inspect/meson.build @@ -0,0 +1,20 @@ +dpservice_inspect_sources = [ + 'common_ip.c', + 'common_vnf.c', + 'inspect.c', + 'inspect_conntrack.c', + 'inspect_iface.c', + 'inspect_lb.c', + 'inspect_nat.c', + 'inspect_vnf.c', + 'inspect_vni.c', + 'main.c', + '../../src/dp_argparse.c', + '../../src/dp_error.c', + '../../src/dp_ipaddr.c', +] + +executable('dpservice-inspect', + sources: [ dpservice_inspect_sources, version_h ], + include_directories: [includes], + dependencies: [dpdk_dep] ) diff --git a/tools/inspect/opts.c b/tools/inspect/opts.c new file mode 100644 index 00000000..4eb65171 --- /dev/null +++ b/tools/inspect/opts.c @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +/***********************************************************************/ +/* DO NOT EDIT THIS FILE */ +/* */ +/* This file has been generated by dp_conf_generate.py */ +/* Please edit dp_conf.json and re-run the script to update this file. */ +/***********************************************************************/ + +#include "dp_argparse.h" + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(ARRAY) (sizeof(ARRAY) / sizeof((ARRAY)[0])) +#endif + +enum { + OPT_HELP = 'h', + OPT_VERSION = 'v', + OPT_OUTPUT_FORMAT = 'o', + OPT_TABLE = 't', + OPT_SOCKET = 's', +_OPT_SHOPT_MAX = 255, + OPT_DUMP, +}; + +#define OPTSTRING ":hv" \ + "o:" \ + "t:" \ + "s:" \ + +static const struct option dp_conf_longopts[] = { + { "help", 0, 0, OPT_HELP }, + { "version", 0, 0, OPT_VERSION }, + { "output-format", 1, 0, OPT_OUTPUT_FORMAT }, + { "table", 1, 0, OPT_TABLE }, + { "socket", 1, 0, OPT_SOCKET }, + { "dump", 0, 0, OPT_DUMP }, + { NULL, 0, 0, 0 } +}; + +static const char *output_format_choices[] = { + "human", + "table", + "csv", + "json", +}; + +static const char *table_choices[] = { + "list", + "conntrack", + "dnat", + "iface", + "lb", + "lb_id", + "portmap", + "portoverload", + "snat", + "vnf", + "vnf_rev", + "vni", +}; + +static enum dp_conf_output_format output_format = DP_CONF_OUTPUT_FORMAT_HUMAN; +static enum dp_conf_table table = DP_CONF_TABLE_LIST; +static int numa_socket = -1; +static bool dump = false; + +enum dp_conf_output_format dp_conf_get_output_format(void) +{ + return output_format; +} + +enum dp_conf_table dp_conf_get_table(void) +{ + return table; +} + +int dp_conf_get_numa_socket(void) +{ + return numa_socket; +} + +bool dp_conf_is_dump(void) +{ + return dump; +} + + + +/* These functions need to be implemented by the user of this generated code */ +static void dp_argparse_version(void); + + +static inline void dp_argparse_help(const char *progname, FILE *outfile) +{ + fprintf(outfile, "Usage: %s [options]\n" + " -h, --help display this help and exit\n" + " -v, --version display version and exit\n" + " -o, --output-format=FORMAT format of the output: 'human' (default), 'table', 'csv' or 'json'\n" + " -t, --table=NAME hash table to choose: 'list' (default), 'conntrack', 'dnat', 'iface', 'lb', 'lb_id', 'portmap', 'portoverload', 'snat', 'vnf', 'vnf_rev' or 'vni'\n" + " -s, --socket=NUMBER NUMA socket to use\n" + " --dump dump table contents\n" + , progname); +} + +static int dp_conf_parse_arg(int opt, const char *arg) +{ + (void)arg; // if no option uses an argument, this would be unused + switch (opt) { + case OPT_OUTPUT_FORMAT: + return dp_argparse_enum(arg, (int *)&output_format, output_format_choices, ARRAY_SIZE(output_format_choices)); + case OPT_TABLE: + return dp_argparse_enum(arg, (int *)&table, table_choices, ARRAY_SIZE(table_choices)); + case OPT_SOCKET: + return dp_argparse_int(arg, &numa_socket, INT_MIN, INT_MAX); + case OPT_DUMP: + return dp_argparse_store_true(&dump); + default: + fprintf(stderr, "Unimplemented option %d\n", opt); + return DP_ERROR; + } +} + +enum dp_conf_runmode dp_conf_parse_args(int argc, char **argv) +{ + const char *progname = argv[0]; + int option_index = -1; + int opt; + + while ((opt = getopt_long(argc, argv, OPTSTRING, dp_conf_longopts, &option_index)) != -1) { + switch (opt) { + case OPT_HELP: + dp_argparse_help(progname, stdout); + return DP_CONF_RUNMODE_EXIT; + case OPT_VERSION: + dp_argparse_version(); + return DP_CONF_RUNMODE_EXIT; + case ':': + fprintf(stderr, "Missing argument for '%s'\n", argv[optind-1]); + return DP_CONF_RUNMODE_ERROR; + case '?': + if (optopt > 0) + fprintf(stderr, "Unknown option '-%c'\n", optopt); + else + fprintf(stderr, "Unknown option '%s'\n", argv[optind-1]); + return DP_CONF_RUNMODE_ERROR; + default: + if (DP_FAILED(dp_conf_parse_arg(opt, optarg))) { + if (option_index >= 0) + fprintf(stderr, "Invalid argument for '--%s'\n", dp_conf_longopts[option_index].name); + else + fprintf(stderr, "Invalid argument for '-%c'\n", opt); + return DP_CONF_RUNMODE_ERROR; + } + } + option_index = -1; + } + return DP_CONF_RUNMODE_NORMAL; +} + diff --git a/tools/inspect/opts.h b/tools/inspect/opts.h new file mode 100644 index 00000000..9e7e5dcb --- /dev/null +++ b/tools/inspect/opts.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +/***********************************************************************/ +/* DO NOT EDIT THIS FILE */ +/* */ +/* This file has been generated by dp_conf_generate.py */ +/* Please edit dp_conf.json and re-run the script to update this file. */ +/***********************************************************************/ + +enum dp_conf_output_format { + DP_CONF_OUTPUT_FORMAT_HUMAN, + DP_CONF_OUTPUT_FORMAT_TABLE, + DP_CONF_OUTPUT_FORMAT_CSV, + DP_CONF_OUTPUT_FORMAT_JSON, +}; + +enum dp_conf_table { + DP_CONF_TABLE_LIST, + DP_CONF_TABLE_CONNTRACK, + DP_CONF_TABLE_DNAT, + DP_CONF_TABLE_IFACE, + DP_CONF_TABLE_LB, + DP_CONF_TABLE_LB_ID, + DP_CONF_TABLE_PORTMAP, + DP_CONF_TABLE_PORTOVERLOAD, + DP_CONF_TABLE_SNAT, + DP_CONF_TABLE_VNF, + DP_CONF_TABLE_VNF_REV, + DP_CONF_TABLE_VNI, +}; + +enum dp_conf_output_format dp_conf_get_output_format(void); +enum dp_conf_table dp_conf_get_table(void); +int dp_conf_get_numa_socket(void); +bool dp_conf_is_dump(void); + +enum dp_conf_runmode { + DP_CONF_RUNMODE_NORMAL, /**< Start normally */ + DP_CONF_RUNMODE_EXIT, /**< End succesfully (e.g. for --help etc.) */ + DP_CONF_RUNMODE_ERROR, /**< Error parsing arguments */ +}; + +enum dp_conf_runmode dp_conf_parse_args(int argc, char **argv); diff --git a/tools/meson.build b/tools/meson.build index ee4b3554..58f9f8fd 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -1 +1,2 @@ subdir('dump') +subdir('inspect')