qca: add nss-userspace-oss

This commit is contained in:
bitthief 2023-02-03 05:52:54 +02:00
parent d5ffa68b18
commit 487f8d1df0
31 changed files with 4931 additions and 0 deletions

View File

@ -0,0 +1,45 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=libnl-nss
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/libnl-nss
SECTION:=Libs
CATEGORY:=Libraries
TITLE:=Framework to communicate between userspace applications and the kernel.
DEPENDS:=+libpthread +libnl +kmod-qca-nss-drv-netlink
endef
define Package/libnl-nss/description
A framework in the userspace that establishes communication between userspace applications and the kernel.
endef
TOOL_CFLAGS:= -I$(STAGING_DIR)/usr/include/qca-nss-clients \
-I$(STAGING_DIR)/usr/include/qca-nss-drv \
-I$(STAGING_DIR)/usr/include/libnl3
TOOL_LDFLAGS:= -L$(STAGING_DIR)/lib
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
CC="$(TARGET_CC)" \
AR="$(TARGET_AR) " \
CFLAGS="$(TOOL_CFLAGS)" \
LD_LIBRARY_PATH="$(TOOL_LDFLAGS)"
endef
define Build/InstallDev
$(INSTALL_DIR) $(STAGING_DIR)/usr/include/libnl-nss
$(CP) $(PKG_BUILD_DIR)/obj/libnl-nss.so $(STAGING_DIR)/usr/lib
$(CP) $(PKG_BUILD_DIR)/include/* $(STAGING_DIR)/usr/include/libnl-nss
endef
define Package/libnl-nss/install
$(INSTALL_DIR) $(1)/lib
$(INSTALL_DATA) $(PKG_BUILD_DIR)/obj/libnl-nss.so $(1)/lib
endef
$(eval $(call BuildPackage,libnl-nss))

View File

@ -0,0 +1,35 @@
MKDIR = @mkdir -p $(@D)
SRCPATH = ./
OBJPATH = obj
BINARY = $(OBJPATH)/libnl-nss.so
SOURCES = $(wildcard $(SRCPATH)/*.c)
OBJECTS = $(SOURCES:$(SRCPATH)/%.c=$(OBJPATH)/%.o)
HEADERS = $(wildcard $(SRCPATH)/*.h)
INCLUDE += -I./include
LDFLAGS = -lnl-3 -lnl-genl-3
EXTRA_CFLAGS = -Wall -Werror -fPIC -Wl,-z,relro -Wl,-z,now
EXTRA_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now
all: release
release: $(BINARY)
$(OBJPATH)/%.o: $(SRCPATH)/%.c $(HEADERS)
$(MKDIR)
@echo [CC] $@
@$(CC) -c $(CFLAGS) $(INCLUDE) $(EXTRA_CFLAGS) -o $@ $<
$(BINARY): $(OBJECTS)
@echo $(BINARY)
@echo [LD] $@
@$(CC) -shared -o $@ $^ $(LDFLAGS) $(LDLIBS)
clean:
@echo [Clean]
@rm -f $(OBJECTS)
@rm -f $(BINARY)
@rm -rf $(OBJPATH)
.PHONY: clean

View File

@ -0,0 +1,71 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSS_NLBASE_H__
#define __NSS_NLBASE_H__
/*
* TODO: Remove inter-dependencies between the header files.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <linux/socket.h>
#include <net/if.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/if_ether.h>
/* Generic Netlink header */
#include <netlink/genl/ctrl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/genl.h>
#if !defined (likely) || !defined (unlikely)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
/* NSS headers */
#include <nss_arch.h>
#include <nss_def.h>
#include <nss_cmn.h>
#include <nss_ipv4.h>
#include <nss_ipv6.h>
#include <nss_nlcmn_if.h>
#include <nss_dtls_cmn.h>
#include <nss_dtlsmgr.h>
#include <nss_nl_if.h>
#include <nss_nlsock_api.h>
#include <nss_nldtls_if.h>
#include <nss_nldtls_api.h>
#include <nss_nlist_api.h>
#include <nss_nlipv4_if.h>
#include <nss_nlipv4_api.h>
#include <nss_nlipv6_if.h>
#include <nss_nlipv6_api.h>
#include <nss_nlmcast_api.h>
#endif /* __NSS_NLBASE_H__ */

View File

@ -0,0 +1,119 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSS_NLDTLS_API_H__
#define __NSS_NLDTLS_API_H__
/** @addtogroup chapter_nldtls
This chapter describes Data Transport Layer Security (DTLS) APIs in the user space.
These APIs are wrapper functions for DTLS family specific operations.
*/
/** @addtogroup nss_nldtls_datatypes @{ */
/**
* Response callback for DTLS.
*
* @param[in] user_ctx User context (provided at socket open).
* @param[in] rule DTLS rule.
* @param[in] resp_ctx User data per callback.
*
* @return
* None.
*/
typedef void (*nss_nldtls_resp_t)(void *user_ctx, struct nss_nldtls_rule *rule, void *resp_ctx);
/**
* Event callback for DTLS.
*
* @param[in] user_ctx User context (provided at socket open).
* @param[in] rule DTLS rule.
*
* @return
* None.
*/
typedef void (*nss_nldtls_event_t)(void *user_ctx, struct nss_nldtls_rule *rule);
/**
* NSS NL DTLS response.
*/
struct nss_nldtls_resp {
void *data; /**< Response context. */
nss_nldtls_resp_t cb; /**< Response callback. */
};
/**
* NSS NL DTLS context.
*/
struct nss_nldtls_ctx {
struct nss_nlsock_ctx sock; /**< NSS socket context. */
nss_nldtls_event_t event; /**< NSS event callback function. */
};
/** @} *//* end_addtogroup nss_nldtls_datatypes */
/** @addtogroup nss_nldtls_functions @{ */
/**
* Opens NSS NL DTLS socket.
*
* @param[in] ctx NSS NL socket context allocated by the caller.
* @param[in] user_ctx User context stored per socket.
* @param[in] event_cb Event callback handler.
*
* @return
* Status of the open call.
*/
int nss_nldtls_sock_open(struct nss_nldtls_ctx *ctx, void *user_ctx, nss_nldtls_event_t event_cb);
/**
* Closes NSS NL DTLS socket.
*
* @param[in] ctx NSS NL context.
*
* @return
* None.
*/
void nss_nldtls_sock_close(struct nss_nldtls_ctx *ctx);
/**
* Send a DTLS rule synchronously to NSS NL NETLINK.
*
* @param[in] ctx NSS DTLS NL context.
* @param[in] rule DTLS rule.
* @param[in] cb Response callback handler.
* @param[in] data Data received from sender.
*
* @return
* Send status:
* - 0 -- Success.
* - Negative version error (-ve) -- Failure.
*/
int nss_nldtls_sock_send(struct nss_nldtls_ctx *ctx, struct nss_nldtls_rule *rule, nss_nldtls_resp_t cb, void *data);
/**
* Initializes create rule message.
*
* @param[in] rule DTLS rule.
* @param[in] type Type of command.
*
* @return
* None.
*/
void nss_nldtls_init_rule(struct nss_nldtls_rule *rule, enum nss_nldtls_cmd_type type);
/** @} *//* end_addtogroup nss_nldtls_functions */
#endif /* __NSS_NLDTLS_API_H__ */

View File

@ -0,0 +1,253 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSS_NLIPV4_API_H__
#define __NSS_NLIPV4_API_H__
#define NSS_IPV4_RULE_CREATE_IDENTIFIER_VALID 0x1000 /**< Identifier is valid. */
/** @addtogroup chapter_nlipv4
This chapter describes IPv4 APIs in the user space.
These APIs are wrapper functions for IPv4 family specific operations.
*/
/** @addtogroup nss_nlipv4_datatypes @{ */
/**
* Response callback for IPv4.
*
* @param[in] user_ctx User context (provided at socket open).
* @param[in] rule IPv4 rule.
* @param[in] resp_ctx User data per callback.
*
* @return
* None.
*/
typedef void (*nss_nlipv4_resp_t)(void *user_ctx, struct nss_nlipv4_rule *rule, void *resp_ctx);
/**
* Event callback for IPv4.
*
* @param[in] user_ctx User context (provided at socket open).
* @param[in] rule IPv4 rule.
*
* @return
* None.
*/
typedef void (*nss_nlipv4_event_t)(void *user_ctx, struct nss_nlipv4_rule *rule);
/**
* NSS NL IPv4 response.
*/
struct nss_nlipv4_resp {
void *data; /**< Response context. */
nss_nlipv4_resp_t cb; /**< Response callback. */
};
/**
* NSS NL IPv4 context.
*/
struct nss_nlipv4_ctx {
struct nss_nlsock_ctx sock; /**< NSS socket context. */
nss_nlipv4_event_t event; /**< NSS event callback function. */
};
/** @} *//* end_addtogroup nss_nlipv4_datatypes */
/** @addtogroup nss_nlipv4_functions @{ */
/**
* Opens NSS NL IPv4 socket.
*
* @param[in] ctx NSS NL socket context allocated by the caller.
* @param[in] user_ctx User context stored per socket.
* @param[in] event_cb Event callback handler.
*
* @return
* Status of the open call.
*/
int nss_nlipv4_sock_open(struct nss_nlipv4_ctx *ctx, void *user_ctx, nss_nlipv4_event_t event_cb);
/**
* Closes NSS NL IPv4 socket.
*
* @param[in] ctx NSS NL context.
*
* @return
* None.
*/
void nss_nlipv4_sock_close(struct nss_nlipv4_ctx *ctx);
/**
* Sends an IPv4 rule synchronously to NSS NETLINK.
*
* @param[in] ctx NSS NL IPv4 context.
* @param[in] rule IPv4 rule.
* @param[in] cb Response callback handler.
* @param[in] data Response data per callback.
*
* @return
* Send status:
* - 0 -- Success.
* - Negative version error (-ve) -- Failure.
*/
int nss_nlipv4_sock_send(struct nss_nlipv4_ctx *ctx, struct nss_nlipv4_rule *rule, nss_nlipv4_resp_t cb, void *data);
/**
* Initializes IPv4 rule message.
*
* @param[in] rule IPv4 rule.
* @param[in] type Command type.
*
* @return
* None.
*/
void nss_nlipv4_init_rule(struct nss_nlipv4_rule *rule, enum nss_ipv4_message_types type);
/**
* Initializes connection rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_conn_rule(struct nss_ipv4_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV4_RULE_CREATE_CONN_VALID;
}
/**
* Enables route flow.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_route_flow_rule(struct nss_ipv4_rule_create_msg *create)
{
create->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_ROUTED;
}
/**
* Enables bridge flow.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_bridge_flow_rule(struct nss_ipv4_rule_create_msg *create)
{
create->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_BRIDGE_FLOW;
}
/**
* Initializes TCP protocol rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_tcp_rule(struct nss_ipv4_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV4_RULE_CREATE_TCP_VALID;
}
/**
* Initializes PPPoE rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_pppoe_rule(struct nss_ipv4_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV4_RULE_CREATE_PPPOE_VALID;
}
/**
* Initializes QoS rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_qos_rule(struct nss_ipv4_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV4_RULE_CREATE_QOS_VALID;
}
/**
* Initializes DSCP rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_dscp_rule(struct nss_ipv4_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV4_RULE_CREATE_DSCP_MARKING_VALID;
}
/**
* Initializes VLAN rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_vlan_rule(struct nss_ipv4_rule_create_msg *create)
{
struct nss_ipv4_vlan_rule *primary;
struct nss_ipv4_vlan_rule *secondary;
primary = &create->vlan_primary_rule;
secondary = &create->vlan_secondary_rule;
create->valid_flags |= NSS_IPV4_RULE_CREATE_VLAN_VALID;
/*
* set the tags to default values
*/
primary->ingress_vlan_tag = NSS_NLIPV4_VLAN_ID_NOT_CONFIGURED;
primary->egress_vlan_tag = NSS_NLIPV4_VLAN_ID_NOT_CONFIGURED;
secondary->ingress_vlan_tag = NSS_NLIPV4_VLAN_ID_NOT_CONFIGURED;
secondary->egress_vlan_tag = NSS_NLIPV4_VLAN_ID_NOT_CONFIGURED;
}
/**
* Initializes Identifier rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv4_init_identifier_rule(struct nss_ipv4_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV4_RULE_CREATE_IDENTIFIER_VALID;
}
/** @} *//* end_addtogroup nss_nlipv4_functions */
#endif /* __NSS_NLIPV4_API_H__ */

View File

@ -0,0 +1,253 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSS_NLIPV6_API_H__
#define __NSS_NLIPV6_API_H__
#define NSS_IPV6_RULE_CREATE_IDENTIFIER_VALID 0x1000 /**< Identifier is valid. */
/** @addtogroup chapter_nlipv6
This chapter describes IPv6 APIs in the user space.
These APIs are wrapper functions for IPv6 family specific operations.
*/
/** @addtogroup nss_nlipv6_datatypes @{ */
/**
* Response callback for IPv6.
*
* @param[in] user_ctx User context (provided at socket open).
* @param[in] rule IPv6 rule.
* @param[in] resp_ctx user data per callback.
*
* @return
* None.
*/
typedef void (*nss_nlipv6_resp_t)(void *user_ctx, struct nss_nlipv6_rule *rule, void *resp_ctx);
/**
* Event callback for IPv6.
*
* @param[in] user_ctx User context (provided at socket open).
* @param[in] rule IPv6 Rule.
*
* @return
* None.
*/
typedef void (*nss_nlipv6_event_t)(void *user_ctx, struct nss_nlipv6_rule *rule);
/**
* NSS NL IPv6 response.
*/
struct nss_nlipv6_resp {
void *data; /**< Response context. */
nss_nlipv6_resp_t cb; /**< Response callback. */
};
/**
* NSS NL IPv6 context.
*/
struct nss_nlipv6_ctx {
struct nss_nlsock_ctx sock; /**< NSS socket context. */
nss_nlipv6_event_t event; /**< NSS event callback function. */
};
/** @} *//* end_addtogroup nss_nlipv6_datatypes */
/** @addtogroup nss_nlipv6_functions @{ */
/**
* Opens NSS NL IPv6 socket.
*
* @param[in] ctx NSS NL socket context allocated by the caller.
* @param[in] user_ctx User context stored per socket.
* @param[in] event_cb Event callback handler.
*
* @return
* Status of the open call.
*/
int nss_nlipv6_sock_open(struct nss_nlipv6_ctx *ctx, void *user_ctx, nss_nlipv6_event_t event_cb);
/**
* Closes NSS NL IPv6 socket.
*
* @param[in] ctx NSS NL context.
*
* @return
* None.
*/
void nss_nlipv6_sock_close(struct nss_nlipv6_ctx *ctx);
/**
* Sends an IPv6 rule synchronously to NSS NETLINK.
*
* @param[in] ctx NSS IPv6 NL context.
* @param[in] rule IPv6 rule.
* @param[in] cb Response callback handler.
* @param[in] data Response data per callback.
*
* @return
* Send status:
* - 0 -- Success.
* - Negative version error (-ve) -- Failure.
*/
int nss_nlipv6_sock_send(struct nss_nlipv6_ctx *ctx, struct nss_nlipv6_rule *rule, nss_nlipv6_resp_t cb, void *data);
/**
* Initializes rule message.
*
* @param[in] rule IPv6 rule.
* @param[in] type Command type.
*
* @return
* None.
*/
void nss_nlipv6_init_rule(struct nss_nlipv6_rule *rule, enum nss_ipv6_message_types type);
/**
* Initializes connection rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_conn_rule(struct nss_ipv6_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV6_RULE_CREATE_CONN_VALID;
}
/**
* Enables route flow.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_route_flow_rule(struct nss_ipv6_rule_create_msg *create)
{
create->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_ROUTED;
}
/**
* Enables bridge flow.
*
* @param[in] create create message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_bridge_flow_rule(struct nss_ipv6_rule_create_msg *create)
{
create->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_BRIDGE_FLOW;
}
/**
* Initializes TCP protocol rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_tcp_rule(struct nss_ipv6_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV6_RULE_CREATE_TCP_VALID;
}
/**
* Initializes PPPoE rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_pppoe_rule(struct nss_ipv6_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV6_RULE_CREATE_PPPOE_VALID;
}
/**
* Initializes QoS rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_qos_rule(struct nss_ipv6_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV6_RULE_CREATE_QOS_VALID;
}
/**
* Initializes DSCP rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_dscp_rule(struct nss_ipv6_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV6_RULE_CREATE_DSCP_MARKING_VALID;
}
/**
* Initializes VLAN rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_vlan_rule(struct nss_ipv6_rule_create_msg *create)
{
struct nss_ipv6_vlan_rule *primary;
struct nss_ipv6_vlan_rule *secondary;
primary = &create->vlan_primary_rule;
secondary = &create->vlan_secondary_rule;
create->valid_flags |= NSS_IPV6_RULE_CREATE_VLAN_VALID;
/*
* set the tags to default values
*/
primary->ingress_vlan_tag = NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED;
primary->egress_vlan_tag = NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED;
secondary->ingress_vlan_tag = NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED;
secondary->egress_vlan_tag = NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED;
}
/**
* Initializes Identifier rule for create message.
*
* @param[in] create Creates message.
*
* @return
* None.
*/
static inline void nss_nlipv6_init_identifier_rule(struct nss_ipv6_rule_create_msg *create)
{
create->valid_flags |= NSS_IPV6_RULE_CREATE_IDENTIFIER_VALID;
}
/** @} *//* end_addtogroup nss_nlipv6_functions */
#endif /* __NSS_NLIPV6_API_H__ */

View File

@ -0,0 +1,220 @@
/*
**************************************************************************
* Copyright (c) 2019,2021 The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSS_NLIST_H__
#define __NSS_NLIST_H__
/** @addtogroup chapter_nlist
This chapter describes Netlink list APIs in the user space.
*/
/** @ingroup nss_nlist_datatypes
* List node
*/
struct nss_nlist {
struct nss_nlist *next; /**< Next node. */
struct nss_nlist *prev; /**< Previous node. */
};
/** @addtogroup nss_nlist_functions @{ */
/**
* Initializes the list node.
*
* @param[in] node List node.
*
* @return
* None.
*/
static inline void nss_nlist_init(struct nss_nlist *node)
{
node->next = node->prev = node;
}
/**
* Gets the previous node.
*
* @param[in] node Previous node.
*
* @return
* Previous node or head node.
*/
static inline struct nss_nlist *nss_nlist_prev(struct nss_nlist *node)
{
return node->prev;
}
/**
* Gets the next node.
*
* @param[in] node Next node.
*
* @return
* Next node or head node.
*/
static inline struct nss_nlist *nss_nlist_next(struct nss_nlist *node)
{
return node->next;
}
/**
* Initializes the head node.
*
* @param[in] head Head of list.
*
* @return
* None.
*/
static inline void nss_nlist_init_head(struct nss_nlist *head)
{
nss_nlist_init(head);
}
/**
* Returns first node in the list.
*
* @param[in] head List head.
*
* @return
* First node.
*/
static inline struct nss_nlist *nss_nlist_first(struct nss_nlist *head)
{
return nss_nlist_next(head);
}
/**
* Returns last node in the list.
*
* @param[in] head List head.
*
* @return
* Last node.
*/
static inline struct nss_nlist *nss_nlist_last(struct nss_nlist *head)
{
return nss_nlist_prev(head);
}
/**
* Checks if list is empty.
*
* @param[in] head List head.
*
* @return
* TRUE if empty.
*/
static inline bool nss_nlist_isempty(struct nss_nlist *head)
{
struct nss_nlist *first = nss_nlist_first(head);
return first == head;
}
/**
* Checks if corresponding node is the last node.
*
* @param[in] head Head node.
* @param[in] node Node to check.
*
* @return
* TRUE if it is the last node.
*/
static inline bool nss_nlist_islast(struct nss_nlist *head, struct nss_nlist *node)
{
struct nss_nlist *last = nss_nlist_last(head);
return last == node;
}
/**
* Adds node to head of the list.
*
* @param[in] head List head.
* @param[in] node Node to add.
*
* @return
* None.
*/
static inline void nss_nlist_add_head(struct nss_nlist *head, struct nss_nlist *node)
{
struct nss_nlist *first = nss_nlist_first(head);
node->prev = head;
node->next = first;
first->prev = node;
head->next = node;
}
/**
* Adds node to tail of the list.
*
* @param[in] head List head.
* @param[in] node Node to add.
*
* @return
* None.
*/
static inline void nss_nlist_add_tail(struct nss_nlist *head, struct nss_nlist *node)
{
struct nss_nlist *last = nss_nlist_last(head);
node->next = head;
node->prev = last;
last->next = node;
head->prev = node;
}
/**
* Unlinks node from the list.
*
* @param[in] node Node to unlink.
*
* @return
* None.
*/
static inline void nss_nlist_unlink(struct nss_nlist *node)
{
struct nss_nlist *prev = nss_nlist_prev(node);
struct nss_nlist *next = nss_nlist_next(node);
prev->next = next;
next->prev = prev;
nss_nlist_init(node);
}
/** @} *//* end_addtogroup nss_nlist_functions */
/** @ingroup nss_nlist_macros
* Lists node iterator.
*
* @hideinitializer
* @param[in] _tmp Temporary node for assignment.
* @param[in] _head Head node to start.
*
* @return
* None.
*/
#define nss_nlist_iterate(_tmp, _head) \
for ((_tmp) = nss_nlist_first((_head)); \
!nss_nlist_islast((_head), (_tmp)); \
(_tmp) = nss_nlist_next((_tmp))
#endif /* __NSS_NLIST_H__ */

View File

@ -0,0 +1,102 @@
/*
**************************************************************************
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSS_NLMCAST_API_H__
#define __NSS_NLMCAST_API_H__
/** @addtogroup chapter_nlmcast
This chapter describes multicast APIs in the user space.
These APIs are wrapper functions for multicast specific operations.
*/
/** @addtogroup nss_nlmcast_datatypes @{ */
/**
* Event callback for multicast.
*
* @param[in] cmd Command received in generic Netlink header.
* @param[in] data Data received in Netlink message.
*/
typedef void (*nss_nlmcast_event_t)(int cmd, void *data);
/**
* NSS multicast context.
*/
struct nss_nlmcast_ctx {
struct nss_nlsock_ctx sock; /**< NSS socket context. */
nss_nlmcast_event_t event; /**< NSS event callback function. */
};
/** @} *//* end_addtogroup nss_nlmcast_datatypes */
/** @addtogroup nss_nlmcast_functions @{ */
/**
* Listens to NSS NL multicast event data.
*
* @param[in] ctx Multicast context.
*
* @return
* Listen status.
*/
int nss_nlmcast_sock_listen(struct nss_nlmcast_ctx *ctx);
/**
* Subscribe the multicast group to receive responses.
*
* @param[in] ctx Multicast context.
* @param[in] grp_name NSS NL group name.
*
* @return
* Subscription status.
*/
int nss_nlmcast_sock_join_grp(struct nss_nlmcast_ctx *ctx, char *grp_name);
/**
* Unsubscribe the multicast group to stop receiving responses.
*
* @param[in] ctx Multicast context.
* @param[in] grp_name NSS NL group name.
*
* @return
* Status of the operation.
*/
int nss_nlmcast_sock_leave_grp(struct nss_nlmcast_ctx *ctx, char *grp_name);
/**
* Opens a socket for listening to NSS NL event data.
*
* @param[in] ctx Multicast context.
* @param[in] cb Callback function.
* @param[in] family_name NSS NL family name.
*
* @return
* Status of the operation.
*/
int nss_nlmcast_sock_open(struct nss_nlmcast_ctx *ctx, nss_nlmcast_event_t cb, const char *family_name);
/**
* Closes socket.
*
* @param[in] ctx Multicast context.
*
* @return
* None.
*/
void nss_nlmcast_sock_close(struct nss_nlmcast_ctx *ctx);
/** @} *//* end_addtogroup nss_nlmcast_functions */
#endif /* __NSS_NLMCAST_API_H__ */

View File

@ -0,0 +1,197 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSS_NLSOCK_API_H__
#define __NSS_NLSOCK_API_H__
/** @addtogroup chapter_nlsocket
This chapter describes socket APIs for direct use.
@note1hang
Use these APIs(s) only if there are no available helpers for the specific family.
*/
/**
* @ingroup nss_nlsocket_datatypes
* NSS NL socket context.
*/
struct nss_nlsock_ctx {
/* Public, caller must populate using helpers */
const char *family_name; /**< Family name. */
void *user_ctx; /**< Socket user context. */
/* Private, maintained by the library */
pthread_t thread; /**< Response sync. */
pthread_spinlock_t lock; /**< Context lock. */
int ref_cnt; /**< References to the socket. */
struct nl_sock *nl_sk; /**< Linux NL socket. */
struct nl_cb *nl_cb; /**< NSS NL callback context. */
pid_t pid; /**< Process ID associated with the socket. */
int family_id; /**< Family identifier. */
int grp_id; /**< Group indentifier. */
bool is_avail; /**< Indicates if the socket is available to send or listen. */
};
/** @addtogroup nss_nlsocket_macros @{ */
/**
* Prints error log.
*
* @param[in] arg Argument to be printed
*/
#define nss_nlsock_log_error(arg, ...) printf("NSS_NLERROR(%s[%d]):"arg, __func__, __LINE__, ##__VA_ARGS__)
/**
* Prints arguments
*
* @param[in] arg Argument to be printed
*/
#define nss_nlsock_log_info(arg, ...) printf("NSS_NLINFO(%s[%d]):"arg, __func__, __LINE__, ##__VA_ARGS__)
/** @} *//* end_addtogroup nss_nlsocket_macros */
/** @addtogroup nss_nlsocket_functions @{ */
/**
* Sets family name.
*
* @param[in] sock Socket context.
* @param[in] name Family name.
*
* @return
* None.
*/
static inline void nss_nlsock_set_family(struct nss_nlsock_ctx *sock, const char *name)
{
sock->family_name = name;
}
/**
* Sets user context.
*
* @param[in] sock Socket context.
* @param[in] user User context.
*
* @return
* None.
*/
static inline void nss_nlsock_set_user_ctx(struct nss_nlsock_ctx *sock, void *user)
{
sock->user_ctx = user;
}
/**
* Extracts NSS NL message data.
*
* @param[in] msg NL message.
*
* @return
* Pointer to start of NSS NL message.
*/
static inline void *nss_nlsock_get_data(struct nl_msg *msg)
{
struct genlmsghdr *genl_hdr = nlmsg_data((nlmsg_hdr(msg)));
return genlmsg_data(genl_hdr);
}
/**
* Opens NSS NL family socket.
*
* @param[in] sock Socket context to be allocated by the caller.
* @param[in] cb Callback function for response.
*
* @return
* Status of the operation.
*
* @note The underlying entity should set the sock->family name for the socket to open.
*/
int nss_nlsock_open(struct nss_nlsock_ctx *sock, nl_recvmsg_msg_cb_t cb);
/**
* Closes NSS NL family socket.
*
* @param[in] sock Socket context.
*
* @return
* None.
*/
void nss_nlsock_close(struct nss_nlsock_ctx *sock);
/**
* Sends NSS NL message synchronously.
*
* @param[in] sock Socket context.
* @param[in] cm Common message header.
* @param[in] data Message data.
* @param[in] has_resp Determines if response is needed from kernel.
*
* @detdesc The function blocks until ack/error is received from the kernel
* and also blocks for the message response from the kernel if is_resp is TRUE
* @return
* Status of the send operation.
*/
int nss_nlsock_send(struct nss_nlsock_ctx *sock, struct nss_nlcmn *cm, void *data, bool has_resp);
/**
* Listens to asynchronous events from kernel.
*
* @param[in] sock Socket context.
*
* @return
* Listen status.
*/
int nss_nlsock_listen(struct nss_nlsock_ctx *sock);
/**
* Subscribes to multicast group.
*
* @param[in] sock Socket context.
* @param[in] grp_name NSS NL group name.
*
* @return
* Subscription status.
*/
int nss_nlsock_join_grp(struct nss_nlsock_ctx *sock, char *grp_name);
/**
* Unsubscribes from multicast group.
*
* @param[in] sock Socket context.
* @param[in] grp_name NSS NL group name.
*
* @return
* Status of the operation.
*/
int nss_nlsock_leave_grp(struct nss_nlsock_ctx *sock, char *grp_name);
/**
* Opens a socket for listening to NSS NL event data.
*
* @param[in] sock Socket context.
* @param[in] cb Callback function.
*
* @return
* Status of the operation.
*/
int nss_nlsock_open_mcast(struct nss_nlsock_ctx *sock, nl_recvmsg_msg_cb_t cb);
/** @} *//* end_addtogroup nss_nlsocket_functions */
#endif /* __NSS_NLSOCK_API_H__ */

View File

@ -0,0 +1,138 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#include <nss_nlbase.h>
#include <nss_nlsock_api.h>
#include <nss_nldtls_api.h>
/*
* nss_nldtls_sock_cb()
* Callback func for dtls netlink socket
*/
int nss_nldtls_sock_cb(struct nl_msg *msg, void *arg)
{
pid_t pid = getpid();
struct nss_nldtls_rule *rule = nss_nlsock_get_data(msg);
if (!rule) {
nss_nlsock_log_error("%d:failed to get NSS NL dtls header\n", pid);
return NL_SKIP;
}
uint8_t cmd = nss_nlcmn_get_cmd(&rule->cm);
switch (cmd) {
case NSS_NLDTLS_CMD_TYPE_CREATE_TUN:
case NSS_NLDTLS_CMD_TYPE_DESTROY_TUN:
case NSS_NLDTLS_CMD_TYPE_UPDATE_CONFIG:
case NSS_NLDTLS_CMD_TYPE_TX_PKTS:
return NL_OK;
default:
nss_nlsock_log_error("%d:unsupported message cmd type(%d)\n", pid, cmd);
return NL_SKIP;
}
}
/*
* nss_nldtls_sock_open()
* Opens the NSS dtls NL socket for usage
*/
int nss_nldtls_sock_open(struct nss_nldtls_ctx *ctx, void *user_ctx, nss_nldtls_event_t event_cb)
{
pid_t pid = getpid();
int error;
if (!ctx) {
nss_nlsock_log_error("%d: invalid parameters passed\n", pid);
return -EINVAL;
}
memset(ctx, 0, sizeof(*ctx));
nss_nlsock_set_family(&ctx->sock, NSS_NLDTLS_FAMILY);
nss_nlsock_set_user_ctx(&ctx->sock, user_ctx);
/*
* try opening the socket with Linux
*/
error = nss_nlsock_open(&ctx->sock, nss_nldtls_sock_cb);
if (error) {
nss_nlsock_log_error("%d:unable to open NSS dtls socket, error(%d)\n", pid, error);
goto fail;
}
return 0;
fail:
memset(ctx, 0, sizeof(*ctx));
return error;
}
/*
* nss_nldtls_sock_close()
* Close the NSS dtls NL socket
*/
void nss_nldtls_sock_close(struct nss_nldtls_ctx *ctx)
{
nss_nlsock_close(&ctx->sock);
memset(ctx, 0, sizeof(struct nss_nldtls_ctx));
}
/*
* nss_nldtls_sock_send()
* Send the dtls message synchronously through the socket
*/
int nss_nldtls_sock_send(struct nss_nldtls_ctx *ctx, struct nss_nldtls_rule *rule, nss_nldtls_resp_t cb, void *data)
{
int32_t family_id = ctx->sock.family_id;
struct nss_nldtls_resp *resp;
pid_t pid = getpid();
bool has_resp = false;
int error = 0;
if (!rule) {
nss_nlsock_log_error("%d:invalid NSS dtls rule\n", pid);
return -EINVAL;
}
if (cb) {
nss_nlcmn_set_cb_owner(&rule->cm, family_id);
resp = nss_nlcmn_get_cb_data(&rule->cm, family_id);
assert(resp);
resp->data = data;
resp->cb = cb;
has_resp = true;
}
error = nss_nlsock_send(&ctx->sock, &rule->cm, rule, has_resp);
if (error) {
nss_nlsock_log_error("%d:failed to send NSS dtls rule, error(%d)\n", pid, error);
}
return error;
}
/*
* nss_nldtls_init_rule()
* Initialize the dtls rule
*/
void nss_nldtls_init_rule(struct nss_nldtls_rule *rule, enum nss_nldtls_cmd_type type)
{
nss_nldtls_rule_init(rule, type);
}

View File

@ -0,0 +1,176 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#include <nss_nlbase.h>
#include <nss_nlsock_api.h>
#include <nss_nlipv4_api.h>
/*
* nss_nlipv4_sock_cb()
* NSS NL IPv4 callback
*/
int nss_nlipv4_sock_cb(struct nl_msg *msg, void *arg)
{
pid_t pid = getpid();
struct nss_nlipv4_ctx *ctx = (struct nss_nlipv4_ctx *)arg;
struct nss_nlsock_ctx *sock = &ctx->sock;
struct nss_nlipv4_rule *rule = nss_nlsock_get_data(msg);
if (!rule) {
nss_nlsock_log_error("%d:failed to get NSS NL IPv4 header\n", pid);
return NL_SKIP;
}
uint8_t cmd = nss_nlcmn_get_cmd(&rule->cm);
switch (cmd) {
case NSS_IPV4_TX_CREATE_RULE_MSG:
case NSS_IPV4_TX_DESTROY_RULE_MSG:
{
void *cb_data = nss_nlcmn_get_cb_data(&rule->cm, sock->family_id);
if (!cb_data) {
return NL_SKIP;
}
/*
* Note: The callback user can modify the CB content so it
* needs to locally save the response data for further use
* after the callback is completed
*/
struct nss_nlipv4_resp resp;
memcpy(&resp, cb_data, sizeof(struct nss_nlipv4_resp));
/*
* clear the ownership of the CB so that callback user can
* use it if needed
*/
nss_nlcmn_clr_cb_owner(&rule->cm);
if (!resp.cb) {
nss_nlsock_log_info("%d:no IPv4 response callback for cmd(%d)\n", pid, cmd);
return NL_SKIP;
}
resp.cb(sock->user_ctx, rule, resp.data);
return NL_OK;
}
case NSS_IPV4_RX_CONN_STATS_SYNC_MSG:
{
nss_nlipv4_event_t event = ctx->event;
assert(event);
event(sock->user_ctx, rule);
return NL_OK;
}
default:
nss_nlsock_log_error("%d:unsupported message cmd type(%d)\n", pid, cmd);
return NL_SKIP;
}
}
/*
* nss_nlipv4_sock_open()
* this opens the NSS IPv4 NL socket for usage
*/
int nss_nlipv4_sock_open(struct nss_nlipv4_ctx *ctx, void *user_ctx, nss_nlipv4_event_t event_cb)
{
pid_t pid = getpid();
int error;
if (!ctx) {
nss_nlsock_log_error("%d: invalid parameters passed\n", pid);
return -EINVAL;
}
memset(ctx, 0, sizeof(*ctx));
nss_nlsock_set_family(&ctx->sock, NSS_NLIPV4_FAMILY);
nss_nlsock_set_user_ctx(&ctx->sock, user_ctx);
/*
* try opening the socket with Linux
*/
error = nss_nlsock_open(&ctx->sock, nss_nlipv4_sock_cb);
if (error) {
nss_nlsock_log_error("%d:unable to open NSS IPv4 socket, error(%d)\n", pid, error);
goto fail;
}
return 0;
fail:
memset(ctx, 0, sizeof(*ctx));
return error;
}
/*
* nss_nlipv4_sock_close()
* close the NSS IPv4 NL socket
*/
void nss_nlipv4_sock_close(struct nss_nlipv4_ctx *ctx)
{
nss_nlsock_close(&ctx->sock);
}
/*
* nss_nlipv4_sock_send()
* register callback and send the IPv4 message synchronously through the socket
*/
int nss_nlipv4_sock_send(struct nss_nlipv4_ctx *ctx, struct nss_nlipv4_rule *rule, nss_nlipv4_resp_t cb, void *data)
{
int32_t family_id = ctx->sock.family_id;
struct nss_nlipv4_resp *resp;
pid_t pid = getpid();
bool has_resp = false;
int error;
if (!rule) {
nss_nlsock_log_error("%d:invalid NSS IPv4 rule\n", pid);
return -ENOMEM;
}
if (cb) {
nss_nlcmn_set_cb_owner(&rule->cm, family_id);
resp = nss_nlcmn_get_cb_data(&rule->cm, family_id);
assert(resp);
resp->data = data;
resp->cb = cb;
has_resp = true;
}
error = nss_nlsock_send(&ctx->sock, &rule->cm, rule, has_resp);
if (error) {
nss_nlsock_log_error("%d:failed to send NSS IPv4 rule, error(%d)\n", pid, error);
return error;
}
return 0;
}
/*
* nss_nlipv4_init_rule()
* init the rule message
*/
void nss_nlipv4_init_rule(struct nss_nlipv4_rule *rule, enum nss_ipv4_message_types type)
{
nss_nlipv4_rule_init(rule, type);
}

View File

@ -0,0 +1,176 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#include <nss_nlbase.h>
#include <nss_nlsock_api.h>
#include <nss_nlipv6_api.h>
/*
* nss_nlipv6_sock_cb()
* NSS NL IPv6 callback
*/
int nss_nlipv6_sock_cb(struct nl_msg *msg, void *arg)
{
pid_t pid = getpid();
struct nss_nlipv6_ctx *ctx = (struct nss_nlipv6_ctx *)arg;
struct nss_nlsock_ctx *sock = &ctx->sock;
struct nss_nlipv6_rule *rule = nss_nlsock_get_data(msg);
if (!rule) {
nss_nlsock_log_error("%d:failed to get NSS NL IPv6 header\n", pid);
return NL_SKIP;
}
uint8_t cmd = nss_nlcmn_get_cmd(&rule->cm);
switch (cmd) {
case NSS_IPV6_TX_CREATE_RULE_MSG:
case NSS_IPV6_TX_DESTROY_RULE_MSG:
{
void *cb_data = nss_nlcmn_get_cb_data(&rule->cm, sock->family_id);
if (!cb_data) {
return NL_SKIP;
}
/*
* Note: The callback user can modify the CB content so it
* needs to locally save the response data for further use
* after the callback is completed
*/
struct nss_nlipv6_resp resp;
memcpy(&resp, cb_data, sizeof(struct nss_nlipv6_resp));
/*
* clear the ownership of the CB so that callback user can
* use it if needed
*/
nss_nlcmn_clr_cb_owner(&rule->cm);
if (!resp.cb) {
nss_nlsock_log_info("%d:no IPv6 response callback for cmd(%d)\n", pid, cmd);
return NL_SKIP;
}
resp.cb(sock->user_ctx, rule, resp.data);
return NL_OK;
}
case NSS_IPV6_RX_CONN_STATS_SYNC_MSG:
{
nss_nlipv6_event_t event = ctx->event;
assert(event);
event(sock->user_ctx, rule);
return NL_OK;
}
default:
nss_nlsock_log_error("%d:unsupported message cmd type(%d)\n", pid, cmd);
return NL_SKIP;
}
}
/*
* nss_nlipv6_sock_open()
* this opens the NSS IPv6 NL socket for usage
*/
int nss_nlipv6_sock_open(struct nss_nlipv6_ctx *ctx, void *user_ctx, nss_nlipv6_event_t event_cb)
{
pid_t pid = getpid();
int error;
if (!ctx) {
nss_nlsock_log_error("%d: invalid parameters passed\n", pid);
return -EINVAL;
}
memset(ctx, 0, sizeof(*ctx));
nss_nlsock_set_family(&ctx->sock, NSS_NLIPV6_FAMILY);
nss_nlsock_set_user_ctx(&ctx->sock, user_ctx);
/*
* try opening the socket with Linux
*/
error = nss_nlsock_open(&ctx->sock, nss_nlipv6_sock_cb);
if (error) {
nss_nlsock_log_error("%d:unable to open NSS IPv6 socket, error(%d)\n", pid, error);
goto fail;
}
return 0;
fail:
memset(ctx, 0, sizeof(*ctx));
return error;
}
/*
* nss_nlipv6_sock_close()
* close the NSS IPv6 NL socket
*/
void nss_nlipv6_sock_close(struct nss_nlipv6_ctx *ctx)
{
nss_nlsock_close(&ctx->sock);
}
/*
* nss_nlipv6_sock_send()
* register callback and send the IPv6 message synchronously through the socket
*/
int nss_nlipv6_sock_send(struct nss_nlipv6_ctx *ctx, struct nss_nlipv6_rule *rule, nss_nlipv6_resp_t cb, void *data)
{
int32_t family_id = ctx->sock.family_id;
struct nss_nlipv6_resp *resp;
pid_t pid = getpid();
bool has_resp = false;
int error;
if (!rule) {
nss_nlsock_log_error("%d:invalid NSS IPv6 rule\n", pid);
return -ENOMEM;
}
if (cb) {
nss_nlcmn_set_cb_owner(&rule->cm, family_id);
resp = nss_nlcmn_get_cb_data(&rule->cm, family_id);
assert(resp);
resp->data = data;
resp->cb = cb;
has_resp = true;
}
error = nss_nlsock_send(&ctx->sock, &rule->cm, rule, has_resp);
if (error) {
nss_nlsock_log_error("%d:failed to send NSS IPv6 rule, error(%d)\n", pid, error);
return error;
}
return 0;
}
/*
* nss_nlipv6_init_rule()
* init the rule message
*/
void nss_nlipv6_init_rule(struct nss_nlipv6_rule *rule, enum nss_ipv6_message_types type)
{
nss_nlipv6_rule_init(rule, type);
}

View File

@ -0,0 +1,146 @@
/*
**************************************************************************
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#include <nss_nlbase.h>
#include <nss_nlsock_api.h>
#include <nss_nlmcast_api.h>
/*
* nss_nlmcast_sock_cb()
* NSS NL mcast callback.
*/
static int nss_nlmcast_sock_cb(struct nl_msg *msg, void *arg)
{
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)arg;
struct genlmsghdr *genl_hdr = nlmsg_data((nlmsg_hdr(msg)));
uint8_t cmd = genl_hdr->cmd;
void *data = nss_nlsock_get_data(msg);
if (!data) {
nss_nlsock_log_error("%d:failed to get NSS NL msg header\n", getpid());
return NL_SKIP;
}
nss_nlmcast_event_t event = ctx->event;
assert(event);
event(cmd, data);
return NL_OK;
}
/*
* nss_nlmcast_sock_open()
* Open the NL socket for listening to MCAST events from kernel.
*/
int nss_nlmcast_sock_open(struct nss_nlmcast_ctx *ctx, nss_nlmcast_event_t event_cb, const char *family_name)
{
int error;
if (!ctx || !event_cb) {
nss_nlsock_log_error("Invalid parameters passed\n");
return -EINVAL;
}
memset(ctx, 0, sizeof(*ctx));
nss_nlsock_set_family(&ctx->sock, family_name);
/*
* Subscribe to the NSS NL Multicast group.
*/
error = nss_nlsock_open_mcast(&ctx->sock, nss_nlmcast_sock_cb);
if (error) {
nss_nlsock_log_error("Unable to create socket, error(%d)\n", error);
return error;
}
ctx->event = event_cb;
return 0;
}
/*
* nss_nlmcast_sock_close()
* Close the NL socket.
*/
void nss_nlmcast_sock_close(struct nss_nlmcast_ctx *ctx)
{
nss_nlsock_close(&ctx->sock);
}
/*
* nss_nlmcast_sock_join_grp()
* Subscribe for MCAST group from kernel.
*/
int nss_nlmcast_sock_join_grp(struct nss_nlmcast_ctx *ctx, char *grp_name)
{
int error;
if (!ctx || !grp_name) {
nss_nlsock_log_error("Invalid parameters passed\n");
return -EINVAL;
}
error = nss_nlsock_join_grp(&ctx->sock, grp_name);
if (error) {
nss_nlsock_log_error("Unable to subscribe for mcast group, error(%d)\n", error);
return error;
}
return 0;
}
/*
* nss_nlmcast_sock_leave_grp()
* Unsubscribe for MCAST group from kernel.
*/
int nss_nlmcast_sock_leave_grp(struct nss_nlmcast_ctx *ctx, char *grp_name)
{
int error;
if (!ctx || !grp_name) {
nss_nlsock_log_error("Invalid parameters passed\n");
return -EINVAL;
}
error = nss_nlsock_leave_grp(&ctx->sock, grp_name);
if (error) {
nss_nlsock_log_error("Unable to unsubscribe for mcast group, error(%d)\n", error);
return error;
}
return 0;
}
/*
* nss_nlmcast_sock_listen()
* Listen for MCAST events from kernel
*/
int nss_nlmcast_sock_listen(struct nss_nlmcast_ctx *ctx)
{
int error;
if (!ctx) {
nss_nlsock_log_error("Invalid parameters passed\n");
return -EINVAL;
}
error = nss_nlsock_listen(&ctx->sock);
if (error) {
nss_nlsock_log_error("Unable to listen to mcast events, error(%d)\n", error);
return error;
}
return 0;
}

View File

@ -0,0 +1,498 @@
/*
**************************************************************************
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* @file netlink socket handler
*/
#include <nss_nlbase.h>
#include <nss_nlsock_api.h>
/*
* nss_nlsock_deinit()
* de-initialize the socket
*/
static void nss_nlsock_deinit(struct nss_nlsock_ctx *sock)
{
assert(sock);
nl_cb_put(sock->nl_cb);
sock->nl_cb = NULL;
nl_socket_free(sock->nl_sk);
sock->nl_sk = NULL;
}
/*
* nss_nlsock_init()
* initialize the socket and callback
*/
static int nss_nlsock_init(struct nss_nlsock_ctx *sock, nl_recvmsg_msg_cb_t cb)
{
int error;
assert(sock);
/*
* Initialize spinlock
*/
error = pthread_spin_init(&sock->lock, PTHREAD_PROCESS_PRIVATE);
if (error) {
nss_nlsock_log_error("Failed to init spinlock for family(%s), error %d\n", sock->family_name, error);
return error;
}
sock->pid = getpid();
/*
* create callback
*/
sock->nl_cb = nl_cb_alloc(NL_CB_CUSTOM);
if (!sock->nl_cb) {
nss_nlsock_log_error("%d:failed to alloc callback for family(%s)\n",sock->pid, sock->family_name);
goto fail1;
}
/*
* register callback
*/
nl_cb_set(sock->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, cb, sock);
/*
* Create netlink socket
*/
sock->nl_sk = nl_socket_alloc_cb(sock->nl_cb);
if (!sock->nl_sk) {
nss_nlsock_log_error("%d:failed to alloc socket for family(%s)\n", sock->pid, sock->family_name);
goto fail2;
}
sock->ref_cnt = 1;
/*
* is_avail is set to indicate the socket is available for send/listen
*/
sock->is_avail = true;
return 0;
fail2:
nl_cb_put(sock->nl_cb);
sock->nl_cb = NULL;
fail1:
pthread_spin_destroy(&sock->lock);
sock->lock = (pthread_spinlock_t)0;
return -ENOMEM;
}
/*
* nss_nlsock_deref()
* decrement the reference count and free socket resources if '0'
*/
static inline void nss_nlsock_deref(struct nss_nlsock_ctx *sock)
{
assert(sock->ref_cnt > 0);
pthread_spin_lock(&sock->lock);
if (--sock->ref_cnt) {
pthread_spin_unlock(&sock->lock);
return;
}
/*
* When there are no more references on the socket,
* deinitialize the socket and destroy the spin lock
* created during nss_nlsock_init
*/
nss_nlsock_deinit(sock);
pthread_spin_unlock(&sock->lock);
pthread_spin_destroy(&sock->lock);
sock->lock = (pthread_spinlock_t)0;
}
/*
* nss_nlsock_ref()
* Increment the reference count.
*
* if ref_cnt == 0, return false
* if ref_cnt != 0, increment the socket reference count and return true
*/
static inline bool nss_nlsock_ref(struct nss_nlsock_ctx *sock)
{
/*
* if ref count is 0, it means there are no references
* on the socket and so return false. Socket will eventually be
* freed by nss_nlsock_deinit else increment the ref count
*/
pthread_spin_lock(&sock->lock);
if (sock->ref_cnt == 0) {
pthread_spin_unlock(&sock->lock);
return false;
}
sock->ref_cnt++;
pthread_spin_unlock(&sock->lock);
return true;
}
/*
* nss_nlsock_listen_callback()
* listen to responses from the netlink socket
*
* The API keeps listening for the responses on the netlink socket
* until socket close is initiated and there are no more
* responses on the socket
*/
static void *nss_nlsock_listen_callback(void *arg)
{
struct nss_nlsock_ctx *sock = (struct nss_nlsock_ctx *)arg;
assert(sock);
/*
* drain responses on the socket
*/
for (;;) {
/*
* if, socket is freed then break out
*/
if (!nss_nlsock_ref(sock)) {
break;
}
/*
* get or block for pending messages
*/
nl_recvmsgs(sock->nl_sk, sock->nl_cb);
nss_nlsock_deref(sock);
}
return NULL;
}
/*
* nss_nlsock_msg_init()
* Initialize parameters to send message down the socket
*/
static int nss_nlsock_msg_init(struct nss_nlsock_ctx *sock, struct nss_nlcmn *cm, void *data, struct nl_msg *msg)
{
int pid = sock->pid;
void *user_hdr;
uint32_t ver;
uint8_t cmd;
int len;
ver = nss_nlcmn_get_ver(cm);
len = nss_nlcmn_get_len(cm);
cmd = nss_nlcmn_get_cmd(cm);
/*
* create space for user header
*/
user_hdr = genlmsg_put(msg, pid, NL_AUTO_SEQ, sock->family_id, len, 0, cmd, ver);
if (!user_hdr) {
nss_nlsock_log_error("%d:failed to put message header of len(%d)\n", pid, len);
return -ENOMEM;
}
memcpy(user_hdr, data, len);
return 0;
}
/*
* nss_nlsock_leave_grp()
* nl socket unsubscribe for the multicast group
*/
int nss_nlsock_leave_grp(struct nss_nlsock_ctx *sock, char *grp_name)
{
int error;
assert(sock->ref_cnt > 0);
/*
* Resolve the group
*/
sock->grp_id = genl_ctrl_resolve_grp(sock->nl_sk, sock->family_name, grp_name);
if (sock->grp_id < 0) {
nss_nlsock_log_error("failed to resolve group(%s)\n", grp_name);
return -EINVAL;
}
/*
* Unsubscribe for the mcast async events
*/
error = nl_socket_drop_memberships(sock->nl_sk, sock->grp_id, 0);
if (error < 0) {
nss_nlsock_log_error("failed to deregister grp(%s)\n", grp_name);
return error;
}
return 0;
}
/*
* nss_nlsock_join_grp()
* nl socket subscribe for the multicast group
*/
int nss_nlsock_join_grp(struct nss_nlsock_ctx *sock, char *grp_name)
{
int error;
assert(sock->ref_cnt > 0);
/*
* Resolve the group
*/
sock->grp_id = genl_ctrl_resolve_grp(sock->nl_sk, sock->family_name, grp_name);
if (sock->grp_id < 0) {
nss_nlsock_log_error("failed to resolve group(%s)\n", grp_name);
return -EINVAL;
}
/*
* Subscribe for the mcast async events
*/
error = nl_socket_add_memberships(sock->nl_sk, sock->grp_id, 0);
if (error < 0) {
nss_nlsock_log_error("failed to register grp(%s)\n", grp_name);
return error;
}
return 0;
}
/*
* nss_nlsock_open_mcast()
* Open the socket for async events
*/
int nss_nlsock_open_mcast(struct nss_nlsock_ctx *sock, nl_recvmsg_msg_cb_t cb)
{
int error;
assert(sock);
error = nss_nlsock_init(sock, cb);
if (error) {
nss_nlsock_log_error("%d:failed to initialize socket(%s)\n", sock->pid, sock->family_name);
return error;
}
/*
* Disable seq number and auto ack checks for sockets listening for mcast events
*/
nl_socket_disable_seq_check(sock->nl_sk);
nl_socket_disable_auto_ack(sock->nl_sk);
/*
* Connect the socket with the netlink bus
*/
if (genl_connect(sock->nl_sk)) {
nss_nlsock_log_error("%d:failed to connect socket for family(%s)\n", sock->pid, sock->family_name);
error = -EBUSY;
goto free_sock;
}
return 0;
free_sock:
nss_nlsock_deref(sock);
return error;
}
/*
* nss_nlsock_send()
* send a message synchronously through the socket
*/
int nss_nlsock_send(struct nss_nlsock_ctx *sock, struct nss_nlcmn *cm, void *data, bool has_resp)
{
int pid = sock->pid;
struct nl_msg *msg;
int error;
/*
* return -EBUSY if the socket is currently unavailable for sending message
*/
pthread_spin_lock(&sock->lock);
if (!sock->is_avail) {
pthread_spin_unlock(&sock->lock);
return -EBUSY;
}
/*
* To indicate the socket is unavailable until the current thread completes the send/listen.
* This is to prevent other threads from simultaneous send/listen.
*/
sock->is_avail = false;
pthread_spin_unlock(&sock->lock);
/*
* allocate new message buffer
*/
msg = nlmsg_alloc();
if (!msg) {
nss_nlsock_log_error("%d:failed to allocate message buffer\n", pid);
sock->is_avail = true;
return -ENOMEM;
}
/*
* Holds a reference on the socket until msg is sent down to the kernel
*/
if (!nss_nlsock_ref(sock)) {
nss_nlsock_log_error("%d:failed to get NL socket\n", pid);
nlmsg_free(msg);
sock->is_avail = true;
return -EINVAL;
}
/*
* Initialize message parameters
*/
error = nss_nlsock_msg_init(sock, cm, data, msg);
if (error) {
nss_nlsock_log_error("%d:failed to initialize message structure (family:%s, error:%d)\n",
pid, sock->family_name, error);
nss_nlsock_deref(sock);
nlmsg_free(msg);
sock->is_avail = true;
return error;
}
/*
* If has_resp is true and msg is sent to FW, then there will be two
* netlink messages coming from kernel - FW response and ACK
* If msg fails in netlink, then error will be returned from kernel.
* If has_resp is false, then there is only one netlink message
* coming from kernel: either ACK or error
* In case firmware response is sent before nl_recvmsgs is invoked,
* the response will be queued until the listener is available.
*/
error = nl_send_sync(sock->nl_sk, msg);
if (error < 0) {
nss_nlsock_log_error("%d:failed to send (family:%s, error:%d)\n", pid, sock->family_name, error);
nss_nlsock_deref(sock);
sock->is_avail = true;
return error;
}
if (has_resp) {
nl_recvmsgs(sock->nl_sk, sock->nl_cb);
}
nss_nlsock_deref(sock);
sock->is_avail = true;
return 0;
}
/*
* nss_nlsock_listen()
* listen for async events on the socket
*/
int nss_nlsock_listen(struct nss_nlsock_ctx *sock)
{
int error;
assert(sock->ref_cnt > 0);
/*
* return -EBUSY if the socket is currently unavailable for listening
*/
if (!sock->is_avail) {
return -EBUSY;
}
/*
* To indicate the socket is unavailable until the current thread completes the send/listen.
* This is to prevent other threads from simultaneous send/listen.
*/
sock->is_avail = false;
/*
* Create an async thread for clearing the pending resp on the socket asynchronously
*/
error = pthread_create(&sock->thread, NULL, nss_nlsock_listen_callback, sock);
if (error) {
nss_nlsock_log_error("%d:failed to create sync thread for family(%s)\n", sock->pid, sock->family_name);
return error;
}
return 0;
}
/*
* nss_nlsock_close()
* close the allocated socket and all associated memory
*/
void nss_nlsock_close(struct nss_nlsock_ctx *sock)
{
assert(sock);
assert(sock->nl_sk);
assert(sock->ref_cnt > 0);
/*
* put the reference down for the socket
*/
nss_nlsock_deref(sock);
/*
* wait for the async thread to complete
*/
if (sock->thread) {
pthread_join(sock->thread, NULL);
sock->thread = NULL;
}
}
/*
* nss_nlsock_open()
* open a socket for unicast communication with the generic netlink framework
*/
int nss_nlsock_open(struct nss_nlsock_ctx *sock, nl_recvmsg_msg_cb_t cb)
{
int error = 0;
assert(sock);
error = nss_nlsock_init(sock, cb);
if (error) {
nss_nlsock_log_error("%d:failed to initialize socket(%s)\n", sock->pid, sock->family_name);
return error;
}
/*
* Connect the socket with the netlink bus
*/
if (genl_connect(sock->nl_sk)) {
nss_nlsock_log_error("%d:failed to connect socket for family(%s)\n", sock->pid, sock->family_name);
error = -EBUSY;
goto free_sock;
}
/*
* resolve the family
*/
sock->family_id = genl_ctrl_resolve(sock->nl_sk, sock->family_name);
if (sock->family_id <= 0) {
nss_nlsock_log_error("%d:failed to resolve family(%s)\n", sock->pid, sock->family_name);
error = -EINVAL;
goto free_sock;
}
return 0;
free_sock:
nss_nlsock_deref(sock);
return error;
}

View File

@ -0,0 +1,39 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=nssinfo
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/nssinfo
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Userspace utility for fetching stats from NSS
DEPENDS:=+libncurses +libnl-nss
endef
define Package/nssinfo/description
A userspace utility for fetching stats from NSS.
endef
TOOL_CFLAGS:= -I$(STAGING_DIR)/usr/include/qca-nss-clients \
-I$(STAGING_DIR)/usr/include/qca-nss-drv \
-I$(STAGING_DIR)/usr/include/libnl3 \
-I$(STAGING_DIR)/usr/include/libnl-nss
TOOL_LDFLAGS:= -L$(STAGING_DIR)/lib
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
CC="$(TARGET_CC)" \
CFLAGS="$(TOOL_CFLAGS)" \
LD_LIBRARY_PATH="$(TOOL_LDFLAGS)"
endef
define Package/nssinfo/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/obj/nssinfo $(1)/usr/sbin/
endef
$(eval $(call BuildPackage,nssinfo))

View File

@ -0,0 +1,35 @@
MKDIR = mkdir -p $(@D)
SRCPATH = src
OBJPATH = obj
SRCDIR = ./
BINARY = $(OBJPATH)/nssinfo
SOURCES = $(wildcard $(SRCDIR)/src/*.c)
HEADERS = $(wildcard $(SRCDIR)/include/*.h)
OBJECTS = $(SOURCES:$(SRCDIR)/src/%.c=$(OBJPATH)/%.o)
INCLUDE += -I../lib/include
EXTRA_CFLAGS = -Wall -Wno-error=format-truncation -UENABLE_DEBUG
LDFLAGS = -lnl-nss -lncurses
LDLIBS = -L../lib/obj
all: release
release: $(BINARY)
$(OBJPATH)/%.o: $(SRCPATH)/%.c $(HEADERS)
$(MKDIR)
@echo [CC] $@
@$(CC) -c $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -o $@ $<
$(BINARY): $(OBJECTS)
@echo $(BINARY)
@echo [LD] $@
@$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
clean:
@echo [Clean]
@rm -f $(OBJECTS)
@rm -f $(BINARY)
@rmdir $(OBJPATH)
.PHONY: clean

View File

@ -0,0 +1,714 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* @file NSSINFO handler
*/
#include <signal.h>
#include "nssinfo.h"
static pthread_t nssinfo_display_thread; /* Display statistics thread */
static char buf[NSSINFO_STR_LEN]; /* Formatted stats buffer */
bool display_all_stats; /* Display all stats per sub-system */
int invalid_input; /* Identify invalid input */
FILE *output_file; /* Output file pointer */
FILE *flow_file; /* Flow file pointer */
/* Array of pointers to node stats */
struct node *nodes[NSS_MAX_CORES][NSS_MAX_NET_INTERFACES];
/*
* NSS subsystems in alphabetical order for nssinfo tool
* - Make sure the order here is the same as in 'enum nss_nlcmn_subsys'
* defined in qca-nss-clients/netlink/include/nss_nlcmn_if.h.
*/
struct nssinfo_subsystem_info nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_MAX] = {
{.subsystem_name = "dynamic_interface", .init = nssinfo_dynamic_interface_init, .deinit = nssinfo_dynamic_interface_deinit},
{.subsystem_name = "eth_rx", .init = nssinfo_eth_rx_init, .deinit = nssinfo_eth_rx_deinit},
{.subsystem_name = "ipv4", .init = nssinfo_ipv4_init, .deinit = nssinfo_ipv4_deinit},
{.subsystem_name = "ipv6", .init = nssinfo_ipv6_init, .deinit = nssinfo_ipv6_deinit},
{.subsystem_name = "lso_rx", .init = nssinfo_lso_rx_init, .deinit = nssinfo_lso_rx_deinit},
{.subsystem_name = "n2h", .init = nssinfo_n2h_init, .deinit = nssinfo_n2h_deinit},
};
char *nssinfo_summary_fmt = "%-12s %-13s %-13s %-9s %-9s\n";
/*
* nssinfo_print_summary_header()
* Print the summary header.
*/
void nssinfo_print_summary_header(void)
{
nssinfo_stats_print(nssinfo_summary_fmt, "Node", "RX Pkts", "TX Pkts", "Drops", "Exceptions");
nssinfo_stats_print(nssinfo_summary_fmt, "----", "-------", "-------", "-----", "----------");
}
/*
* nssinfo_print_summary()
* Print the summary of the stats:
* - rx pkts
* - tx pkts
* - rx queue drops
* - exceptions
*/
void nssinfo_print_summary(char *node, uint64_t *cmn_node_stats, uint64_t *exception_stats, uint64_t exception_max)
{
int i;
uint64_t drops = 0, exceptions = 0;
char str_rx[NSSINFO_STR_LEN], str_tx[NSSINFO_STR_LEN], str_drop[NSSINFO_STR_LEN], str_ex[NSSINFO_STR_LEN];
assert(cmn_node_stats);
memset(str_rx, 0, sizeof(str_rx));
memset(str_tx, 0, sizeof(str_tx));
memset(str_drop, 0, sizeof(str_drop));
memset(str_ex, 0, sizeof(str_ex));
for (i = NSS_STATS_NODE_RX_QUEUE_0_DROPPED; i < NSS_STATS_NODE_MAX; i++) {
drops += cmn_node_stats[i];
}
if (exception_stats) {
for (i = 0 ; i < exception_max; i++) {
exceptions += exception_stats[i];
}
}
if (cmn_node_stats[NSS_STATS_NODE_RX_PKTS] > 0 || cmn_node_stats[NSS_STATS_NODE_TX_PKTS] > 0 ||
drops > 0 || exceptions > 0 || arguments.verbose) {
char *format_stats = nssinfo_format_stats(cmn_node_stats[NSS_STATS_NODE_RX_PKTS]);
strlcpy(str_rx, format_stats, sizeof(str_rx));
format_stats = nssinfo_format_stats(cmn_node_stats[NSS_STATS_NODE_TX_PKTS]);
strlcpy(str_tx, format_stats, sizeof(str_tx));
format_stats = nssinfo_format_stats(drops);
strlcpy(str_drop, format_stats, sizeof(str_drop));
if (exception_stats) {
format_stats = nssinfo_format_stats(exceptions);
strlcpy(str_ex, format_stats, sizeof(str_ex));
}
nssinfo_stats_print(nssinfo_summary_fmt, node, str_rx, str_tx, str_drop, str_ex);
}
}
/*
* nssinfo_print_all()
* Print detailed statistics.
*/
void nssinfo_print_all(char *node, char *stat_details, struct nssinfo_stats_info *stats_info, uint64_t max, uint64_t *stats_val)
{
int i;
uint16_t maxlen = 0;
char *type;
for (i = 0; i < max; i++){
if (strlen(stats_info[i].stats_name) > maxlen) {
maxlen = strlen(stats_info[i].stats_name);
}
}
/*
* Display stats header, e.g. "#ipv4 Common Stats\n"
*/
if (stat_details != NULL) {
nssinfo_stats_print("#%s\n", stat_details);
}
/* Display each stat, e.g.
* ipv4_rx_byts = 32903179 common
* ipv4_mc_create_invalid_interface = 12 special
* ...
*/
for (i = 0; i < max; i++) {
if (arguments.verbose || stats_val[i] > 0) {
switch (stats_info[i].stats_type) {
case NSS_STATS_TYPE_COMMON:
type = "common";
break;
case NSS_STATS_TYPE_SPECIAL:
type = "special";
break;
case NSS_STATS_TYPE_DROP:
type = "drop";
break;
case NSS_STATS_TYPE_ERROR:
type = "error";
break;
case NSS_STATS_TYPE_EXCEPTION:
type = "exception";
break;
default:
type = "unknown";
break;
}
nssinfo_stats_print("%s_%-*s = %-20llu %-s\n",
node, maxlen, stats_info[i].stats_name, stats_val[i], type);
}
}
nssinfo_stats_print("\n");
return;
}
/*
* nssinfo_parse_stats_strings()
* Parse each line in the debug strings file.
*
* Each line has the following format:
* \t<stats_type> , <stats_name>\n
* for example:
* root@OpenWrt:/sys/kernel/debug/qca-nss-drv/strings# cat n2h
* 0 , rx_pkts
* ...
* 1 , rx_queue[0]_drops
* ...
* 4 , n2h_data_interface_invalid
*/
static void nssinfo_parse_stats_strings(struct nssinfo_stats_info *info, char *line)
{
char *token;
char *rest = NULL;
token = strtok_r(line, " ", &rest);
if (token) {
info->stats_type = atoi(token);
token = strtok_r(NULL, ",", &rest);
}
if (token) {
token = strtok_r(token, " ", &rest);
}
if (token) {
token = strtok_r(token, "\n", &rest);
}
if (token) {
strlcpy(info->stats_name, token, sizeof(info->stats_name));
}
}
/*
* nssinfo_stats_info_init()
* Init 'struct nssinfo_stats_info' from a file in /sys/kernel/debug/qca-nss-drv/strings/.
*/
int nssinfo_stats_info_init(struct nssinfo_stats_info *info, char *strings_file)
{
FILE *fptr;
char line[NSS_STATS_MAX_STR_LENGTH];
fptr = fopen(strings_file, "r");
if (!fptr) {
nssinfo_error("Unable to open\n");
return -1;
}
while (fgets(line, NSS_STATS_MAX_STR_LENGTH, fptr)) {
nssinfo_parse_stats_strings(info, line);
info++;
}
fclose(fptr);
return 0;
}
/*
* nssinfo_node_stats_destroy()
* Release memories used to store the node stats.
*/
void nssinfo_node_stats_destroy(pthread_mutex_t *mutex, uint32_t core_id, uint32_t if_num)
{
struct node *p, *next;
if (mutex) {
pthread_mutex_lock(mutex);
}
p = nodes[core_id][if_num];
nodes[core_id][if_num] = NULL;
if (mutex) {
pthread_mutex_unlock(mutex);
}
while (p) {
next = p->next;
if (p->cmn_node_stats) {
free(p->cmn_node_stats);
}
if (p->node_stats) {
free(p->node_stats);
}
if (p->exception_stats) {
free(p->exception_stats);
}
free(p);
p = next;
}
return;
}
/*
* nssinfo_add_comma()
* Add commas in thousand's place in statistics.
*/
static char* nssinfo_add_comma(uint64_t num)
{
if (num < 1000) {
snprintf(buf, sizeof(buf), "%llu", num);
return buf;
}
nssinfo_add_comma(num/1000);
snprintf(buf + strlen(buf), sizeof(buf[NSSINFO_STR_LEN] + strlen(buf)), ",%03llu", num % 1000);
return buf;
}
/*
* nssinfo_add_suffix()
* Convert number into K thousands M million and B billion suffix.
*/
static char* nssinfo_add_suffix(uint64_t num)
{
if (num < 1000) {
snprintf(buf, sizeof(buf), "%llu", num);
return buf;
}
if (1000 <= num && num < 1000000) {
snprintf(buf , sizeof(buf), "%.2lfK", num / 1000.0);
return buf;
}
if (1000000 <= num && num < 1000000000) {
snprintf(buf , sizeof(buf), "%.2lfM", num / 1000000.0);
return buf;
}
if (1000000000 <= num) {
snprintf(buf , sizeof(buf), "%.2lfB", num / 1000000000.0);
return buf;
}
return buf;
}
/*
* nssinfo_format_stats()
* Format statistics value.
*/
char* nssinfo_format_stats(uint64_t num)
{
memset(buf, 0, sizeof(buf));
if (!arguments.higher_unit) {
return nssinfo_add_comma(num);
}
return nssinfo_add_suffix(num);
}
/*
* nssinfo_stats_display()
* Invoke each sub-system's display function.
*/
static void *nssinfo_stats_display(void *arg)
{
int i, j, core;
char mesg[]="NSS STATS";
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
for (;;) {
nssinfo_stats_print("\t\t\t%s\n", mesg);
/*
* If user does not specify a core id,
* check if the flow file is specified and display stats accordingly.
*/
if (arguments.core < 0) {
/*
* If flow file is not specified (via '-f' option),
* display each node's summary stats in alphabetical order for all the cores.
*/
if (!flow_file) {
for (core = 0 ; core < NSS_MAX_CORES ; core++) {
nssinfo_stats_print("Stats for core %d\n",core);
nssinfo_print_summary_header();
for (i = 0 ; i < NSS_NLCMN_SUBSYS_MAX; i++) {
if (nssinfo_subsystem_array[i].is_inited && i != NSS_NLCMN_SUBSYS_DYNAMIC_INTERFACE) {
nssinfo_subsystem_array[i].display(core, NULL);
}
}
nssinfo_stats_print("\n");
}
goto done;
}
/*
* Flow file is specified (via '-f' option),
* Parse the network graph from flow file and display the node's summary stats
* For example, the network graph would look like
* ipv4-0 eth_rx-0 n2h-1
* Where, node = ipv4 , core = 0
* node = eth_rx , core = 0
* node = n2h , core = 1
*/
char *line = NULL;
char *rest = NULL;
size_t len = 0;
ssize_t read;
char *node = NULL;
int matched = 0;
nssinfo_print_summary_header();
fseek(flow_file, 0, SEEK_SET);
while ((read = getline(&line, &len, flow_file)) != -1) {
node = strtok_r(line, "-", &rest);
while (node != NULL) {
core = atoi(strtok_r(NULL, " ", &rest));
if (core >= NSS_MAX_CORES || core < 0) {
printf("Invalid core id `%d'\n", core);
exit(-1);
}
for (j = 0; j < NSS_NLCMN_SUBSYS_MAX; j++) {
if (nssinfo_subsystem_array[j].is_inited &&
strstr(node, nssinfo_subsystem_array[j].subsystem_name)) {
if (j != NSS_NLCMN_SUBSYS_DYNAMIC_INTERFACE) {
++matched;
nssinfo_subsystem_array[j].display(core, node);
}
}
}
node = strtok_r(NULL, "-", &rest);
}
/* If all NODE names are invalid */
if (matched == invalid_input) {
nssinfo_error("Invalid input\n");
return NULL;
}
}
if (line) {
free(line);
}
goto done;
}
if (!arguments.strings[0]) {
/*
* If a core id is specified (via '-c' option) but NODE is not specified,
* display each node's summary stats in alphabetical order for that core.
*/
nssinfo_stats_print("Stats for core %d\n", arguments.core);
nssinfo_print_summary_header();
for (i = 0 ; i < NSS_NLCMN_SUBSYS_MAX; i++) {
if (nssinfo_subsystem_array[i].is_inited && i != NSS_NLCMN_SUBSYS_DYNAMIC_INTERFACE) {
nssinfo_subsystem_array[i].display(arguments.core, NULL);
}
}
goto done;
}
/*
* If a core id is specified and at least one NODE is specified.
*/
nssinfo_stats_print("Stats for core %d\n", arguments.core);
/*
* If user specifies only one NODE, then display all stats for this NODE.
* For example, if NODE="ipv4", then display:
* - common stats (i.e. enum nss_stats_node)
* - ipv4 special stats (i.e. enum nss_ipv4_stats_types)
* - ipv4 exception stats (i.e. enum nss_ipv4_exception_events)
*/
if (!arguments.strings[1]) {
display_all_stats = true;
} else {
/*
* If user specifies more than one NODEs, then display the summary stats for each node
*/
nssinfo_print_summary_header();
}
/*
* Now, display NODEs in the desired order.
*/
int matched = 0;
for (i = 0; arguments.strings[i]; i++) {
for (j = 0; j < NSS_NLCMN_SUBSYS_MAX; j++) {
if (nssinfo_subsystem_array[j].is_inited &&
strstr(arguments.strings[i], nssinfo_subsystem_array[j].subsystem_name)) {
if (j != NSS_NLCMN_SUBSYS_DYNAMIC_INTERFACE) {
++matched;
nssinfo_subsystem_array[j].display(arguments.core, arguments.strings[i]);
}
}
}
}
/*
* If all NODE names are invalid.
*/
if (matched == invalid_input) {
nssinfo_error("Invalid input\n");
return NULL;
}
done:
/*
* If using ncurses, refresh the screen.
*/
if (!output_file) {
refresh(); /* draw on screen */
clear(); /* clear screen buffer */
move(0, 0); /* move cursor to (line, column)=(0,0) */
}
invalid_input = 0;
sleep(arguments.rate);
}
}
/*
* nssinfo_curses_init()
* Initialize curses library.
*/
static int nssinfo_curses_init()
{
int rows, cols;
if (!initscr()) { /* satrt curses mode */
nssinfo_error("Unable to initialize curses screen\n");
return -EOPNOTSUPP;
}
getmaxyx(stdscr, rows, cols); /* get the size of the screen */
if (rows < CURSES_ROWS_MIN) {
nssinfo_error("Screen must be at least %d rows in height", CURSES_ROWS_MIN);
goto out;
}
if (cols < CURSES_COLS_MIN) {
nssinfo_error("Screen must be at least %d columns width", CURSES_COLS_MIN);
goto out;
}
cbreak(); /* disable line buffering */
noecho(); /* not to echo the input back to the screen */
nonl(); /* disable 'enter' key translation */
keypad(stdscr, TRUE); /* enable keypad keys, such as arrow keys, etc. */
nodelay(stdscr, TRUE); /* cause getch() to be a non-blocking call */
curs_set(0); /* make the cursor invisible */
clear(); /* clear screen buffer */
move(0, 0); /* move cursor to (line, column)=(0,0) */
return 0;
out:
endwin(); /* stop curses mode */
return -1;
}
/*
* nssinfo_termination_handler()
* Terminates all the modules.
*/
static void nssinfo_termination_handler(int signum)
{
pthread_cancel(nssinfo_display_thread);
}
/*
* nssinfo_display_init()
* Handle displaying all the stats.
*/
static int nssinfo_display_init()
{
int error;
if (!output_file) {
if (nssinfo_curses_init() != 0) {
return -1;
}
}
error = pthread_create(&nssinfo_display_thread, NULL, nssinfo_stats_display, NULL);
if (error) {
nssinfo_error("failed to create display thread, error %d\n", error);
if (!output_file) {
endwin();
}
}
return error;
}
/*
* nssinfo_display_wait()
* Wait for the display thread.
*/
static int nssinfo_display_wait()
{
/*
* waiting for the display thread to be terminated.
*/
pthread_join(nssinfo_display_thread, NULL);
if (!output_file) {
refresh();
endwin();
}
return 0;
}
/*
* nssinfo_notify_callback
* Get notified when NL message is received.
*/
static void nssinfo_notify_callback(int cmd, void *data)
{
if (cmd < NSS_NLCMN_SUBSYS_MAX && nssinfo_subsystem_array[cmd].is_inited) {
nssinfo_subsystem_array[cmd].notify(data);
} else {
nssinfo_error("Unknown message type %d\n", cmd);
}
}
/*
*/
static void nssinfo_deinit(struct nss_nlmcast_ctx *ctx)
{
int i, core;
struct node *node;
nssinfo_deinit_t deinit;
/*
* Close NL socket and terminate ctx->sock.thread
*/
nss_nlmcast_sock_close(ctx);
/*
* Release memory used for storing stats
*/
for (core = 0; core < NSS_MAX_CORES; ++core) {
for (i = 0; i < NSS_MAX_NET_INTERFACES; ++i) {
node = nodes[core][i];
if (node) {
assert(node->subsystem_id != NSS_NLCMN_SUBSYS_DYNAMIC_INTERFACE);
nssinfo_subsystem_array[node->subsystem_id].destroy(core, i);
}
}
}
/*
* Release resources used by each subsystem
*/
for (i = 0; i < NSS_NLCMN_SUBSYS_MAX; i++) {
deinit = nssinfo_subsystem_array[i].deinit;
if (deinit) {
deinit(ctx);
}
}
}
/*
* nssinfo_init()
* Initialize all the modules.
*/
int nssinfo_init(void)
{
int error, i;
struct nss_nlmcast_ctx ctx;
nssinfo_init_t init;
memset(&ctx, 0, sizeof(ctx));
/*
* Create NL socket
*/
error = nss_nlmcast_sock_open(&ctx, nssinfo_notify_callback, NULL);
if (error) {
nssinfo_error("Socket creation failed for NSSINFO, error(%d)\n", error);
return error;
}
/*
* Initialize all the subsystems and subscribe for mcast groups.
*/
for (i = 0; i < NSS_NLCMN_SUBSYS_MAX; i++) {
init = nssinfo_subsystem_array[i].init;
if (init) {
error = init(&ctx);
if (error) {
nssinfo_error("%s init failed, error(%d)\n", nssinfo_subsystem_array[i].subsystem_name, error);
}
}
}
/*
* Listen for MCAST events from kernel.
*/
error = nss_nlmcast_sock_listen(&ctx);
if (error < 0) {
nssinfo_error("failed to listen for mcast events from kernel\n");
goto end;
}
/*
* Create a thread which displays the stats continuously.
*/
error = nssinfo_display_init();
if (error) {
goto end;
}
/*
* Install CTRL-C handler
*/
struct sigaction new_action;
new_action.sa_handler = nssinfo_termination_handler;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
error = sigaction(SIGINT, &new_action, NULL);
if (error) {
nssinfo_error("failed to install CTRL-C handler\n");
goto end;
}
/*
* main thread is waiting here
*/
nssinfo_display_wait();
end:
nssinfo_deinit(&ctx);
return error;
}

View File

@ -0,0 +1,233 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSSINFO_FAMILY_H
#define __NSSINFO_FAMILY_H
#include "nss_nlbase.h"
#include "ncurses.h"
#include "nssinfo_ipv4.h"
#include "nssinfo_ipv6.h"
#include "nssinfo_ethrx.h"
#include "nssinfo_n2h.h"
#include "nssinfo_dynamic_interface.h"
#include "nssinfo_lso_rx.h"
#include "nss_api_if.h"
#include "nss_dynamic_interface.h"
#include "nss_stats_public.h"
#define NSSINFO_COLOR_RST "\x1b[0m"
#define NSSINFO_COLOR_GRN "\x1b[32m"
#define NSSINFO_COLOR_RED "\x1b[31m"
#define NSSINFO_COLOR_MGT "\x1b[35m"
#ifdef ENABLE_DEBUG
#define nssinfo_info(fmt, arg...) printf(NSSINFO_COLOR_GRN"INF "NSSINFO_COLOR_RST fmt, ## arg)
#define nssinfo_trace(fmt, arg...) printf(NSSINFO_COLOR_MGT"TRC(%s:%d) "NSSINFO_COLOR_RST fmt, __func__, __LINE__, ## arg)
#define nssinfo_options(fmt, arg...) printf(NSSINFO_COLOR_MGT"OPT_%d "NSSINFO_COLOR_RST fmt, ## arg)
#define nssinfo_warn(fmt, arg...) printf(NSSINFO_COLOR_RED"WARN(%s:%d) "NSSINFO_COLOR_RST fmt, __func__, __LINE__, ##arg)
#else
#define nssinfo_info(fmt, arg...)
#define nssinfo_trace(fmt, arg...)
#define nssinfo_options(fmt, arg...)
#define nssinfo_warn(fmt, arg...)
#endif
#define nssinfo_error(fmt, arg...) printf(NSSINFO_COLOR_RED"ERR(%s:%d) "NSSINFO_COLOR_RST fmt, __func__, __LINE__, ## arg)
#define nssinfo_stats_print(fmt, arg...) ({ \
if (output_file) { \
fprintf(output_file, fmt, ## arg); \
} else { \
wprintw(stdscr, fmt, ## arg); \
} \
})
/*
* Minimum terminal size to use curses library
*/
#define CURSES_ROWS_MIN 4
#define CURSES_COLS_MIN 48
/*
* Maximum formatted statistics length
*/
#define NSSINFO_STR_LEN 30
extern bool display_all_stats;
extern FILE *output_file;
extern FILE *flow_file;
extern int invalid_input;
extern struct arguments arguments;
extern char *nssinfo_summary_fmt;
/**
* @brief display method_t function
*
* @param core[IN] NSS core id
*/
typedef void (*nssinfo_stats_display_t)(int core, char *input);
/**
* @brief stats notify method_t function
*
* @param data[IN] data received from Netlink client
*/
typedef void (*nssinfo_stats_notify_t)(void *data);
/**
* @brief init method_t function
*
* @param data[IN] an opague context to be used for initialization
*/
typedef int (*nssinfo_init_t)(void *data);
/**
* @brief deinit method_t function
*
* @param data[IN] an opague context to be used for deinitialization
*/
typedef void (*nssinfo_deinit_t)(void *data);
/**
* @brief destroy method_t function
*
* @param core_id[IN] core id of the node to be destroyed
* @param if_num[IN] interface id of the node to be destroyed
*/
typedef void (*nssinfo_destroy_t)(uint32_t core_id, uint32_t if_num);
/**
* @brief Used by main to communicate with parse_opt
*/
struct arguments {
bool verbose; /*< '-v' >*/
char *output_file; /*< file arg to '--output' >*/
char *flow_file; /*< file arg to '--flowfile' >*/
bool higher_unit; /*< display higher units '-h' >*/
int core; /*< core id >*/
char **strings; /*< non-option arguments: [NODE1 [NODE2 ...]] >*/
int rate; /*< display rate in second >*/
};
/**
* @brief NSSINFO subsystem information
*/
struct nssinfo_subsystem_info {
char *subsystem_name; /**< Subsystem name string */
nssinfo_init_t init; /**< Initialize method_t */
nssinfo_deinit_t deinit; /**< Deinitialize method_t */
nssinfo_stats_display_t display; /**< Display method_t */
nssinfo_stats_notify_t notify; /**< Stats notify method_t */
nssinfo_destroy_t destroy; /**< Stats notify method_t */
bool is_inited; /**< True if the subsystem is initialized */
};
extern struct nssinfo_subsystem_info nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_MAX];
/**
* @brief NSSINFO pnode stats
*/
struct node {
struct node *next; /**< Pointer to next node */
uint64_t id; /**< Dynamic interface number */
int type; /**< see 'enum nss_dynamic_interface_type' */
int subsystem_id; /**< see 'enum nss_nlcmn_subsys' */
void *cmn_node_stats; /**< Common node stats */
void *node_stats; /**< Special stats */
void *exception_stats; /**< Exception stats */
};
extern struct node *nodes[NSS_MAX_CORES][NSS_MAX_NET_INTERFACES];
/**
* @brief Structure definition carrying stats info.
*/
struct nssinfo_stats_info {
char stats_name[NSS_STATS_MAX_STR_LENGTH]; /* stat name */
int stats_type; /* enum that tags stat type */
};
/**
* @brief validates core id and interface number
*
* @param core_id[IN] validates the core d
* @param if_num[IN] validates the interface number
*
* @return true on success or false for failure
*/
static inline bool nssinfo_coreid_ifnum_valid(uint32_t core_id, uint32_t if_num)
{
return (core_id < NSS_MAX_CORES && if_num < NSS_MAX_NET_INTERFACES);
}
/**
* @brief initialize all the modules
*
* @param flow_file[IN] parse it and display output accordingly
*
* @return 0 on success or -ve for failure
*/
int nssinfo_init(void);
/**
* @brief Format statistics value
*
* @param num[IN] statistics value in uint64_t
*
* @return comma separated string
*/
char* nssinfo_format_stats(uint64_t num);
/**
* @brief Init nssinfo_stats_info from kernel debug file.
*
* @param info[IN] pointer to a nssinfo_stats_info array
* @param line[IN] string file in kernel/debug/qca-nss-drv/strings/
*/
int nssinfo_stats_info_init(struct nssinfo_stats_info *info, char *strings_file);
/**
* @brief Free all resources used for node stats.
*
* @param mutex[IN] mutex lock
* @param core_id[IN] core id
* @param if_num[IN] node's interface number
*/
void nssinfo_node_stats_destroy(pthread_mutex_t *mutex, uint32_t core_id, uint32_t if_num);
/**
* @brief Print detailed statistics.
*
* @param node[IN] node for which stats to be printed
* @param stat_details[IN] statistics details to be printed
* @param stats_info[IN] statistics information
* @param max[IN] maximum number of strings
* @param stats_val[IN] statistics values
*/
void nssinfo_print_all(char *node, char *stat_details, struct nssinfo_stats_info *stats_info, uint64_t max, uint64_t *stats_val);
/**
* @brief Print the summary of the statistics.
*
* @param node[IN] node for which stats to be printed
* @param cmn_node_stats[IN] common node stats
* @param exception_stats[IN] exception stats
* @param exception_max[IN] maximum exception type
*/
void nssinfo_print_summary(char *node, uint64_t *cmn_node_stats, uint64_t *exception_stats, uint64_t exception_max);
void nssinfo_print_summary_header(void);
#endif /* __NSSINFO_FAMILY_H*/

View File

@ -0,0 +1,72 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* @file NSSINFO dynamic interface handler
*/
#include "nssinfo.h"
#include <nss_nldynamic_interface_if.h>
/*
* nssinfo_dynamic_interface_destroy_notify()
* Dynamic interface notify callback function.
*/
static void nssinfo_dynamic_interface_destroy_notify(void *data)
{
struct nss_dynamic_interface_notification *nss_info = (struct nss_dynamic_interface_notification *)data;
struct node *node = nodes[nss_info->core_id][nss_info->if_num];
if (!node) {
return;
}
nssinfo_subsystem_array[node->subsystem_id].destroy(nss_info->core_id, nss_info->if_num);
}
/*
* nssinfo_dynamic_interface_deinit()
* Deinitialize dynamic_interface module.
*/
void nssinfo_dynamic_interface_deinit(void *data)
{
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
nss_nlmcast_sock_leave_grp(ctx, NSS_NLDYNAMIC_INTERFACE_MCAST_GRP);
}
/*
* nssinfo_dynamic_interface_init()
* Initialize dynamic interface module.
*/
int nssinfo_dynamic_interface_init(void *data)
{
int error;
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
/*
* Subscribe for dynamic interface multicast group.
*/
nss_nlsock_set_family(&ctx->sock, NSS_NLDYNAMIC_INTERFACE_FAMILY);
error = nss_nlmcast_sock_join_grp(ctx, NSS_NLDYNAMIC_INTERFACE_MCAST_GRP);
if (error) {
nssinfo_warn("Unable to join dynamic interface multicast group.\n");
return error;
}
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_DYNAMIC_INTERFACE].notify = nssinfo_dynamic_interface_destroy_notify;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_DYNAMIC_INTERFACE].is_inited = true;
return 0;
}

View File

@ -0,0 +1,28 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSSINFO_DYNAMIC_INTERFACE_H
#define __NSSINFO_DYNAMIC_INTERFACE_H
/**
* @brief initialize dynamic interface module.
*
* @return 0 on success or -ve for failure
*/
int nssinfo_dynamic_interface_init(void *data);
void nssinfo_dynamic_interface_deinit(void *data);
#endif /* __NSSINFO_DYNAMIC_INTERFACE_H*/

View File

@ -0,0 +1,215 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* @file NSSINFO Ethernet Rx handler
*/
#include "nssinfo.h"
#include <nss_eth_rx.h>
#include <nss_nlethrx_if.h>
static pthread_mutex_t eth_rx_lock;
static struct nssinfo_stats_info nss_eth_rx_cmn_stats_str[NSS_STATS_NODE_MAX];
static struct nssinfo_stats_info nss_eth_rx_stats_str[NSS_ETH_RX_STATS_MAX];
static struct nssinfo_stats_info nss_eth_rx_exception_stats_str[NSS_ETH_RX_EXCEPTION_EVENT_MAX];
/*
* nssinfo_eth_rx_stats_display()
* Ethernet Rx display callback function.
*/
static void nssinfo_eth_rx_stats_display(int core, char *input)
{
struct node *eth_rx_node;
if (input && strncmp(input, nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_ETHRX].subsystem_name, strlen(input))) {
++invalid_input;
nssinfo_trace("Invalid node name: %s\n", input);
return;
}
pthread_mutex_lock(&eth_rx_lock);
eth_rx_node = nodes[core][NSS_ETH_RX_INTERFACE];
if (!eth_rx_node) {
pthread_mutex_unlock(&eth_rx_lock);
return;
}
if (!display_all_stats) {
nssinfo_print_summary("eth_rx", (uint64_t *)eth_rx_node->cmn_node_stats, (uint64_t *)eth_rx_node->exception_stats, NSS_ETH_RX_EXCEPTION_EVENT_MAX);
pthread_mutex_unlock(&eth_rx_lock);
return;
}
nssinfo_print_all("eth_rx", "eth_rx Common Stats", nss_eth_rx_cmn_stats_str, NSS_STATS_NODE_MAX, (uint64_t *)eth_rx_node->cmn_node_stats);
nssinfo_print_all("eth_rx", "eth_rx Special Stats", nss_eth_rx_stats_str, NSS_ETH_RX_STATS_MAX, (uint64_t *)eth_rx_node->node_stats);
nssinfo_print_all("eth_rx", "eth_rx Exception Stats", nss_eth_rx_exception_stats_str, NSS_ETH_RX_EXCEPTION_EVENT_MAX, (uint64_t *)eth_rx_node->exception_stats);
pthread_mutex_unlock(&eth_rx_lock);
}
/*
* nssinfo_eth_rx_stats_notify()
* Ethernet Rx statistics notify callback function.
*/
static void nssinfo_eth_rx_stats_notify(void *data)
{
uint64_t *cmn_node_stats, *node_stats, *exception_stats;
struct nss_eth_rx_stats_notification *nss_stats = (struct nss_eth_rx_stats_notification *)data;
struct node *eth_rx_node;
struct node **eth_rx_ptr;
if (!nssinfo_coreid_ifnum_valid(nss_stats->core_id, NSS_ETH_RX_INTERFACE)) {
return;
}
pthread_mutex_lock(&eth_rx_lock);
eth_rx_ptr = &nodes[nss_stats->core_id][NSS_ETH_RX_INTERFACE];
eth_rx_node = *eth_rx_ptr;
if (eth_rx_node) {
memcpy(eth_rx_node->cmn_node_stats, &nss_stats->cmn_node_stats, sizeof(nss_stats->cmn_node_stats));
memcpy(eth_rx_node->node_stats, &nss_stats->special_stats, sizeof(nss_stats->special_stats));
memcpy(eth_rx_node->exception_stats, &nss_stats->exception_stats, sizeof(nss_stats->exception_stats));
pthread_mutex_unlock(&eth_rx_lock);
return;
}
pthread_mutex_unlock(&eth_rx_lock);
eth_rx_node = (struct node *)calloc(1, sizeof(struct node));
if (!eth_rx_node) {
nssinfo_warn("Failed to allocate memory for eth rx node\n");
return;
}
cmn_node_stats = (uint64_t *)malloc(sizeof(nss_stats->cmn_node_stats));
if (!cmn_node_stats) {
nssinfo_warn("Failed to allocate memory for eth rx common node statistics\n");
goto eth_rx_node_free;
}
node_stats = (uint64_t *)malloc(sizeof(nss_stats->special_stats));
if (!node_stats) {
nssinfo_warn("Failed to allocate memory for eth rx special stats\n");
goto cmn_node_stats_free;
}
exception_stats = (uint64_t *)malloc(sizeof(nss_stats->exception_stats));
if (!exception_stats) {
nssinfo_warn("Failed to allocate memory for eth rx exception stats\n");
goto node_stats_free;
}
memcpy(cmn_node_stats, &nss_stats->cmn_node_stats, sizeof(nss_stats->cmn_node_stats));
memcpy(node_stats, &nss_stats->special_stats, sizeof(nss_stats->special_stats));
memcpy(exception_stats, &nss_stats->exception_stats, sizeof(nss_stats->exception_stats));
eth_rx_node->cmn_node_stats = cmn_node_stats;
eth_rx_node->node_stats = node_stats;
eth_rx_node->exception_stats = exception_stats;
eth_rx_node->subsystem_id = NSS_NLCMN_SUBSYS_ETHRX;
/*
* Notifify is guaranteed to be single threaded via Netlink listen callback
*/
pthread_mutex_lock(&eth_rx_lock);
nodes[nss_stats->core_id][NSS_ETH_RX_INTERFACE] = eth_rx_node;
pthread_mutex_unlock(&eth_rx_lock);
return;
node_stats_free:
free(node_stats);
cmn_node_stats_free:
free(cmn_node_stats);
eth_rx_node_free:
free(eth_rx_node);
return;
}
/*
* nssinfo_eth_rx_destroy()
* Destroy ethernet Rx node.
*/
static void nssinfo_eth_rx_destroy(uint32_t core_id, uint32_t if_num)
{
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_ETHRX].is_inited) {
nssinfo_node_stats_destroy(&eth_rx_lock, core_id, NSS_ETH_RX_INTERFACE);
}
}
/*
* nssinfo_ethrx_deinit()
* Deinitialize ethrx module.
*/
void nssinfo_eth_rx_deinit(void *data)
{
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_ETHRX].is_inited) {
pthread_mutex_destroy(&eth_rx_lock);
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_ETHRX].is_inited = false;
}
nss_nlmcast_sock_leave_grp(ctx, NSS_NLETHRX_MCAST_GRP);
}
/*
* nssinfo_eth_rx_init()
* Initialize Ethernet Rx module.
*/
int nssinfo_eth_rx_init(void *data)
{
int error;
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
/*
* Subscribe for Ethernet Rx MCAST group.
*/
nss_nlsock_set_family(&ctx->sock, NSS_NLETHRX_FAMILY);
error = nss_nlmcast_sock_join_grp(ctx, NSS_NLETHRX_MCAST_GRP);
if (error) {
nssinfo_warn("Unable to join Ethernet Rx mcast group.\n");
return error;
}
if (nssinfo_stats_info_init(nss_eth_rx_cmn_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/common_node_stats") != 0) {
goto fail;
}
if (nssinfo_stats_info_init(nss_eth_rx_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/eth_rx/special_stats_str") != 0) {
goto fail;
}
if (nssinfo_stats_info_init(nss_eth_rx_exception_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/eth_rx/exception_stats_str") != 0) {
goto fail;
}
if (pthread_mutex_init(&eth_rx_lock, NULL) != 0) {
nssinfo_warn("Mutex init has failed for Ethernet Rx\n");
goto fail;
}
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_ETHRX].display = nssinfo_eth_rx_stats_display;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_ETHRX].notify = nssinfo_eth_rx_stats_notify;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_ETHRX].destroy = nssinfo_eth_rx_destroy;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_ETHRX].is_inited = true;
return 0;
fail:
nss_nlmcast_sock_leave_grp(ctx, NSS_NLETHRX_MCAST_GRP);
return -1;
}

View File

@ -0,0 +1,28 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSSINFO_ETHRX_H
#define __NSSINFO_ETHRX_H
/**
* @brief initialize Ethernet Rx module.
*
* @return 0 on success or -ve for failure
*/
int nssinfo_eth_rx_init(void *data);
void nssinfo_eth_rx_deinit(void *data);
#endif /* __NSSINFO_ETHRX_H*/

View File

@ -0,0 +1,215 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* @file NSSINFO ipv4 handler
*/
#include "nssinfo.h"
static pthread_mutex_t ipv4_lock;
static struct nssinfo_stats_info nss_stats_str_node[NSS_STATS_NODE_MAX];
static struct nssinfo_stats_info nss_ipv4_stats_str[NSS_IPV4_STATS_MAX];
static struct nssinfo_stats_info nss_ipv4_exception_stats_str[NSS_IPV4_EXCEPTION_EVENT_MAX];
/*
* nssinfo_ipv4_stats_display()
* IPv4 display callback function.
*/
static void nssinfo_ipv4_stats_display(int core, char *input)
{
struct node *ipv4_node;
if (input && strncmp(input, nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV4].subsystem_name, strlen(input)) != 0) {
++invalid_input;
nssinfo_trace("Invalid node name: %s\n", input);
return;
}
pthread_mutex_lock(&ipv4_lock);
ipv4_node = nodes[core][NSS_IPV4_RX_INTERFACE];
if (!ipv4_node) {
pthread_mutex_unlock(&ipv4_lock);
return;
}
if (!display_all_stats) {
nssinfo_print_summary("ipv4", (uint64_t *)ipv4_node->cmn_node_stats, (uint64_t *)ipv4_node->exception_stats, NSS_IPV4_EXCEPTION_EVENT_MAX);
pthread_mutex_unlock(&ipv4_lock);
return;
}
nssinfo_print_all("ipv4", "ipv4 Common Stats", nss_stats_str_node, NSS_STATS_NODE_MAX, (uint64_t *)ipv4_node->cmn_node_stats);
nssinfo_print_all("ipv4", "ipv4 Special Stats", nss_ipv4_stats_str, NSS_IPV4_STATS_MAX, (uint64_t *)ipv4_node->node_stats);
nssinfo_print_all("ipv4", "ipv4 Exception Stats", nss_ipv4_exception_stats_str, NSS_IPV4_EXCEPTION_EVENT_MAX, (uint64_t *)ipv4_node->exception_stats);
pthread_mutex_unlock(&ipv4_lock);
}
/*
* nssinfo_ipv4_stats_notify()
* IPv4 stats notify callback function.
*/
static void nssinfo_ipv4_stats_notify(void *data)
{
uint64_t *cmn_node_stats, *node_stats, *exception_stats;
struct nss_nlipv4_rule *rule = (struct nss_nlipv4_rule *)data;
struct node *ipv4_node;
struct node **ipv4_ptr;
if (!nssinfo_coreid_ifnum_valid(rule->stats.core_id, NSS_IPV4_RX_INTERFACE)) {
return;
}
ipv4_ptr = &nodes[rule->stats.core_id][NSS_IPV4_RX_INTERFACE];
pthread_mutex_lock(&ipv4_lock);
ipv4_node = *ipv4_ptr;
if (ipv4_node) {
memcpy(ipv4_node->cmn_node_stats, &rule->stats.cmn_node_stats, sizeof(rule->stats.cmn_node_stats));
memcpy(ipv4_node->node_stats, &rule->stats.special_stats, sizeof(rule->stats.special_stats));
memcpy(ipv4_node->exception_stats, &rule->stats.exception_stats, sizeof(rule->stats.exception_stats));
pthread_mutex_unlock(&ipv4_lock);
return;
}
pthread_mutex_unlock(&ipv4_lock);
ipv4_node = (struct node *)calloc(1, sizeof(struct node));
if (!ipv4_node) {
nssinfo_warn("Failed to allocate memory for ipv4 node\n");
return;
}
cmn_node_stats = (uint64_t *)malloc(sizeof(rule->stats.cmn_node_stats));
if (!cmn_node_stats) {
nssinfo_warn("Failed to allocate memory for ipv4 common node stats\n");
goto ipv4_node_free;
}
node_stats = (uint64_t *)malloc(sizeof(rule->stats.special_stats));
if (!node_stats) {
nssinfo_warn("Failed to allocate memory for ipv4 special stats\n");
goto cmn_node_stats_free;
}
exception_stats = (uint64_t *)malloc(sizeof(rule->stats.exception_stats));
if (!exception_stats) {
nssinfo_warn("Failed to allocate memory for ipv4 exception stats\n");
goto node_stats_free;
}
memcpy(cmn_node_stats, &rule->stats.cmn_node_stats, sizeof(rule->stats.cmn_node_stats));
memcpy(node_stats, &rule->stats.special_stats, sizeof(rule->stats.special_stats));
memcpy(exception_stats, &rule->stats.exception_stats, sizeof(rule->stats.exception_stats));
ipv4_node->cmn_node_stats = cmn_node_stats;
ipv4_node->node_stats = node_stats;
ipv4_node->exception_stats = exception_stats;
ipv4_node->subsystem_id = NSS_NLCMN_SUBSYS_IPV4;
/*
* Notifify is guaranteed to be single threaded via Netlink listen callback
*/
pthread_mutex_lock(&ipv4_lock);
*ipv4_ptr = ipv4_node;
pthread_mutex_unlock(&ipv4_lock);
return;
node_stats_free:
free(node_stats);
cmn_node_stats_free:
free(cmn_node_stats);
ipv4_node_free:
free(ipv4_node);
}
/*
* nssinfo_ipv4_destroy()
* Destroy IPv4 node.
*/
static void nssinfo_ipv4_destroy(uint32_t core_id, uint32_t if_num)
{
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV4].is_inited) {
nssinfo_node_stats_destroy(&ipv4_lock, core_id, NSS_IPV4_RX_INTERFACE);
}
}
/*
* nssinfo_ipv4_deinit()
* Initialize IPv4 module.
*/
void nssinfo_ipv4_deinit(void *data)
{
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV4].is_inited) {
pthread_mutex_destroy(&ipv4_lock);
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV4].is_inited = false;
}
nss_nlmcast_sock_leave_grp(ctx, NSS_NLIPV4_MCAST_GRP);
}
/*
* nssinfo_ipv4_init()
* Initialize IPv4 module.
*/
int nssinfo_ipv4_init(void *data)
{
int error;
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
/*
* Subscribe for IPV4 MCAST group.
*/
nss_nlsock_set_family(&ctx->sock, NSS_NLIPV4_FAMILY);
error = nss_nlmcast_sock_join_grp(ctx, NSS_NLIPV4_MCAST_GRP);
if (error) {
nssinfo_warn("Unable to join IPv4 mcast group\n");
return error;
}
if (nssinfo_stats_info_init(nss_stats_str_node,
"/sys/kernel/debug/qca-nss-drv/strings/common_node_stats") != 0) {
goto fail;
}
if (nssinfo_stats_info_init(nss_ipv4_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/ipv4/special_stats_str") != 0) {
goto fail;
}
if (nssinfo_stats_info_init(nss_ipv4_exception_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/ipv4/exception_stats_str") != 0) {
goto fail;
}
if (pthread_mutex_init(&ipv4_lock, NULL) != 0) {
nssinfo_warn("Mutex init has failed for IPV4\n");
goto fail;
}
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV4].display = nssinfo_ipv4_stats_display;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV4].notify = nssinfo_ipv4_stats_notify;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV4].destroy = nssinfo_ipv4_destroy;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV4].is_inited = true;
return 0;
fail:
nss_nlmcast_sock_leave_grp(ctx, NSS_NLIPV4_MCAST_GRP);
return -1;
}

View File

@ -0,0 +1,30 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSSINFO_IPV4_H
#define __NSSINFO_IPV4_H
#define NSSINFO_IPV4_HDR_VERSION 4
/**
* @brief initialize IPv4 module.
*
* @return 0 on success or -ve for failure
*/
int nssinfo_ipv4_init(void *data);
void nssinfo_ipv4_deinit(void *data);
#endif /* __NSSINFO_IPV4_H*/

View File

@ -0,0 +1,212 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* @file NSSINFO ipv6 handler
*/
#include "nssinfo.h"
static pthread_mutex_t ipv6_lock;
static struct nssinfo_stats_info nss_stats_str_node[NSS_STATS_NODE_MAX];
static struct nssinfo_stats_info nss_ipv6_stats_str[NSS_IPV6_STATS_MAX];
static struct nssinfo_stats_info nss_ipv6_exception_stats_str[NSS_IPV6_EXCEPTION_EVENT_MAX];
/*
* nssinfo_ipv6_stats_display()
* IPv6 display callback function.
*/
static void nssinfo_ipv6_stats_display(int core, char *input)
{
struct node *ipv6_node;
if (input && strncmp(input, nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV6].subsystem_name, strlen(input))) {
++invalid_input;
nssinfo_trace("Invalid node name: %s\n", input);
return;
}
pthread_mutex_lock(&ipv6_lock);
ipv6_node = nodes[core][NSS_IPV6_RX_INTERFACE];
if (!ipv6_node) {
pthread_mutex_unlock(&ipv6_lock);
return;
}
if (display_all_stats) {
nssinfo_print_all("ipv6", "ipv6 Common Stats", nss_stats_str_node, NSS_STATS_NODE_MAX, (uint64_t *)ipv6_node->cmn_node_stats);
nssinfo_print_all("ipv6", "ipv6 Special Stats", nss_ipv6_stats_str, NSS_IPV6_STATS_MAX, (uint64_t *)ipv6_node->node_stats);
nssinfo_print_all("ipv6", "ipv6 Exception Stats", nss_ipv6_exception_stats_str, NSS_IPV6_EXCEPTION_EVENT_MAX, (uint64_t *)ipv6_node->exception_stats);
pthread_mutex_unlock(&ipv6_lock);
return;
}
nssinfo_print_summary("ipv6", (uint64_t *)ipv6_node->cmn_node_stats, (uint64_t *)ipv6_node->exception_stats, NSS_IPV6_EXCEPTION_EVENT_MAX);
pthread_mutex_unlock(&ipv6_lock);
}
/*
* nssinfo_ipv6_stats_notify()
* IPv6 stats notify callback function.
*/
static void nssinfo_ipv6_stats_notify(void *data)
{
uint64_t *cmn_node_stats, *node_stats, *exception_stats;
struct nss_nlipv6_rule *rule = (struct nss_nlipv6_rule *)data;
struct node *ipv6_node;
struct node **ipv6_ptr;
if (!nssinfo_coreid_ifnum_valid(rule->stats.core_id, NSS_IPV6_RX_INTERFACE)) {
return;
}
pthread_mutex_lock(&ipv6_lock);
ipv6_ptr = &nodes[rule->stats.core_id][NSS_IPV6_RX_INTERFACE];
ipv6_node = *ipv6_ptr;
if (ipv6_node) {
memcpy(ipv6_node->cmn_node_stats, &rule->stats.cmn_node_stats, sizeof(rule->stats.cmn_node_stats));
memcpy(ipv6_node->node_stats, &rule->stats.special_stats, sizeof(rule->stats.special_stats));
memcpy(ipv6_node->exception_stats, &rule->stats.exception_stats, sizeof(rule->stats.exception_stats));
pthread_mutex_unlock(&ipv6_lock);
return;
}
pthread_mutex_unlock(&ipv6_lock);
ipv6_node = (struct node *)calloc(1, sizeof(struct node));
if (!ipv6_node) {
nssinfo_warn("Failed to allocate memory for ipv6 node\n");
return;
}
cmn_node_stats = (uint64_t *)malloc(sizeof(rule->stats.cmn_node_stats));
if (!cmn_node_stats) {
nssinfo_warn("Failed to allocate memory for ipv6 common node statistics\n");
goto ipv6_node_free;
}
node_stats = (uint64_t *)malloc(sizeof(rule->stats.special_stats));
if (!node_stats) {
nssinfo_warn("Failed to allocate memory for ipv6 special stats\n");
goto cmn_node_stats_free;
}
exception_stats = (uint64_t *)malloc(sizeof(rule->stats.exception_stats));
if (!exception_stats) {
nssinfo_warn("Failed to allocate memory for ipv6 exception stats\n");
goto node_stats_free;
}
memcpy(cmn_node_stats, &rule->stats.cmn_node_stats, sizeof(rule->stats.cmn_node_stats));
memcpy(node_stats, &rule->stats.special_stats, sizeof(rule->stats.special_stats));
memcpy(exception_stats, &rule->stats.exception_stats, sizeof(rule->stats.exception_stats));
ipv6_node->cmn_node_stats = cmn_node_stats;
ipv6_node->node_stats = node_stats;
ipv6_node->exception_stats = exception_stats;
ipv6_node->subsystem_id = NSS_NLCMN_SUBSYS_IPV6;
/*
* Notifify is guaranteed to be single threaded via Netlink listen callback
*/
pthread_mutex_lock(&ipv6_lock);
*ipv6_ptr = ipv6_node;
pthread_mutex_unlock(&ipv6_lock);
return;
node_stats_free:
free(node_stats);
cmn_node_stats_free:
free(cmn_node_stats);
ipv6_node_free:
free(ipv6_node);
}
/*
* nssinfo_ipv6_destroy()
* Destroy IPv6 node.
*/
static void nssinfo_ipv6_destroy(uint32_t core_id, uint32_t if_num)
{
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV6].is_inited) {
nssinfo_node_stats_destroy(&ipv6_lock, core_id, NSS_IPV6_RX_INTERFACE);
}
}
/*
* nssinfo_ipv6_deinit()
* Deinitialize ipv6 module.
*/
void nssinfo_ipv6_deinit(void *data)
{
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV6].is_inited) {
pthread_mutex_destroy(&ipv6_lock);
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV6].is_inited = false;
}
nss_nlmcast_sock_leave_grp(ctx, NSS_NLIPV6_MCAST_GRP);
}
/*
* nssinfo_ipv6_init()
* Initialize IPv6 module.
*/
int nssinfo_ipv6_init(void *data)
{
int error;
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
/*
* Subscribe for IPV6 MCAST group.
*/
nss_nlsock_set_family(&ctx->sock, NSS_NLIPV6_FAMILY);
error = nss_nlmcast_sock_join_grp(ctx, NSS_NLIPV6_MCAST_GRP);
if (error) {
nssinfo_warn("Unable to join IPv6 mcast group\n");
return error;
}
if (nssinfo_stats_info_init(nss_stats_str_node,
"/sys/kernel/debug/qca-nss-drv/strings/common_node_stats") != 0) {
goto fail;
}
if (nssinfo_stats_info_init(nss_ipv6_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/ipv6/special_stats_str") != 0) {
goto fail;
}
if (nssinfo_stats_info_init(nss_ipv6_exception_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/ipv6/exception_stats_str") != 0) {
goto fail;
}
if (pthread_mutex_init(&ipv6_lock, NULL) != 0) {
nssinfo_warn("Mutex init has failed for IPV6\n");
goto fail;
}
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV6].display = nssinfo_ipv6_stats_display;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV6].notify = nssinfo_ipv6_stats_notify;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV6].destroy = nssinfo_ipv6_destroy;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_IPV6].is_inited = true;
return 0;
fail:
nss_nlmcast_sock_leave_grp(ctx, NSS_NLIPV6_MCAST_GRP);
return -1;
}

View File

@ -0,0 +1,30 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSSINFO_IPV6_H
#define __NSSINFO_IPV6_H
#define NSSINFO_IPV6_HDR_VERSION 4
/**
* @brief initialize IPv4 module.
*
* @return 0 on success or -ve for failure
*/
int nssinfo_ipv6_init(void *data);
void nssinfo_ipv6_deinit(void *data);
#endif /* __NSSINFO_IPV6_H*/

View File

@ -0,0 +1,195 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* @file NSSINFO lso_rx handler
*/
#include "nssinfo.h"
#include <nss_lso_rx.h>
#include <nss_nllso_rx_if.h>
static pthread_mutex_t lso_rx_lock;
static struct nssinfo_stats_info nss_stats_str_node[NSS_STATS_NODE_MAX];
static struct nssinfo_stats_info nss_lso_rx_stats_str[NSS_LSO_RX_STATS_MAX];
/*
* nssinfo_lso_rx_stats_display()
* LSO Rx display callback function.
*/
static void nssinfo_lso_rx_stats_display(int core, char *input)
{
struct node *lso_rx_node;
if (input && strncmp(input, nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_LSO_RX].subsystem_name, strlen(input))) {
++invalid_input;
nssinfo_trace("Invalid node name: %s\n", input);
return;
}
pthread_mutex_lock(&lso_rx_lock);
lso_rx_node = nodes[core][NSS_LSO_RX_INTERFACE];
if (!lso_rx_node) {
pthread_mutex_unlock(&lso_rx_lock);
nssinfo_error("%s is not running on the NPU\n", input);
return;
}
if (display_all_stats) {
nssinfo_print_all("lso_rx", "lso_rx Common Stats", nss_stats_str_node, NSS_STATS_NODE_MAX, (uint64_t *)lso_rx_node->cmn_node_stats);
nssinfo_print_all("lso_rx", "lso_rx Special Stats", nss_lso_rx_stats_str, NSS_LSO_RX_STATS_MAX, (uint64_t *)lso_rx_node->node_stats);
pthread_mutex_unlock(&lso_rx_lock);
return;
}
nssinfo_print_summary("lso_rx", (uint64_t *)lso_rx_node->cmn_node_stats, NULL, 0);
pthread_mutex_unlock(&lso_rx_lock);
}
/*
* nssinfo_lso_rx_stats_notify()
* LSO Rx stats notify callback function.
*/
static void nssinfo_lso_rx_stats_notify(void *data)
{
uint64_t *cmn_node_stats, *node_stats;
struct nss_lso_rx_stats_notification *nss_stats = (struct nss_lso_rx_stats_notification *)data;
struct node *lso_rx_node;
struct node **lso_rx_ptr;
if (!nssinfo_coreid_ifnum_valid(nss_stats->core_id, NSS_LSO_RX_INTERFACE)) {
return;
}
pthread_mutex_lock(&lso_rx_lock);
lso_rx_ptr = &nodes[nss_stats->core_id][NSS_LSO_RX_INTERFACE];
lso_rx_node = *lso_rx_ptr;
if (lso_rx_node) {
memcpy(lso_rx_node->cmn_node_stats, &nss_stats->cmn_node_stats, sizeof(nss_stats->cmn_node_stats));
memcpy(lso_rx_node->node_stats, &nss_stats->node_stats, sizeof(nss_stats->node_stats));
pthread_mutex_unlock(&lso_rx_lock);
return;
}
pthread_mutex_unlock(&lso_rx_lock);
lso_rx_node = (struct node *)calloc(1, sizeof(struct node));
if (!lso_rx_node) {
nssinfo_warn("Failed to allocate memory for lso_rx node\n");
return;
}
cmn_node_stats = (uint64_t *)malloc(sizeof(nss_stats->cmn_node_stats));
if (!cmn_node_stats) {
nssinfo_warn("Failed to allocate memory for lso_rx common node statistics\n");
goto lso_rx_node_free;
}
node_stats = (uint64_t *)malloc(sizeof(nss_stats->node_stats));
if (!node_stats) {
nssinfo_warn("Failed to allocate memory for lso_rx connection stats\n");
goto cmn_node_stats_free;
}
memcpy(cmn_node_stats, &nss_stats->cmn_node_stats, sizeof(nss_stats->cmn_node_stats));
memcpy(node_stats, &nss_stats->node_stats, sizeof(nss_stats->node_stats));
lso_rx_node->cmn_node_stats = cmn_node_stats;
lso_rx_node->node_stats = node_stats;
lso_rx_node->subsystem_id = NSS_NLCMN_SUBSYS_LSO_RX;
/*
* Notify is guaranteed to be single threaded via Netlink listen callback
*/
pthread_mutex_lock(&lso_rx_lock);
*lso_rx_ptr = lso_rx_node;
pthread_mutex_unlock(&lso_rx_lock);
return;
cmn_node_stats_free:
free(cmn_node_stats);
lso_rx_node_free:
free(lso_rx_node);
}
/*
* nssinfo_lso_rx_destroy()
* Destroy LSO Rx node.
*/
static void nssinfo_lso_rx_destroy(uint32_t core_id, uint32_t if_num)
{
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_LSO_RX].is_inited) {
nssinfo_node_stats_destroy(&lso_rx_lock, core_id, NSS_LSO_RX_INTERFACE);
}
}
/*
* nssinfo_lso_rx_deinit()
* Deinitialize lso_rx module.
*/
void nssinfo_lso_rx_deinit(void *data)
{
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_LSO_RX].is_inited) {
pthread_mutex_destroy(&lso_rx_lock);
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_LSO_RX].is_inited = false;
}
nss_nlmcast_sock_leave_grp(ctx, NSS_NLLSO_RX_MCAST_GRP);
}
/*
* nssinfo_lso_rx_init()
* Initialize LSO Rx module.
*/
int nssinfo_lso_rx_init(void *data)
{
int error;
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
/*
* Subscribe for LSO Rx multicast group.
*/
nss_nlsock_set_family(&ctx->sock, NSS_NLLSO_RX_FAMILY);
error = nss_nlmcast_sock_join_grp(ctx, NSS_NLLSO_RX_MCAST_GRP);
if (error) {
nssinfo_warn("Unable to join LSO Rx multicast group\n");
return error;
}
if (nssinfo_stats_info_init(nss_stats_str_node,
"/sys/kernel/debug/qca-nss-drv/strings/common_node_stats") != 0) {
goto fail;
}
if (nssinfo_stats_info_init(nss_lso_rx_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/lso_rx") != 0) {
goto fail;
}
if (pthread_mutex_init(&lso_rx_lock, NULL) != 0) {
nssinfo_warn("Mutex init has failed for LSO Rx\n");
goto fail;
}
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_LSO_RX].display = nssinfo_lso_rx_stats_display;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_LSO_RX].notify = nssinfo_lso_rx_stats_notify;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_LSO_RX].destroy = nssinfo_lso_rx_destroy;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_LSO_RX].is_inited = true;
return 0;
fail:
nss_nlmcast_sock_leave_grp(ctx, NSS_NLLSO_RX_MCAST_GRP);
return -1;
}

View File

@ -0,0 +1,28 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSSINFO_LSO_RX_H
#define __NSSINFO_LSO_RX_H
/**
* @brief initialize LSO_RX module.
*
* @return 0 on success or -ve for failure
*/
int nssinfo_lso_rx_init(void *data);
void nssinfo_lso_rx_deinit(void *data);
#endif /* __NSSINFO_LSO_RX_H*/

View File

@ -0,0 +1,191 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#include "nssinfo.h"
#include <getopt.h>
static const char *nssinfo_version = "1.0";
static struct option long_options[] = {
{"verbose", no_argument, NULL, 'v'},
{"higherunit", no_argument, NULL, 'u'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{"output", required_argument, NULL, 'o'},
{"flowfile", required_argument, NULL, 'f'},
{"core", required_argument, NULL, 'c'},
{"rate", required_argument, NULL, 'r'},
{0, 0, 0, 0}
};
static char *short_options = "vuh?Vo:f:c:r:";
static void print_help(void)
{
printf("nssinfo is an userspace tool used to display NODE stats from NSS-FW\n");
printf("Usage: nssinfo [OPTION ...] [-c ID [NODE1 NODE2 ...]]\n");
printf("OPTION:\n");
printf(" -c, --core=ID Display statistics based on core id\n");
printf(" -f, --flowfile=FILE Specify output content in FILE\n");
printf(" -o, --output=FILE Write output to FILE instead of stdout\n");
printf(" -r, --rate=RATE Update screen every RATE seconds\n");
printf(" -u, --higherunit Display stats in higher units, i.e. K, M, B\n");
printf(" -v, --verbose Display all the stats (zero and non-zero stats)\n");
printf(" -V, --version Print program version\n");
printf(" -h, --help Give this help message\n");
printf("Examples:\n");
printf(" nssinfo\n");
printf(" nssinfo -c0\n");
printf(" nssinfo -c0 ipv4 edma[0] edma[4]\n");
printf(" nssinfo -r5 -o stats.log\n");
}
struct arguments arguments;
/*
* getopt_parse()
* Parse command line arguments using getopt_long().
*/
static int getopt_parse(int argc, char **argv, void *a)
{
struct arguments *arguments = (struct arguments *)a;
while (1) {
int key = getopt_long(argc, argv, short_options, long_options, NULL);
/*
* Detect the end of the options.
*/
if (key == -1)
break;
switch (key) {
case 'v':
arguments->verbose = true;
break;
case 'u':
arguments->higher_unit = true;
break;
case 'f':
arguments->flow_file = optarg;
break;
case 'o':
arguments->output_file = optarg;
break;
case 'r': /* -r5 */
arguments->rate = atoi(optarg);
if (arguments->rate <= 0) {
printf("Invalid rate `%s'\n", optarg);
exit(-1);
}
break;
case 'c': /* -c0 */
arguments->core = atoi(optarg);
if (arguments->core >= NSS_MAX_CORES || arguments->core < 0) {
printf("Invalid core id `%s'\n", optarg);
exit(-1);
}
break;
case 'h':
print_help();
exit(0);
case 'V':
printf("%s\n", nssinfo_version);
exit(0);
case '?':
default:
/*
* getopt_long already printed an error message.
*/
exit(-1);
}
}
/* Any remaining non-option arguments start from argv[optind].
* Init arguments->strings so that
* arguments->strings[0] points to the 1st non-option argument
* arguments->strings[1] points to the 2nd non-option argument
* ...
* arguments->strings[n] points to the last non-option argument
* arguments->strings[n+1] is NULL
*
* For example,
* If user enters 'nssinfo -c1 edma1 edma2', optind is 2 at this point and
* arguments->strings[0] = "edma1", arguments->strings[1] = "edma2", arguments->strings[2] = NULL.
* If user does not specify any non-option argument (e.g. nssinfo -v),
* argv[optind] is NULL so arguments->strings[0] is NULL.
*/
arguments->strings = &argv[optind];
return 0;
}
/*
* main()
*/
int main(int argc, char **argv)
{
int error;
arguments.output_file = NULL;
arguments.flow_file = NULL;
arguments.verbose = false;
arguments.higher_unit = false;
arguments.core = -1; /* display stats for all cores */
arguments.rate = 1; /* 1 sec */
getopt_parse(argc, argv, &arguments);
if (arguments.output_file) {
output_file = fopen(arguments.output_file, "w");
if (!output_file) {
nssinfo_error("Error opening output file!\n");
exit(1);
}
}
if (arguments.flow_file) {
flow_file = fopen(arguments.flow_file, "r");
if (!flow_file) {
nssinfo_error("Error opening flow file!\n");
error = -1;
goto end;
}
}
error = nssinfo_init();
if (error) {
nssinfo_info("Nssinfo initialization failed(%d)\n", error);
}
if (flow_file) {
fclose(flow_file);
}
end:
if (output_file) {
fclose(output_file);
}
return error;
}

View File

@ -0,0 +1,209 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* @file NSSINFO n2h handler
*/
#include "nssinfo.h"
#include <nss_n2h.h>
#include <nss_nln2h_if.h>
static pthread_mutex_t n2h_lock;
static uint64_t drv_stats[NSS_STATS_DRV_MAX];
static struct nssinfo_stats_info nssinfo_n2h_stats_str[NSS_N2H_STATS_MAX];
/*
* nssinfo_n2h_stats_display()
* N2H display callback function.
*/
static void nssinfo_n2h_stats_display(int core, char *input)
{
struct node *n2h_node;
char str_rx[NSSINFO_STR_LEN], str_tx[NSSINFO_STR_LEN];
if (input && strncmp(input, nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_N2H].subsystem_name, strlen(input))) {
++invalid_input;
nssinfo_trace("Invalid node name: %s\n", input);
return;
}
pthread_mutex_lock(&n2h_lock);
n2h_node = nodes[core][NSS_N2H_INTERFACE];
if (!n2h_node) {
pthread_mutex_unlock(&n2h_lock);
return;
}
if (display_all_stats) {
nssinfo_print_all("n2h", "n2h Stats", nssinfo_n2h_stats_str, NSS_N2H_STATS_MAX, (uint64_t *)n2h_node->node_stats);
pthread_mutex_unlock(&n2h_lock);
return;
}
nssinfo_print_summary("n2h", (uint64_t *)n2h_node->node_stats, NULL, 0);
if (core == (NSS_MAX_CORES - 1)) {
char *format_stats = nssinfo_format_stats(drv_stats[NSS_STATS_DRV_RX_CMD_RESP]);
strlcpy(str_rx, format_stats, sizeof(str_rx));
format_stats = nssinfo_format_stats(drv_stats[NSS_STATS_DRV_TX_CMD_REQ]);
strlcpy(str_tx, format_stats, sizeof(str_tx));
nssinfo_stats_print(nssinfo_summary_fmt, " buf_cmd", str_rx, str_tx, "", "");
memset(str_rx, 0, sizeof(str_rx));
memset(str_tx, 0, sizeof(str_tx));
format_stats = nssinfo_format_stats(drv_stats[NSS_STATS_DRV_RX_EMPTY]);
strlcpy(str_rx, format_stats, sizeof(str_rx));
format_stats = nssinfo_format_stats(drv_stats[NSS_STATS_DRV_TX_EMPTY]);
strlcpy(str_tx, format_stats, sizeof(str_tx));
nssinfo_stats_print(nssinfo_summary_fmt, " buf_emty", str_rx, str_tx, "", "");
memset(str_rx, 0, sizeof(str_rx));
memset(str_tx, 0, sizeof(str_tx));
format_stats = nssinfo_format_stats(drv_stats[NSS_STATS_DRV_RX_PACKET]);
strlcpy(str_rx, format_stats, sizeof(str_rx));
format_stats = nssinfo_format_stats(drv_stats[NSS_STATS_DRV_TX_PACKET]);
strlcpy(str_tx, format_stats, sizeof(str_tx));
nssinfo_stats_print(nssinfo_summary_fmt, " buf_pkt", str_rx, str_tx, "", "");
memset(str_rx, 0, sizeof(str_rx));
format_stats = nssinfo_format_stats(drv_stats[NSS_STATS_DRV_RX_STATUS]);
strlcpy(str_rx, format_stats, sizeof(str_rx));
nssinfo_stats_print(nssinfo_summary_fmt, " status_sync", str_rx, "", "", "");
}
pthread_mutex_unlock(&n2h_lock);
}
/*
* nssinfo_n2h_stats_notify()
* N2H stats notify callback function.
*/
static void nssinfo_n2h_stats_notify(void *data)
{
uint64_t *node_stats;
struct nss_n2h_stats_notification *nss_stats = (struct nss_n2h_stats_notification *)data;
struct node *n2h_node;
struct node **n2h_ptr;
if (!nssinfo_coreid_ifnum_valid(nss_stats->core_id, NSS_N2H_INTERFACE)) {
return;
}
pthread_mutex_lock(&n2h_lock);
n2h_ptr = &nodes[nss_stats->core_id][NSS_N2H_INTERFACE];
n2h_node = *n2h_ptr;
if (n2h_node) {
memcpy(n2h_node->node_stats, &nss_stats->n2h_stats, sizeof(nss_stats->n2h_stats));
memcpy(drv_stats, &nss_stats->drv_stats, sizeof(nss_stats->drv_stats));
pthread_mutex_unlock(&n2h_lock);
return;
}
pthread_mutex_unlock(&n2h_lock);
n2h_node = (struct node *)calloc(1, sizeof(struct node));
if (!n2h_node) {
nssinfo_warn("Failed to allocate memory for N2H node\n");
return;
}
node_stats = (uint64_t *)malloc(sizeof(nss_stats->n2h_stats));
if (!node_stats) {
nssinfo_warn("Failed to allocate memory for n2h node stats\n");
goto n2h_node_free;
}
memcpy(node_stats, &nss_stats->n2h_stats, sizeof(nss_stats->n2h_stats));
memcpy(drv_stats, &nss_stats->drv_stats, sizeof(nss_stats->drv_stats));
n2h_node->node_stats = node_stats;
n2h_node->subsystem_id = NSS_NLCMN_SUBSYS_N2H;
/*
* Notify is guaranteed to be single threaded via Netlink listen callback
*/
pthread_mutex_lock(&n2h_lock);
*n2h_ptr = n2h_node;
pthread_mutex_unlock(&n2h_lock);
return;
n2h_node_free:
free(n2h_node);
}
/*
* nssinfo_n2h_destroy()
* Destroy N2H node.
*/
static void nssinfo_n2h_destroy(uint32_t core_id, uint32_t if_num)
{
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_N2H].is_inited) {
nssinfo_node_stats_destroy(&n2h_lock, core_id, NSS_N2H_INTERFACE);
}
}
/*
* nssinfo_n2h_deinit()
* Deinitialize n2h module.
*/
void nssinfo_n2h_deinit(void *data)
{
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
if (nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_N2H].is_inited) {
pthread_mutex_destroy(&n2h_lock);
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_N2H].is_inited = false;
}
nss_nlmcast_sock_leave_grp(ctx, NSS_NLN2H_MCAST_GRP);
}
/*
* nssinfo_n2h_init()
* Initialize N2H module.
*/
int nssinfo_n2h_init(void *data)
{
int error;
struct nss_nlmcast_ctx *ctx = (struct nss_nlmcast_ctx *)data;
/*
* Subscribe for N2H MCAST group.
*/
nss_nlsock_set_family(&ctx->sock, NSS_NLN2H_FAMILY);
error = nss_nlmcast_sock_join_grp(ctx, NSS_NLN2H_MCAST_GRP);
if (error) {
nssinfo_warn("Unable to join N2H mcast group.\n");
return error;
}
if (nssinfo_stats_info_init(nssinfo_n2h_stats_str,
"/sys/kernel/debug/qca-nss-drv/strings/n2h") != 0) {
goto fail;
}
if (pthread_mutex_init(&n2h_lock, NULL) != 0) {
nssinfo_warn("Mutex init has failed for n2h\n");
goto fail;
}
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_N2H].display = nssinfo_n2h_stats_display;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_N2H].notify = nssinfo_n2h_stats_notify;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_N2H].destroy = nssinfo_n2h_destroy;
nssinfo_subsystem_array[NSS_NLCMN_SUBSYS_N2H].is_inited = true;
return 0;
fail:
nss_nlmcast_sock_leave_grp(ctx, NSS_NLN2H_MCAST_GRP);
return -1;
}

View File

@ -0,0 +1,28 @@
/*
**************************************************************************
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#ifndef __NSSINFO_N2H_H
#define __NSSINFO_N2H_H
/**
* @brief initialize N2H module.
*
* @return 0 on success or -ve for failure
*/
int nssinfo_n2h_init(void *data);
void nssinfo_n2h_deinit(void *data);
#endif /* __NSSINFO_N2H_H*/