[ovs-dev] [2/2] utilities/nlmon: Add TC flower monitoring support

Mohammad Heib mheib at redhat.com
Sun Nov 21 23:39:33 UTC 2021


Currently nlmon tool only supports monitoring Netlink
messages of type link-layer changes.

This patch extends this tool to support monitoring TC flower of
type create/replace/change flower Netlink messages that sent
to the kernel TC subsystem from any userspace process.

This change can be used for testing and improving visibility
and debuggability of OVS  HW offload.
This patch implements the basic TC flower/actions parsing
and future patches will be sent to extend this parsing.

This change is backward compatible and apps that use nlmon
can still use it without any change required.

Signed-off-by: Mohammad Heib <mheib at redhat.com>
---
 utilities/nlmon.c | 461 +++++++++++++++++++++++++++++++++++++++++++++-
 utilities/nlmon.h | 162 ++++++++++++++++
 2 files changed, 615 insertions(+), 8 deletions(-)
 create mode 100644 utilities/nlmon.h

diff --git a/utilities/nlmon.c b/utilities/nlmon.c
index c2f232a12..bfe9d8939 100644
--- a/utilities/nlmon.c
+++ b/utilities/nlmon.c
@@ -18,11 +18,13 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <net/if.h>
+#include <netinet/in.h>
 #include <poll.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
 #include <stddef.h>
 #include <linux/rtnetlink.h>
+#include<linux/if_ether.h>
 #include <getopt.h>
 #include "netlink.h"
 #include "netlink-socket.h"
@@ -32,20 +34,21 @@
 #include "timeval.h"
 #include "util.h"
 #include "openvswitch/vlog.h"
+#include "nlmon.h"
 
 #define MAX_TYPE_LEN 25
 
 typedef int (*GRP_handler)(void *);
 
-static const struct nl_policy rtnlgrp_link_policy[] = {
-    [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
-    [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
-};
-
 int nlmon_link(void *);
 int nlmon_tc(void *);
 
 static void print_usage(void);
+static void nl_parse_tc_opts(struct nlattr *);
+static void parse_rtattr_flags(struct rtattr **, int,
+                struct rtattr *, int, unsigned short);
+static void nl_parse_tc_acts(struct rtattr *);
+static void nl_parse_tc_act(struct rtattr *);
 
 static const struct {
     enum rtnetlink_groups gr_id;
@@ -58,6 +61,164 @@ static const struct {
     { RTNLGRP_NONE, NULL, NULL }
 };
 
+static const struct nl_policy rtnlgrp_TC_policy[] = {
+    [TCA_KIND] = { .type = NL_A_STRING, .optional = false, },
+    [TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = false, },
+    [TCA_CHAIN] = { .type = NL_A_U32, .optional = true, },
+};
+
+static const struct nl_policy rtnlgrp_link_policy[] = {
+    [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
+    [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
+};
+
+static const struct nl_policy tca_flower_policy[] = {
+    [TCA_FLOWER_CLASSID] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_INDEV] = { .type = NL_A_STRING, .max_len = IFNAMSIZ,
+                           .optional = true, },
+    [TCA_FLOWER_KEY_ETH_SRC] = { .type = NL_A_UNSPEC,
+                                 .min_len = ETH_ALEN, .optional = true, },
+    [TCA_FLOWER_KEY_ETH_DST] = { .type = NL_A_UNSPEC,
+                                 .min_len = ETH_ALEN, .optional = true, },
+    [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .type = NL_A_UNSPEC,
+                                      .min_len = ETH_ALEN,
+                                      .optional = true, },
+    [TCA_FLOWER_KEY_ETH_DST_MASK] = { .type = NL_A_UNSPEC,
+                                      .min_len = ETH_ALEN,
+                                      .optional = true, },
+    [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NL_A_U16, .optional = false, },
+    [TCA_FLOWER_KEY_ARP_SIP] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_ARP_TIP] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_ARP_SHA] = { .type = NL_A_UNSPEC,
+                                 .min_len = ETH_ALEN,
+                                 .optional = true, },
+    [TCA_FLOWER_KEY_ARP_THA] = { .type = NL_A_UNSPEC,
+                                 .min_len = ETH_ALEN,
+                                 .optional = true, },
+    [TCA_FLOWER_KEY_ARP_OP] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_KEY_ARP_SIP_MASK] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_ARP_TIP_MASK] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_ARP_SHA_MASK] = { .type = NL_A_UNSPEC,
+                                      .min_len = ETH_ALEN,
+                                      .optional = true, },
+    [TCA_FLOWER_KEY_ARP_THA_MASK] = { .type = NL_A_UNSPEC,
+                                      .min_len = ETH_ALEN,
+                                      .optional = true, },
+    [TCA_FLOWER_KEY_ARP_OP_MASK] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_FLAGS] = { .type = NL_A_U32, .optional = false, },
+    [TCA_FLOWER_ACT] = { .type = NL_A_NESTED, .optional = false, },
+    [TCA_FLOWER_KEY_IP_PROTO] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_IPV4_DST] = {.type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_IPV6_SRC] = { .type = NL_A_UNSPEC,
+                                  .min_len = sizeof(struct in6_addr),
+                                  .optional = true, },
+    [TCA_FLOWER_KEY_IPV6_DST] = { .type = NL_A_UNSPEC,
+                                  .min_len = sizeof(struct in6_addr),
+                                  .optional = true, },
+    [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC,
+                                       .min_len = sizeof(struct in6_addr),
+                                       .optional = true, },
+    [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .type = NL_A_UNSPEC,
+                                       .min_len = sizeof(struct in6_addr),
+                                       .optional = true, },
+    [TCA_FLOWER_KEY_TCP_SRC] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_TCP_DST] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_UDP_SRC] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_UDP_DST] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_SCTP_DST] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_MPLS_TTL] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_KEY_MPLS_TC] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_KEY_MPLS_BOS] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_KEY_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NL_A_U32,
+                                           .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NL_A_U32,
+                                           .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
+                                      .min_len = sizeof(struct in6_addr),
+                                      .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC,
+                                      .min_len = sizeof(struct in6_addr),
+                                      .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC,
+                                           .min_len = sizeof(struct in6_addr),
+                                           .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .type = NL_A_UNSPEC,
+                                           .min_len = sizeof(struct in6_addr),
+                                           .optional = true, },
+    [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NL_A_U16,
+                                          .optional = true, },
+    [TCA_FLOWER_KEY_FLAGS] = { .type = NL_A_BE32, .optional = true, },
+    [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NL_A_BE32, .optional = true, },
+    [TCA_FLOWER_KEY_IP_TTL] = { .type = NL_A_U8,
+                                .optional = true, },
+    [TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NL_A_U8,
+                                     .optional = true, },
+    [TCA_FLOWER_KEY_IP_TOS] = { .type = NL_A_U8,
+                                .optional = true, },
+    [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NL_A_U8,
+                                     .optional = true, },
+    [TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NL_A_U16,
+                                   .optional = true, },
+    [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NL_A_U16,
+                                        .optional = true, },
+    [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IP_TOS] = { .type = NL_A_U8,
+                                    .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NL_A_U8,
+                                         .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IP_TTL] = { .type = NL_A_U8,
+                                    .optional = true, },
+    [TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NL_A_U8,
+                                         .optional = true, },
+    [TCA_FLOWER_KEY_ENC_OPTS] = { .type = NL_A_NESTED, .optional = true, },
+    [TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NL_A_NESTED,
+                                       .optional = true, },
+    [TCA_FLOWER_KEY_CT_STATE] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CT_STATE_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CT_ZONE] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CT_MARK] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_CT_MARK_MASK] = { .type = NL_A_U32, .optional = true, },
+    [TCA_FLOWER_KEY_CT_LABELS] = { .type = NL_A_U128, .optional = true, },
+    [TCA_FLOWER_KEY_CT_LABELS_MASK] = { .type = NL_A_U128,
+                                        .optional = true, },
+    [TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NL_A_U8,
+                                     .optional = true, },
+    [TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NL_A_U8,
+                                          .optional = true, },
+    [TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NL_A_U8,
+                                     .optional = true, },
+    [TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NL_A_U8,
+                                          .optional = true, },
+    [TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NL_A_U8,
+                                     .optional = true, },
+    [TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NL_A_U8,
+                                          .optional = true, },
+    [TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NL_A_U8,
+                                     .optional = true, },
+    [TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NL_A_U8,
+                                          .optional = true, },
+};
+
 int
 main(int argc, char *argv[])
 {
@@ -229,10 +390,294 @@ int nlmon_link(void * args)
     }
 }
 
-int nlmon_tc(void * args OVS_UNUSED)
+int nlmon_tc(void * args)
 {
-    printf("not supported Will be added soon \n");
-    return ENOTSUP;
+    uint64_t buf_stub[4096];
+    struct nl_sock *sock;
+    struct ofpbuf buf;
+    int nsid, error;
+    __u16 f_proto = 0;
+    __u32 f_prio = 0;
+
+    init_socket(&sock, (enum rtnetlink_groups *) args);
+
+    ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);
+    for (;;) {
+        error = nl_sock_recv(sock, &buf, &nsid, false);
+        if (error == EAGAIN) {
+            /* Nothing to do. */
+        } else if (error == ENOBUFS) {
+            ovs_error(0, "network monitor socket overflowed");
+        } else if (error) {
+            ovs_fatal(error, "error on network monitor socket");
+        } else {
+            struct nlmsghdr *nlh;
+            struct tcmsg *tc;
+            struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_TC_policy)];
+
+            nlh = (struct nlmsghdr *) ofpbuf_at(&buf, 0, NLMSG_HDRLEN);
+            tc = (struct tcmsg *) ofpbuf_at(&buf, NLMSG_HDRLEN, sizeof *tc);
+            if (!tc) {
+                ovs_error(0, "received bad TC message (no TC header)");
+                continue;
+            }
+            /* TODO ADD: monitoring via dev name option */
+            if (nlh->nlmsg_type != RTM_NEWTFILTER) {
+                /* ignore Delete and Get filter messages */
+                continue;
+            }
+            if (!nl_policy_parse(&buf, NLMSG_HDRLEN + sizeof(struct tcmsg),
+                                 rtnlgrp_TC_policy,
+                                 attrs, ARRAY_SIZE(rtnlgrp_TC_policy))) {
+                ovs_error(0, "received bad rtnl message (policy)");
+                continue;
+            }
+
+            printf("filter ifindex %d nsid ", tc->tcm_ifindex);
+            if (nsid < 0) {
+                printf("local ");
+            } else {
+                printf("%u ", nsid);
+            }
+
+            f_proto = TC_H_MIN(tc->tcm_info);
+            f_prio = TC_H_MAJ(tc->tcm_info) >> 16;
+
+            if (f_proto) {
+                printf("protocol 0x%x ", ntohs(f_proto));
+            }
+            if (f_prio) {
+                printf("pref %u ", f_prio);
+            }
+
+            if (attrs[TCA_KIND]) {
+                printf("%s ", nl_attr_get_string(attrs[TCA_KIND]));
+            }
+            if (attrs[TCA_CHAIN]) {
+                printf("chain %u ", nl_attr_get_u32(attrs[TCA_CHAIN]));
+            }
+            if (tc->tcm_handle) {
+                printf("handle 0x%x ", tc->tcm_handle);
+            }
+            printf("\n");
+            if (attrs[TCA_OPTIONS]) {
+                nl_parse_tc_opts(attrs[TCA_OPTIONS]);
+            } else {
+                ovs_error(0, "BAD TC options");
+            }
+        }
+
+        nl_sock_wait(sock, POLLIN);
+        poll_block();
+    }
+
+
+    return 0;
+}
+
+static void nl_parse_tc_opts(struct nlattr * nlattr)
+{
+    struct nlattr *attrs[ARRAY_SIZE(tca_flower_policy)];
+    struct ds str_filter = DS_EMPTY_INITIALIZER;
+    unsigned char * tmp_str;
+    __u8 tmp_u8 = 0;
+    __u16 tmp_u16 = 0;
+    __u32 tmp_u32 = 0;
+    int i;
+    char buf[256];
+
+    if (!nl_parse_nested(nlattr, tca_flower_policy,
+                         attrs, ARRAY_SIZE(tca_flower_policy))) {
+        ovs_error(0, "failed to parse flower classifier options");
+        return ;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(tca_flower_policy); i++) {
+        if (!nl_attr_get_flag(attrs[i])) {
+            continue;
+        }
+        switch (attrs[i]->nla_type) {
+        case TCA_FLOWER_CLASSID:
+            ds_put_format(&str_filter, "  classid:%u\n",
+                             nl_attr_get_u32(attrs[i]));
+            break;
+        case TCA_FLOWER_INDEV:
+            ds_put_format(&str_filter, "  indev:%s\n",
+                            nl_attr_get_string(attrs[i]));
+            break;
+        case TCA_FLOWER_KEY_ETH_DST:
+            if (nl_attr_get_size(attrs[i]) == ETH_ALEN) {
+                ds_put_cstr(&str_filter, "  dst_mac:");
+                tmp_str = (unsigned char *) nl_attr_get(attrs[i]);
+                ds_put_format(&str_filter, "%02x", tmp_str[0]);
+                for (int j = 1; j < ETH_ALEN; j++) {
+                    ds_put_format(&str_filter, ":%02x", tmp_str[j]);
+                }
+                ds_put_cstr(&str_filter, "\n");
+            }
+            break;
+        case TCA_FLOWER_KEY_ETH_SRC:
+            if (nl_attr_get_size(attrs[i]) == ETH_ALEN) {
+                ds_put_cstr(&str_filter, "  src_mac:");
+                tmp_str = (unsigned char *) nl_attr_get(attrs[i]);
+                ds_put_format(&str_filter, "%02x", tmp_str[0]);
+                for (int j = 1; j < ETH_ALEN; j++) {
+                    ds_put_format(&str_filter, ":%02x", tmp_str[j]);
+                }
+                ds_put_cstr(&str_filter, "\n");
+            }
+            break;
+        case TCA_FLOWER_KEY_ETH_TYPE:
+             tmp_u16 = nl_attr_get_u16(attrs[i]);
+             ds_put_cstr(&str_filter, "  eth_type:");
+             if (tmp_u16 == htons(ETH_P_IP)) {
+                 ds_put_cstr(&str_filter, "ipv4\n");
+             } else if (tmp_u16 == htons(ETH_P_IPV6)) {
+                 ds_put_cstr(&str_filter, "ipv6\n");
+            } else if (tmp_u16 == htons(ETH_P_ARP)) {
+                 ds_put_cstr(&str_filter, "arp\n");
+            } else if (tmp_u16 == htons(ETH_P_RARP)) {
+                 ds_put_cstr(&str_filter, "rarp\n");
+            } else {
+                 ds_put_format(&str_filter, "%04x\n", ntohs(tmp_u16));
+            }
+            break;
+        case TCA_FLOWER_KEY_IP_PROTO:
+            tmp_u8 = nl_attr_get_u8(attrs[i]);
+            ds_put_cstr(&str_filter, "  ip_proto:");
+            switch (tmp_u8) {
+            case IPPROTO_TCP:
+                ds_put_cstr(&str_filter, "tcp\n");
+                break;
+            case IPPROTO_UDP:
+                ds_put_cstr(&str_filter, "udp\n");
+                break;
+            case IPPROTO_SCTP:
+                ds_put_cstr(&str_filter, "sctp\n");
+                break;
+            case IPPROTO_ICMP:
+                ds_put_cstr(&str_filter, "icmp\n");
+                break;
+            case IPPROTO_ICMPV6:
+                ds_put_cstr(&str_filter, "icmpv6\n");
+                break;
+            default:
+                ds_put_format(&str_filter, "%02x\n", tmp_u8);
+            }
+            break;
+        case TCA_FLOWER_KEY_IPV4_SRC:
+        case TCA_FLOWER_KEY_IPV6_SRC:
+            if (tmp_u16 == htons(ETH_P_IP)) {
+                inet_ntop(AF_INET, nl_attr_get(attrs[i]), buf, 256);
+            } else if (tmp_u16 == htons(ETH_P_IPV6)) {
+                inet_ntop(AF_INET6, nl_attr_get(attrs[i]), buf, 256);
+            } else {
+                continue;
+            }
+            ds_put_format(&str_filter, "  src_ip:%s\n", buf);
+            break;
+        case TCA_FLOWER_KEY_IPV4_DST:
+        case TCA_FLOWER_KEY_IPV6_DST:
+            if (tmp_u16 == htons(ETH_P_IP)) {
+                inet_ntop(AF_INET, nl_attr_get(attrs[i]), buf, 256);
+            } else if (tmp_u16 == htons(ETH_P_IPV6)) {
+                inet_ntop(AF_INET6, nl_attr_get(attrs[i]), buf, 256);
+            } else {
+                continue;
+            }
+            ds_put_format(&str_filter, "  dst_ip:%s\n", buf);
+            break;
+        case TCA_FLOWER_KEY_TCP_SRC:
+        case TCA_FLOWER_KEY_UDP_SRC:
+        case TCA_FLOWER_KEY_SCTP_SRC:
+            tmp_u16 = ntohs(nl_attr_get_u16(attrs[i]));
+            ds_put_format(&str_filter, "  src_port:%u\n", tmp_u16);
+        break;
+        case TCA_FLOWER_KEY_TCP_DST:
+        case TCA_FLOWER_KEY_UDP_DST:
+        case TCA_FLOWER_KEY_SCTP_DST:
+            tmp_u16 = ntohs(nl_attr_get_u16(attrs[i]));
+            ds_put_format(&str_filter, "  dst_port:%u\n", tmp_u16);
+        break;
+        case TCA_FLOWER_FLAGS:
+            tmp_u32 = nl_attr_get_u32(attrs[i]);
+            ds_put_cstr(&str_filter, "  filter-flags:[");
+            if (tmp_u32 & TCA_CLS_FLAGS_SKIP_HW) {
+                ds_put_cstr(&str_filter, "skip-hw, ");
+            }
+            if (tmp_u32 & TCA_CLS_FLAGS_SKIP_SW) {
+                ds_put_cstr(&str_filter, "skip-sw, ");
+            }
+            if (tmp_u32 & TCA_CLS_FLAGS_IN_HW) {
+                ds_put_cstr(&str_filter, "in-hw, ");
+            }
+            if (tmp_u32 & TCA_CLS_FLAGS_NOT_IN_HW) {
+                ds_put_cstr(&str_filter, "not-in-hw, ");
+            }
+            ds_truncate(&str_filter, str_filter.length - 2);
+            ds_put_cstr(&str_filter, "]\n");
+            if (nl_attr_get_flag(attrs[TCA_FLOWER_IN_HW_COUNT])) {
+                tmp_u32 = nl_attr_get_u32(attrs[TCA_FLOWER_IN_HW_COUNT]);
+                ds_put_format(&str_filter, "  in-hw-count : %u\n", tmp_u32);
+            }
+            break;
+        default:
+            continue;
+        }
+    }
+
+    printf("%s", ds_cstr(&str_filter));
+    if (attrs[TCA_FLOWER_ACT]) {
+        nl_parse_tc_acts((struct rtattr *) attrs[TCA_FLOWER_ACT]);
+    }
+
+    ds_destroy(&str_filter);
+}
+
+static void nl_parse_tc_acts(struct rtattr *rta){
+    struct rtattr *tb[33];
+    int i;
+
+    parse_rtattr_flags(tb, 33, RTA_DATA(rta), RTA_PAYLOAD(rta),
+                           NLA_F_NESTED);
+
+    for (i = 0; i < 33; i++) {
+        if (tb[i]) {
+            nl_parse_tc_act(tb[i]);
+        }
+    }
+}
+
+static void nl_parse_tc_act(struct rtattr *rta){
+    struct rtattr *tb[33];
+
+    parse_rtattr_flags(tb, 33, RTA_DATA(rta), RTA_PAYLOAD(rta),
+                           NLA_F_NESTED);
+
+    if (tb[1] == NULL) {
+        fprintf(stderr, "NULL Action!\n");
+        return;
+    }
+
+    printf("act name = %s\n", (char *) RTA_DATA(tb[1]));
+
+}
+static void parse_rtattr_flags(struct rtattr *tb[], int max,
+                struct rtattr *rta, int len, unsigned short flags) {
+    unsigned short type;
+
+    memset(tb, 0, sizeof(struct rtattr *) * max);
+    while (RTA_OK(rta, len)) {
+        type = rta->rta_type & ~flags;
+        if ((type <= max) && (!tb[type])) {
+            tb[type] = rta;
+        }
+        rta = RTA_NEXT(rta, len);
+    }
+    if (len) {
+        fprintf(stderr, "!!!Deficit %d, rta_len=%d\n",
+                len, rta->rta_len);
+    }
 }
 
 static void print_usage(void)
diff --git a/utilities/nlmon.h b/utilities/nlmon.h
new file mode 100644
index 000000000..750b64775
--- /dev/null
+++ b/utilities/nlmon.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ * Nicira, Inc.
+ * Copyright (c) 2016 Mellanox Technologies, Ltd.
+ *
+ * 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.
+ */
+
+#ifndef NLMON_H
+#define NLMON_H 1
+
+/*
+ * Flower classifier:
+ * This are a kernel definition of the TC flower attributes
+ * since we are not sure that the kernel header are always available
+ * added them here.
+ * */
+
+enum {
+        TCA_FLOWER_UNSPEC,
+        TCA_FLOWER_CLASSID,
+        TCA_FLOWER_INDEV,
+        TCA_FLOWER_ACT,
+        TCA_FLOWER_KEY_ETH_DST,
+        TCA_FLOWER_KEY_ETH_DST_MASK,
+        TCA_FLOWER_KEY_ETH_SRC,
+        TCA_FLOWER_KEY_ETH_SRC_MASK,
+        TCA_FLOWER_KEY_ETH_TYPE,
+        TCA_FLOWER_KEY_IP_PROTO,
+        TCA_FLOWER_KEY_IPV4_SRC,
+        TCA_FLOWER_KEY_IPV4_SRC_MASK,
+        TCA_FLOWER_KEY_IPV4_DST,
+        TCA_FLOWER_KEY_IPV4_DST_MASK,
+        TCA_FLOWER_KEY_IPV6_SRC,
+        TCA_FLOWER_KEY_IPV6_SRC_MASK,
+        TCA_FLOWER_KEY_IPV6_DST,
+        TCA_FLOWER_KEY_IPV6_DST_MASK,
+        TCA_FLOWER_KEY_TCP_SRC,
+        TCA_FLOWER_KEY_TCP_DST,
+        TCA_FLOWER_KEY_UDP_SRC,
+        TCA_FLOWER_KEY_UDP_DST,
+        TCA_FLOWER_FLAGS,
+        TCA_FLOWER_KEY_VLAN_ID,
+        TCA_FLOWER_KEY_VLAN_PRIO,
+        TCA_FLOWER_KEY_VLAN_ETH_TYPE,
+
+        TCA_FLOWER_KEY_ENC_KEY_ID,
+        TCA_FLOWER_KEY_ENC_IPV4_SRC,
+        TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,
+        TCA_FLOWER_KEY_ENC_IPV4_DST,
+        TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,
+        TCA_FLOWER_KEY_ENC_IPV6_SRC,
+        TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,
+        TCA_FLOWER_KEY_ENC_IPV6_DST,
+        TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,
+
+        TCA_FLOWER_KEY_TCP_SRC_MASK,
+        TCA_FLOWER_KEY_TCP_DST_MASK,
+        TCA_FLOWER_KEY_UDP_SRC_MASK,
+        TCA_FLOWER_KEY_UDP_DST_MASK,
+        TCA_FLOWER_KEY_SCTP_SRC_MASK,
+        TCA_FLOWER_KEY_SCTP_DST_MASK,
+
+        TCA_FLOWER_KEY_SCTP_SRC,
+        TCA_FLOWER_KEY_SCTP_DST,
+
+        TCA_FLOWER_KEY_ENC_UDP_SRC_PORT,
+        TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK,
+        TCA_FLOWER_KEY_ENC_UDP_DST_PORT,
+        TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK,
+
+        TCA_FLOWER_KEY_FLAGS,
+        TCA_FLOWER_KEY_FLAGS_MASK,
+
+        TCA_FLOWER_KEY_ICMPV4_CODE,
+        TCA_FLOWER_KEY_ICMPV4_CODE_MASK,
+        TCA_FLOWER_KEY_ICMPV4_TYPE,
+        TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,
+        TCA_FLOWER_KEY_ICMPV6_CODE,
+        TCA_FLOWER_KEY_ICMPV6_CODE_MASK,
+        TCA_FLOWER_KEY_ICMPV6_TYPE,
+        TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,
+
+        TCA_FLOWER_KEY_ARP_SIP,
+        TCA_FLOWER_KEY_ARP_SIP_MASK,
+        TCA_FLOWER_KEY_ARP_TIP,
+        TCA_FLOWER_KEY_ARP_TIP_MASK,
+        TCA_FLOWER_KEY_ARP_OP,
+        TCA_FLOWER_KEY_ARP_OP_MASK,
+        TCA_FLOWER_KEY_ARP_SHA,
+        TCA_FLOWER_KEY_ARP_SHA_MASK,
+        TCA_FLOWER_KEY_ARP_THA,
+        TCA_FLOWER_KEY_ARP_THA_MASK,
+
+        TCA_FLOWER_KEY_MPLS_TTL,
+        TCA_FLOWER_KEY_MPLS_BOS,
+        TCA_FLOWER_KEY_MPLS_TC,
+        TCA_FLOWER_KEY_MPLS_LABEL,
+
+        TCA_FLOWER_KEY_TCP_FLAGS,
+        TCA_FLOWER_KEY_TCP_FLAGS_MASK,
+        TCA_FLOWER_KEY_IP_TOS,
+        TCA_FLOWER_KEY_IP_TOS_MASK,
+        TCA_FLOWER_KEY_IP_TTL,
+        TCA_FLOWER_KEY_IP_TTL_MASK,
+
+        TCA_FLOWER_KEY_CVLAN_ID,
+        TCA_FLOWER_KEY_CVLAN_PRIO,
+        TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
+
+        TCA_FLOWER_KEY_ENC_IP_TOS,
+        TCA_FLOWER_KEY_ENC_IP_TOS_MASK,
+        TCA_FLOWER_KEY_ENC_IP_TTL,
+        TCA_FLOWER_KEY_ENC_IP_TTL_MASK,
+
+        TCA_FLOWER_KEY_ENC_OPTS,
+        TCA_FLOWER_KEY_ENC_OPTS_MASK,
+
+        TCA_FLOWER_IN_HW_COUNT,
+
+        TCA_FLOWER_KEY_PORT_SRC_MIN,
+        TCA_FLOWER_KEY_PORT_SRC_MAX,
+        TCA_FLOWER_KEY_PORT_DST_MIN,
+        TCA_FLOWER_KEY_PORT_DST_MAX,
+
+        TCA_FLOWER_KEY_CT_STATE,
+        TCA_FLOWER_KEY_CT_STATE_MASK,
+        TCA_FLOWER_KEY_CT_ZONE,
+        TCA_FLOWER_KEY_CT_ZONE_MASK,
+        TCA_FLOWER_KEY_CT_MARK,
+        TCA_FLOWER_KEY_CT_MARK_MASK,
+        TCA_FLOWER_KEY_CT_LABELS,
+        TCA_FLOWER_KEY_CT_LABELS_MASK,
+
+        __TCA_FLOWER_MAX,
+};
+
+#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1)
+
+/* tca flags definitions */
+#define TCA_CLS_FLAGS_SKIP_HW   (1 << 0) /* don't offload filter to HW */
+#define TCA_CLS_FLAGS_SKIP_SW   (1 << 1) /* don't use filter in SW */
+#define TCA_CLS_FLAGS_IN_HW     (1 << 2) /* filter is offloaded to HW */
+#define TCA_CLS_FLAGS_NOT_IN_HW (1 << 3) /* filter isn't offloaded to HW */
+
+#define TC_H_MAJ_MASK (0xFFFF0000U)
+#define TC_H_MIN_MASK (0x0000FFFFU)
+#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
+#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
+#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
+
+#endif /* nlmon.h */
-- 
2.27.0



More information about the dev mailing list