From 09fde351bb04818bc517270b70ba0709f9e911be Mon Sep 17 00:00:00 2001 From: Qosmio Date: Sun, 7 Jan 2024 18:07:58 -0500 Subject: [PATCH] nss-udp-st: Add experimental UDP speedtest --- qca/nss-udp-st-drv/Makefile | 53 ++ .../patches/0001-fix-missing-ppp_defs.patch | 10 + .../0002-fix-missing-hrtimer-symbols.patch | 28 + qca/nss-udp-st-drv/src/Makefile | 15 + .../src/exports/nss_udp_st_drv.h | 198 ++++++ qca/nss-udp-st-drv/src/nss_udp_st_drv.c | 393 ++++++++++++ qca/nss-udp-st-drv/src/nss_udp_st_ip.c | 307 ++++++++++ qca/nss-udp-st-drv/src/nss_udp_st_ip.h | 63 ++ qca/nss-udp-st-drv/src/nss_udp_st_public.h | 37 ++ qca/nss-udp-st-drv/src/nss_udp_st_rx.c | 107 ++++ qca/nss-udp-st-drv/src/nss_udp_st_rx.h | 28 + qca/nss-udp-st-drv/src/nss_udp_st_tx.c | 577 ++++++++++++++++++ qca/nss-udp-st-drv/src/nss_udp_st_tx.h | 32 + qca/nss-userspace-oss/nss-udp-st/Makefile | 35 ++ qca/nss-userspace-oss/nss-udp-st/src/Makefile | 8 + .../nss-udp-st/src/nss-udp-st.c | 461 ++++++++++++++ .../nss-udp-st/src/nss-udp-st.h | 73 +++ 17 files changed, 2425 insertions(+) create mode 100644 qca/nss-udp-st-drv/Makefile create mode 100644 qca/nss-udp-st-drv/patches/0001-fix-missing-ppp_defs.patch create mode 100644 qca/nss-udp-st-drv/patches/0002-fix-missing-hrtimer-symbols.patch create mode 100644 qca/nss-udp-st-drv/src/Makefile create mode 100644 qca/nss-udp-st-drv/src/exports/nss_udp_st_drv.h create mode 100644 qca/nss-udp-st-drv/src/nss_udp_st_drv.c create mode 100755 qca/nss-udp-st-drv/src/nss_udp_st_ip.c create mode 100755 qca/nss-udp-st-drv/src/nss_udp_st_ip.h create mode 100644 qca/nss-udp-st-drv/src/nss_udp_st_public.h create mode 100644 qca/nss-udp-st-drv/src/nss_udp_st_rx.c create mode 100644 qca/nss-udp-st-drv/src/nss_udp_st_rx.h create mode 100644 qca/nss-udp-st-drv/src/nss_udp_st_tx.c create mode 100644 qca/nss-udp-st-drv/src/nss_udp_st_tx.h create mode 100755 qca/nss-userspace-oss/nss-udp-st/Makefile create mode 100644 qca/nss-userspace-oss/nss-udp-st/src/Makefile create mode 100644 qca/nss-userspace-oss/nss-udp-st/src/nss-udp-st.c create mode 100644 qca/nss-userspace-oss/nss-udp-st/src/nss-udp-st.h diff --git a/qca/nss-udp-st-drv/Makefile b/qca/nss-udp-st-drv/Makefile new file mode 100644 index 0000000..d0957ba --- /dev/null +++ b/qca/nss-udp-st-drv/Makefile @@ -0,0 +1,53 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=nss-udp-st-drv +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/$(PKG_NAME) + SECTION:=kernel + CATEGORY:=Kernel modules + SUBMENU:=Network Support + DEPENDS:=@(TARGET_qualcommax||TARGET_ipq60xx) +kmod-pppoe + TITLE:=Kernel driver for NSS UDP Speedtest + FILES:=$(PKG_BUILD_DIR)/nss-udp-st.ko +endef + +define KernelPackage/$(PKG_NAME)/Description +Kernel driver for host data path NSS UDP speedtest +endef + +define KernelPackage/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/bin +endef + +ifeq ($(CONFIG_TARGET_SUBTARGET), "ipq807x") + SOC="ipq807x_64" + subtarget:=$(CONFIG_TARGET_SUBTARGET) +else ifeq ($(CONFIG_TARGET_BOARD), "ipq60xx") + SOC="ipq60xx_64" + subtarget:=$(SUBTARGET) +endif + +define Build/InstallDev + $(INSTALL_DIR) $(STAGING_DIR)/usr/include/nss-udp-st-drv + $(CP) $(PKG_BUILD_DIR)/exports/* $(STAGING_DIR)/usr/include/nss-udp-st-drv/ +endef + +EXTRA_CFLAGS+= -I$(STAGING_DIR)/usr/include/nss-udp-st-drv/ + +define Build/Compile + +$(MAKE) -C "$(LINUX_DIR)" \ + CROSS_COMPILE="$(TARGET_CROSS)" \ + ARCH="$(LINUX_KARCH)" \ + M="$(PKG_BUILD_DIR)" \ + EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \ + SoC=$(SOC) \ + $(KERNEL_MAKE_FLAGS) \ + $(PKG_JOBS) \ + modules +endef + +$(eval $(call KernelPackage,$(PKG_NAME))) diff --git a/qca/nss-udp-st-drv/patches/0001-fix-missing-ppp_defs.patch b/qca/nss-udp-st-drv/patches/0001-fix-missing-ppp_defs.patch new file mode 100644 index 0000000..90c5694 --- /dev/null +++ b/qca/nss-udp-st-drv/patches/0001-fix-missing-ppp_defs.patch @@ -0,0 +1,10 @@ +--- a/nss_udp_st_tx.c ++++ b/nss_udp_st_tx.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include "nss_udp_st_public.h" + diff --git a/qca/nss-udp-st-drv/patches/0002-fix-missing-hrtimer-symbols.patch b/qca/nss-udp-st-drv/patches/0002-fix-missing-hrtimer-symbols.patch new file mode 100644 index 0000000..f63b953 --- /dev/null +++ b/qca/nss-udp-st-drv/patches/0002-fix-missing-hrtimer-symbols.patch @@ -0,0 +1,28 @@ +--- a/nss_udp_st_tx.c ++++ b/nss_udp_st_tx.c +@@ -18,7 +18,7 @@ + + #include + #include +-#include ++// #include + #include + #include + #include +@@ -34,6 +34,9 @@ static enum hrtimer_restart tx_hr_restar + static struct vlan_hdr vh; + static struct net_device *xmit_dev; + static struct pppoe_opt info; ++extern void hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode); ++extern void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,enum hrtimer_mode mode); ++extern int hrtimer_cancel(struct hrtimer *timer); + + /* + * nss_udp_st_generate_ipv4_hdr() +@@ -576,3 +579,6 @@ bool nss_udp_st_tx(void) + + return true; + } ++MODULE_AUTHOR("Qualcomm Technologies"); ++MODULE_DESCRIPTION("NSS UDP Speedtest"); ++MODULE_LICENSE("Dual BSD/GPL"); diff --git a/qca/nss-udp-st-drv/src/Makefile b/qca/nss-udp-st-drv/src/Makefile new file mode 100644 index 0000000..b63010f --- /dev/null +++ b/qca/nss-udp-st-drv/src/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for NSS UDP Speed Test +# +obj ?= . + +NSS_UDP_ST_BASE_OBJS := nss_udp_st_drv.o nss_udp_st_rx.o nss_udp_st_tx.o nss_udp_st_ip.o + +obj-m += nss-udp-st.o + +# +# Base files +# +nss-udp-st-objs := $(NSS_UDP_ST_BASE_OBJS) + +ccflags-y += -Werror -Wall -I$(obj) -I$(obj)/exports diff --git a/qca/nss-udp-st-drv/src/exports/nss_udp_st_drv.h b/qca/nss-udp-st-drv/src/exports/nss_udp_st_drv.h new file mode 100644 index 0000000..a754466 --- /dev/null +++ b/qca/nss-udp-st-drv/src/exports/nss_udp_st_drv.h @@ -0,0 +1,198 @@ +/* + ************************************************************************** + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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_UDP_ST_DRV_H +#define __NSS_UDP_ST_DRV_H + +#ifdef __KERNEL__ /* only kernel will use. */ +#include +#endif + +#define NSS_UDP_ST_IFNAMSZ 24 +#define NSS_UDP_ST_IPNAMSZ 40 +#define NSS_UDP_ST_MODESZ 24 + +#ifdef __KERNEL__ /* only kernel will use. */ +#define NSS_UDP_ST_FLAG_IPV4 0x1 +#define NSS_UDP_ST_FLAG_IPV6 0x2 +#endif + +/* + * NSS UDP speedtest ioctl parameters + */ +#define NSS_UDP_ST_IOCTL_MAGIC 'n' +#define NSS_UDP_ST_IOCTL_INIT _IOW(NSS_UDP_ST_IOCTL_MAGIC, 0, struct nss_udp_st_param* ) +#define NSS_UDP_ST_IOCTL_START_TX _IOW(NSS_UDP_ST_IOCTL_MAGIC, 1, int) +#define NSS_UDP_ST_IOCTL_START_RX _IOW(NSS_UDP_ST_IOCTL_MAGIC, 2, int) +#define NSS_UDP_ST_IOCTL_STOP _IO(NSS_UDP_ST_IOCTL_MAGIC, 3) +#define NSS_UDP_ST_DEV "/dev/nss_udp_st" + +#ifdef __KERNEL__ /* only kernel will use. */ +#define NSS_UDP_ST_MAX_HEADROOM 32 /* Maximum headroom needed */ +#define NSS_UDP_ST_MAX_TAILROOM 32 /* Maximum tailroom needed */ +#define NSS_UDP_ST_BUFFER_SIZE_MAX 1500 /* 1500 bytes */ +#define NSS_UDP_ST_RATE_MAX 20000000000 /* 20 Gbps */ + +extern struct nss_udp_st nust; +extern struct delayed_work nss_udp_st_tx_delayed_work; +extern struct workqueue_struct *work_queue; +extern void nss_udp_st_update_stats(size_t pkt_size); +extern uint64_t nss_udp_st_tx_num_pkt; +extern struct net_device *nust_dev; +#endif + +/* + * nss_udp_st_rule + * NSS UDP speedtest rules parameters + */ +enum nss_udp_st_rule { + NSS_UDP_ST_SIP, /* source IP */ + NSS_UDP_ST_DIP, /* destination IP */ + NSS_UDP_ST_SPORT, /* source port */ + NSS_UDP_ST_DPORT, /* destination port */ + NSS_UDP_ST_FLAGS, /* IP version flag */ +}; + +/* + * nss_udp_st_stats_stats + * time stats + */ +enum nss_udp_st_stats_time { + NSS_UDP_ST_STATS_TIME_START, /* Start time of the test */ + NSS_UDP_ST_STATS_TIME_CURRENT, /* Current time of the running test */ + NSS_UDP_ST_STATS_TIME_ELAPSED, /* Elapsed time of the current test */ + NSS_UDP_ST_STATS_TIME_MAX /* Maximum timer statistics type */ +}; + +/* + * nss_udp_st_error + * error stats + */ +enum nss_udp_st_error { + NSS_UDP_ST_ERROR_NONE, /* no error */ + NSS_UDP_ST_ERROR_INCORRECT_RATE, /* incorrect rate */ + NSS_UDP_ST_ERROR_INCORRECT_BUFFER_SIZE, /* incorrect buffer size */ + NSS_UDP_ST_ERROR_MEMORY_FAILURE, /* Memory allocation failed */ + NSS_UDP_ST_ERROR_INCORRECT_IP_VERSION, /* Incorrect IP version */ + NSS_UDP_ST_ERROR_PACKET_DROP, /* Packet Drop */ + NSS_UDP_ST_ERROR_MAX /* Maximum error statistics type */ +}; + +/* + * nss_udp_st_type + * tx/rx flags + */ +enum nss_udp_st_type { + NSS_UDP_ST_TX, /* Tx 0 */ + NSS_UDP_ST_RX, /* Rx 1 */ +}; + +/* + * nss_udp_st_param + * config parameters for Tx test + */ +struct nss_udp_st_param { + uint32_t rate; /* target rate in Mbps */ + uint32_t buffer_sz; /* buffer size of each packet */ + uint32_t dscp; /* dscp flag for tx packet */ + char net_dev[NSS_UDP_ST_IFNAMSZ]; /* net device interface */ +}; + +/* + * nss_udp_st_opt + * 5 tuple config parameters + */ +struct nss_udp_st_opt { + uint16_t sport; /* source port */ + uint16_t dport; /* destination port */ + uint16_t ip_version; /* ip version flag */ + char sip[NSS_UDP_ST_IPNAMSZ]; /* source ip string */ + char dip[NSS_UDP_ST_IPNAMSZ]; /* dest ip string */ +}; + +#ifdef __KERNEL__ /* only kernel will use. */ +/* + * nss_udp_st_mode + * start/stop flags + */ +enum nss_udp_st_mode { + NSS_UDP_ST_STOP, /* Stop 0 */ + NSS_UDP_ST_START, /* Start 1 */ +}; + +/* + * nss_udp_st_ip + * ipv4/ipv6 params + */ +struct nss_udp_st_ip { + union { + uint32_t ipv4; /* IPv4 address. */ + uint32_t ipv6[4]; /* IPv6 address. */ + } ip; +}; + +/* + * nss_udp_st_pkt_stats + * packet stats + */ +struct nss_udp_st_pkt_stats { + atomic_long_t tx_packets; /* Number of packets transmitted */ + atomic_long_t tx_bytes; /* Number of bytes transmitted */ + atomic_long_t rx_packets; /* Number of packets received */ + atomic_long_t rx_bytes; /* Number of bytes received */ +}; +/* + * nss_udp_st_stats + * stats for tx/rx test + */ +struct nss_udp_st_stats { + struct nss_udp_st_pkt_stats p_stats; /* Packet statistics */ + atomic_long_t timer_stats[NSS_UDP_ST_STATS_TIME_MAX]; /* Time statistics */ + atomic_long_t errors[NSS_UDP_ST_ERROR_MAX]; /* Error statistics */ + bool first_pkt; /* First packet flag */ +}; + +/* + * nss_udp_st_rules + * config rules configured for tx/rx test + */ +struct nss_udp_st_rules { + struct list_head list; /* kernel’s list structure */ + struct nss_udp_st_ip sip; /* source ip */ + struct nss_udp_st_ip dip; /* dest ip */ + uint16_t sport; /* source port */ + uint16_t dport; /* dest port */ + uint16_t flags; /* version of IP address */ + uint8_t dst_mac[ETH_ALEN]; /* dest mac */ +}; + +/* + * nss_udp_st + * config/rules/stats for tx/rx test + */ +struct nss_udp_st { + struct nss_udp_st_param config; /* config params for tx */ + struct nss_udp_st_rules rules; /* database for config rules */ + struct nss_udp_st_stats stats; /* result statistics */ + uint32_t rule_count; /* no of rules configured */ + uint64_t time; /* duration of test */ + bool mode; /* start =0; stop=1 */ + bool dir; /* tx=0; rx=1 */ +}; +#endif + +#endif /*NSS_UDP_ST_H*/ diff --git a/qca/nss-udp-st-drv/src/nss_udp_st_drv.c b/qca/nss-udp-st-drv/src/nss_udp_st_drv.c new file mode 100644 index 0000000..44d0532 --- /dev/null +++ b/qca/nss-udp-st-drv/src/nss_udp_st_drv.c @@ -0,0 +1,393 @@ +/* + ************************************************************************** + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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 +#include +#include +#include "nss_udp_st_public.h" + +#define DEVICE_NAME "nss_udp_st" +#define CLASS_NAME "nss_udp_st" + +static const struct file_operations nss_udp_st_ops; +static struct class *dump_class; +static int dump_major; + +struct nss_udp_st nust; +struct delayed_work nss_udp_st_tx_delayed_work; +struct workqueue_struct *work_queue; +void nss_udp_st_update_stats(size_t pkt_size); +uint64_t nss_udp_st_tx_num_pkt; +struct net_device *nust_dev; + +/* + * nss_udp_st_rx_ipv4_pre_routing_hook + * pre-routing hook into netfilter packet monitoring point for IPv4 + */ +static struct nf_hook_ops nss_udp_st_nf_ipv4_ops[] __read_mostly = { + { + .hook = nss_udp_st_rx_ipv4_pre_routing_hook, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_RAW_BEFORE_DEFRAG, + }, +}; + +/* + * nss_udp_st_rx_ipv6_pre_routing_hook + * pre-routing hook into netfilter packet monitoring point for IPv6 + */ +static struct nf_hook_ops nss_udp_st_nf_ipv6_ops[] __read_mostly = { + { + .hook = nss_udp_st_rx_ipv6_pre_routing_hook, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_RAW_BEFORE_DEFRAG, + }, +}; + +/* + * nss_udp_st_check_rules() + * check for ARP resolution and valid return mac + */ +static int nss_udp_st_check_rules(struct nss_udp_st_rules *rules) +{ + if (rules->flags == NSS_UDP_ST_FLAG_IPV4) { + if (nss_udp_st_get_macaddr_ipv4(rules->dip.ip.ipv4, (uint8_t *)&rules->dst_mac)) { + pr_err("Error in Updating the Return MAC Address\n"); + return -EINVAL; + } + } else if (rules->flags == NSS_UDP_ST_FLAG_IPV6) { + if (nss_udp_st_get_macaddr_ipv6(rules->dip.ip.ipv6, (uint8_t *)&rules->dst_mac)) { + pr_err("Error in Updating the Return MAC Address\n"); + return -EINVAL; + } + } else { + pr_err("invalid ip version flag\n"); + return -EINVAL; + } + return 0; +} + +/* + * nss_udp_st_clear_rules() + * clear rules list + */ +static void nss_udp_st_clear_rules(void) +{ + struct nss_udp_st_rules *pos = NULL; + struct nss_udp_st_rules *n = NULL; + + list_for_each_entry_safe(pos, n, &nust.rules.list, list) { + list_del(&pos->list); + kfree(pos); + } + nust.rule_count = 0; +} + +/* + * nss_udp_st_open() + * open for file ops on /dev/nss-udp-st + */ +static int nss_udp_st_open(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * nss_udp_st_release() + * release /dev/nss-udp-st + */ +static int nss_udp_st_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * nss_udp_st_read() + * send stats to userspace + */ +static ssize_t nss_udp_st_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int copied = 0; + copied = copy_to_user(buf, &nust.stats, sizeof(struct nss_udp_st_stats)); + return copied; +} + +/* + * nss_udp_st_write() + * receive rules from userspace + */ +static ssize_t nss_udp_st_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0; + struct nss_udp_st_opt opt; + struct nss_udp_st_rules *rules; + + /* + * don't push rules if test has already started + */ + if (nust.mode == NSS_UDP_ST_START) { + pr_err("Test already started\n"); + return -EINVAL; + } + + rules = (struct nss_udp_st_rules *)kzalloc(sizeof(struct nss_udp_st_rules), GFP_KERNEL); + if (!rules) { + atomic_long_inc(&nust.stats.errors[NSS_UDP_ST_ERROR_MEMORY_FAILURE]); + return -EINVAL; + } + + ret = copy_from_user((void *)(uintptr_t)&opt, (void __user *)buf, sizeof(struct nss_udp_st_opt)); + if (ret) { + kfree(rules); + return -EINVAL; + } + rules->sport = opt.sport; + rules->dport = opt.dport; + if(opt.ip_version == 4) { + rules->flags |= NSS_UDP_ST_FLAG_IPV4; + nss_udp_st_get_ipaddr_ntoh(opt.sip, sizeof(struct in_addr), &rules->sip.ip.ipv4); + nss_udp_st_get_ipaddr_ntoh(opt.dip, sizeof(struct in_addr), &rules->dip.ip.ipv4); + } else if(opt.ip_version == 6) { + rules->flags |= NSS_UDP_ST_FLAG_IPV6; + nss_udp_st_get_ipaddr_ntoh(opt.sip, sizeof(struct in6_addr), rules->sip.ip.ipv6); + nss_udp_st_get_ipaddr_ntoh(opt.dip, sizeof(struct in6_addr), rules->dip.ip.ipv6); + } else { + kfree(rules); + return -EINVAL; + } + + ret = nss_udp_st_check_rules(rules); + if (ret) { + kfree(rules); + return -EINVAL; + } + + list_add_tail(&(rules->list), &(nust.rules.list)); + nust.rule_count++; + return 0; +} + +/* + * nss_udp_st_reset_stats() + * clear stats before starting test + */ +static void nss_udp_st_reset_stats(void) { + memset(&nust.stats, 0, sizeof(struct nss_udp_st_stats)); + nust.stats.first_pkt = true; + nss_udp_st_tx_num_pkt = 0; +} + +/* + * nss_udp_st_ioctl() + * receive ioctl to init / start / stop test + */ +static long nss_udp_st_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long arg) +{ + int ret = 0; + + switch (ioctl_num) { + case NSS_UDP_ST_IOCTL_INIT: + memset(&(nust.config), 0, sizeof(struct nss_udp_st_param)); + ret = copy_from_user((void *)&(nust.config), (void __user *)arg, sizeof(struct nss_udp_st_param)); + if (ret) { + return -EINVAL; + } + break; + + case NSS_UDP_ST_IOCTL_START_TX: + if (nust.mode == NSS_UDP_ST_START) { + pr_err("Tx test already started\n"); + return -EINVAL; + } + + nss_udp_st_reset_stats(); + nust.dir = NSS_UDP_ST_TX; + + ret = copy_from_user((void *)&(nust.time), (void __user *)arg, sizeof(nust.time)); + if (ret) { + return -EINVAL; + } + if (!nust.time) { + nust.time = NSS_UDP_ST_TX_DEFAULT_TIMEOUT; + } + if (!nss_udp_st_tx()) { + pr_err("Unable to start Tx test\n"); + return -EINVAL; + } + nust.mode = NSS_UDP_ST_START; + break; + + case NSS_UDP_ST_IOCTL_START_RX: + if (nust.mode == NSS_UDP_ST_START) { + pr_err("Rx test already started\n"); + return -EINVAL; + } + + nss_udp_st_reset_stats(); + nust.dir = NSS_UDP_ST_RX; + + /* + * register pre-routing hook for rx path + */ + ret = nf_register_net_hooks(&init_net, nss_udp_st_nf_ipv4_ops, ARRAY_SIZE(nss_udp_st_nf_ipv4_ops)); + if (ret < 0) { + pr_err("Can't register Rx netfilter hooks.\n"); + return -EINVAL; + } + + ret = nf_register_net_hooks(&init_net, nss_udp_st_nf_ipv6_ops, ARRAY_SIZE(nss_udp_st_nf_ipv6_ops)); + if (ret < 0) { + pr_err("Can't register Rx netfilter hooks.\n"); + nf_unregister_net_hooks(&init_net, nss_udp_st_nf_ipv4_ops, ARRAY_SIZE(nss_udp_st_nf_ipv4_ops)); + return -EINVAL; + } + nust.mode = NSS_UDP_ST_START; + break; + + case NSS_UDP_ST_IOCTL_STOP: + if (nust.mode == NSS_UDP_ST_STOP) + break; + + nust.mode = NSS_UDP_ST_STOP; + + if (nust.dir == NSS_UDP_ST_RX) { + /* + * de-register pre-routing hook for rx path + */ + nf_unregister_net_hooks(&init_net, nss_udp_st_nf_ipv4_ops, ARRAY_SIZE(nss_udp_st_nf_ipv4_ops)); + nf_unregister_net_hooks(&init_net, nss_udp_st_nf_ipv6_ops, ARRAY_SIZE(nss_udp_st_nf_ipv6_ops)); + } else { + nss_udp_st_hrtimer_cleanup(); + } + nss_udp_st_clear_rules(); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +/* + * file ops for /dev/nss_udp_st + */ +static const struct file_operations nss_udp_st_ops = { + .open = nss_udp_st_open, + .read = nss_udp_st_read, + .write = nss_udp_st_write, + .unlocked_ioctl = nss_udp_st_ioctl, + .release = nss_udp_st_release, +}; + +/* + * nss_udp_st_init() + * create char device /dev/nss_udp_st + */ +static int __init nss_udp_st_init(void) +{ + int ret = 0; + struct device *dump_dev; + + memset(&nust, 0, sizeof(struct nss_udp_st)); + INIT_LIST_HEAD(&(nust.rules.list)); + + dump_major = register_chrdev(UNNAMED_MAJOR, DEVICE_NAME, &nss_udp_st_ops); + if (dump_major < 0) { + ret = dump_major; + pr_err("Unable to allocate a major number err = %d\n", ret); + goto reg_failed; + } + + dump_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(dump_class)) { + ret = PTR_ERR(dump_class); + pr_err("Unable to create dump class = %d\n", ret); + goto class_failed; + } + + dump_dev = device_create(dump_class, NULL, MKDEV(dump_major, 0), NULL, DEVICE_NAME); + if (IS_ERR(dump_dev)) { + ret = PTR_ERR(dump_dev); + pr_err("Unable to create a device err = %d\n", ret); + goto device_failed; + } + return ret; + +device_failed: + class_destroy(dump_class); +class_failed: + unregister_chrdev(dump_major, DEVICE_NAME); +reg_failed: + return ret; +} + +/* + * nss_udp_st_exit() + * clean up for /dev/nss_udp_st + */ +static void __exit nss_udp_st_exit(void) +{ + nust.mode = NSS_UDP_ST_STOP; + device_destroy(dump_class, MKDEV(dump_major, 0)); + class_destroy(dump_class); + unregister_chrdev(dump_major, DEVICE_NAME); +} + +/* + * nss_udp_st_update_stats() + * update packet and time stats for tx/rx + */ +void nss_udp_st_update_stats(size_t pkt_size) +{ + long time_curr; + long time_start; + + if (nust.stats.first_pkt) { + atomic_long_set(&nust.stats.timer_stats[NSS_UDP_ST_STATS_TIME_START], (jiffies * 1000/HZ)); + nust.stats.first_pkt = false; + } + + if (nust.dir == NSS_UDP_ST_TX) { + atomic_long_inc(&nust.stats.p_stats.tx_packets); + atomic_long_add(pkt_size, &nust.stats.p_stats.tx_bytes); + } + + if (nust.dir == NSS_UDP_ST_RX) { + atomic_long_inc(&nust.stats.p_stats.rx_packets); + atomic_long_add(pkt_size, &nust.stats.p_stats.rx_bytes); + } + + atomic_long_set(&nust.stats.timer_stats[NSS_UDP_ST_STATS_TIME_CURRENT], (jiffies * 1000/HZ)); + + time_curr = atomic_long_read(&nust.stats.timer_stats[NSS_UDP_ST_STATS_TIME_CURRENT]); + time_start = atomic_long_read(&nust.stats.timer_stats[NSS_UDP_ST_STATS_TIME_START]); + atomic_long_set(&nust.stats.timer_stats[NSS_UDP_ST_STATS_TIME_ELAPSED], (long)(time_curr - time_start)); +} + +module_init(nss_udp_st_init); +module_exit(nss_udp_st_exit); + +MODULE_AUTHOR("Qualcomm Technologies"); +MODULE_DESCRIPTION("NSS UDP Speedtest"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/qca/nss-udp-st-drv/src/nss_udp_st_ip.c b/qca/nss-udp-st-drv/src/nss_udp_st_ip.c new file mode 100755 index 0000000..02d5956 --- /dev/null +++ b/qca/nss-udp-st-drv/src/nss_udp_st_ip.c @@ -0,0 +1,307 @@ +/* + ************************************************************************** + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nss_udp_st_ip.h" + +#define NSS_UDP_ST_IPV4_SIZE sizeof(struct in_addr) +#define NSS_UDP_ST_IPV6_SIZE sizeof(struct in6_addr) + +/* + * nss_udp_st_get_ipaddr_ntoh() + * extract the IPv4 or IPv6 address in host order from the incoming data + */ +int nss_udp_st_get_ipaddr_ntoh(const char *arg, uint16_t data_sz, uint32_t *data) +{ + + uint32_t dest[4]; + + if (!arg || !data) { + return -EINVAL; + } + + switch(data_sz) { + case NSS_UDP_ST_IPV4_SIZE: /* IPv4 */ + if(!in4_pton(arg, -1, (uint8_t *)data, '\0', NULL)) { + return -EINVAL; + } + + data[0] = ntohl(data[0]); + return 0; + + case NSS_UDP_ST_IPV6_SIZE: /* IPv6 */ + if(!in6_pton(arg, -1,(uint8_t *)data, -1, NULL)) { + return -EINVAL; + } + + nss_udp_st_swap_addr_ipv6(data, dest); + + data[0] = ntohl(dest[0]); + data[1] = ntohl(dest[1]); + data[2] = ntohl(dest[2]); + data[3] = ntohl(dest[3]); + + return 0; + + default: + pr_err("IP address storage incorrect:%d\n", data_sz); + return -E2BIG; + } +} + +/* + * nss_udp_st_get_neigh_ipv4() + * Returns neighbour reference for a given IP address + */ +static struct neighbour *nss_udp_st_get_neigh_ipv4(uint32_t ip_addr) +{ + struct neighbour *neigh; + struct rtable *rt; + struct dst_entry *dst; + + /* + * search for route entry + */ + rt = ip_route_output(&init_net, ip_addr, 0, 0, 0); + if (IS_ERR(rt)) { + return NULL; + } + + dst = (struct dst_entry *)rt; + + /* + * neighbour lookup using IP address in the route table + */ + neigh = dst_neigh_lookup(dst, &ip_addr); + if (likely(neigh)) { + dst_release(dst); + return neigh; + } + + /* + * neighbour lookup using IP address, device in the arp table + */ + neigh = neigh_lookup(&arp_tbl, &ip_addr, dst->dev); + if (likely(neigh)) { + dst_release(dst); + return neigh; + } + + /* + * dst reference count was held during the lookup + */ + dst_release(dst); + return NULL; +} + +/* + * nss_udp_st_get_macaddr_get_ipv4() + * Return the hardware (MAC) address of the given IPv4 address, if any. + * + * Returns 0 on success or a negative result on failure. + * We look up the rtable entry for the address and, + * from its neighbour structure,obtain the hardware address. + * This means we will also work if the neighbours are routers too. + */ +int nss_udp_st_get_macaddr_ipv4(uint32_t ip_addr, uint8_t mac_addr[]) +{ + struct neighbour *neigh; + + /* + * handle multicast IP address seperately + */ + if (ipv4_is_multicast(htonl(ip_addr))) { + return -EINVAL; + } + + /* + * retrieve the neighbour + */ + rcu_read_lock(); + neigh = nss_udp_st_get_neigh_ipv4(htonl(ip_addr)); + if (!neigh) { + rcu_read_unlock(); + pr_err("neighbour lookup failed for IP:0x%x\n", ip_addr); + return -ENODEV; + } + rcu_read_unlock(); + + if ((neigh->nud_state & NUD_VALID) == 0) { + pr_err("neighbour state is invalid for IP:0x%x\n", ip_addr); + goto fail; + } + + if (!neigh->dev) { + pr_err("neighbour device not found for IP:0x%x\n", ip_addr); + goto fail; + } + + if (is_multicast_ether_addr(neigh->ha)) { + pr_err( "neighbour MAC address is multicast or broadcast\n"); + goto fail; + } + + ether_addr_copy(mac_addr, neigh->ha); + neigh_release(neigh); + return 0; + +fail: + neigh_release(neigh); + return -ENODEV; +} + +/* + * nss_udp_st_get_neigh_ipv6() + * Returns neighbour reference for a given IP address + */ +struct neighbour *nss_udp_st_get_neigh_ipv6(uint32_t dst_addr[4]) +{ + struct neighbour *neigh; + struct dst_entry *dst; + struct rt6_info *rt; + struct in6_addr daddr; + + NSS_UDP_ST_IPV6_ADDR_TO_IN6_ADDR(daddr, dst_addr); + + rt = rt6_lookup(&init_net, &daddr, NULL, 0, NULL, 0); + if (!rt) { + return NULL; + } + + dst = (struct dst_entry *)rt; + + /* + * neighbour lookup using IP address in the route table + */ + neigh = dst_neigh_lookup(dst, &daddr); + if (likely(neigh)) { + neigh_hold(neigh); + dst_release(dst); + return neigh; + } + dst_release(dst); + + return NULL; +} + +/* + * nss_udp_st_get_ipv6_addr_hton() + * Convert the ipv6 address from host order to network order. + */ +void nss_udp_st_get_ipv6_addr_hton(uint32_t src[4], uint32_t dst[4]) +{ + nss_udp_st_swap_addr_ipv6(src, dst); + + dst[0] = htonl(dst[0]); + dst[1] = htonl(dst[1]); + dst[2] = htonl(dst[2]); + dst[3] = htonl(dst[3]); +} + +/* + * nss_udp_st_get_addr_ntoh() + * Convert the ipv6 address from network order to host order. + */ +void nss_udp_st_get_ipv6_addr_ntoh(uint32_t src[4], uint32_t dst[4]) +{ + nss_udp_st_swap_addr_ipv6(src, dst); + + dst[0] = ntohl(dst[0]); + dst[1] = ntohl(dst[1]); + dst[2] = ntohl(dst[2]); + dst[3] = ntohl(dst[3]); +} + +/* + * nss_udp_st_get_macaddr_get_ipv6() + * Return the hardware (MAC) address of the given ipv6 address, if any. + * + * Returns 0 on success or a negative result on failure. + * We look up the rtable entry for the address and, + * from its neighbour structure,obtain the hardware address. + * This means we will also work if the neighbours are routers too. + */ +int nss_udp_st_get_macaddr_ipv6(uint32_t ip_addr[4], uint8_t mac_addr[]) +{ + struct neighbour *neigh; + struct in6_addr addr; + + nss_udp_st_get_ipv6_addr_hton(ip_addr, addr.s6_addr32); + + if (ipv6_addr_is_multicast(&addr)) { + return 0; + } + + /* + * retrieve the neighbour + */ + rcu_read_lock(); + neigh = nss_udp_st_get_neigh_ipv6(addr.s6_addr32); + if (!neigh) { + rcu_read_unlock(); + pr_info("neighbour lookup failed for %pI6c\n", addr.s6_addr32); + return -ENODEV; + } + rcu_read_unlock(); + + if ((neigh->nud_state & NUD_VALID) == 0) { + pr_err("neighbour state is invalid for %pI6c\n", addr.s6_addr32); + goto fail; + } + + if (!neigh->dev) { + pr_err("neighbour device not found for %pI6c\n", addr.s6_addr32); + goto fail; + } + + if (is_multicast_ether_addr(neigh->ha)) { + pr_err("neighbour MAC address is multicast or broadcast\n"); + goto fail; + } + + ether_addr_copy(mac_addr, neigh->ha); + neigh_release(neigh); + return 0; +fail: + neigh_release(neigh); + return -ENODEV; +} + +/* + * nss_udp_st_compare_ipv6() + * Comapre ipv6 source and dest addresses + */ +bool nss_udp_st_compare_ipv6(uint32_t src[4], uint32_t dst[4]) +{ + if ((src[0] == dst[0]) && (src[1] == dst[1]) && + (src[2] == dst[2]) && (src[3]==dst[3])) { + return true; + } + + return false; +} diff --git a/qca/nss-udp-st-drv/src/nss_udp_st_ip.h b/qca/nss-udp-st-drv/src/nss_udp_st_ip.h new file mode 100755 index 0000000..a3424b0 --- /dev/null +++ b/qca/nss-udp-st-drv/src/nss_udp_st_ip.h @@ -0,0 +1,63 @@ +/* + ************************************************************************** + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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_UDP_ST_IP_H +#define __NSS_UDP_ST_IP_H + +/* + * Converts the format of an IPv6 address from NSS to Linux + */ +#define NSS_UDP_ST_IPV6_ADDR_TO_IN6_ADDR(in6, ipv6) \ + { \ + in6.in6_u.u6_addr32[0] = ((uint32_t *)ipv6)[0]; \ + in6.in6_u.u6_addr32[1] = ((uint32_t *)ipv6)[1]; \ + in6.in6_u.u6_addr32[2] = ((uint32_t *)ipv6)[2]; \ + in6.in6_u.u6_addr32[3] = ((uint32_t *)ipv6)[3]; \ + } + +int nss_udp_st_get_ipaddr_ntoh(const char *arg, uint16_t data_sz, uint32_t *data); + +void nss_udp_st_get_ipv6_addr_hton(uint32_t src[4], uint32_t dst[4]); + +void nss_udp_st_get_ipv6_addr_ntoh(uint32_t src[4], uint32_t dst[4]); + +int nss_udp_st_get_macaddr_ipv4(uint32_t ip_addr, uint8_t mac_addr[]); + +int nss_udp_st_get_macaddr_ipv6(uint32_t ip_addr[4], uint8_t mac_addr[]); + +bool nss_udp_st_compare_ipv6(uint32_t src[4], uint32_t dst[4]); + +/* + * nss_udp_st_swap_addr_ipv6() + * Swap the ipv6 source and destination address. + */ +static inline void nss_udp_st_swap_addr_ipv6(uint32_t *src, uint32_t *dst) +{ + uint32_t temp[4]; + + if (src == dst) { + memcpy(temp, src, sizeof(temp)); + src = temp; + } + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +#endif /*NSS_UDP_ST_IP_H*/ diff --git a/qca/nss-udp-st-drv/src/nss_udp_st_public.h b/qca/nss-udp-st-drv/src/nss_udp_st_public.h new file mode 100644 index 0000000..2ea0203 --- /dev/null +++ b/qca/nss-udp-st-drv/src/nss_udp_st_public.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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 nss_udp_st_public.h + * NSS UDP ST Public definitions. + */ + +#ifndef _NSS_UDP_ST_PUBLIC_H_ +#define _NSS_UDP_ST_PUBLIC_H_ + +/** + * @addtogroup nss_udp_st_public_subsystem + * @{ + */ + +#include "nss_udp_st_drv.h" +#include "nss_udp_st_tx.h" +#include "nss_udp_st_rx.h" +#include "nss_udp_st_ip.h" + +/** @} */ /* end_addtogroup nss_udp_st_public_subsystem */ + +#endif /*_NSS_UDP_ST_PUBLIC_H_*/ diff --git a/qca/nss-udp-st-drv/src/nss_udp_st_rx.c b/qca/nss-udp-st-drv/src/nss_udp_st_rx.c new file mode 100644 index 0000000..34dda17 --- /dev/null +++ b/qca/nss-udp-st-drv/src/nss_udp_st_rx.c @@ -0,0 +1,107 @@ +/* + ************************************************************************** + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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 +#include +#include +#include "nss_udp_st_public.h" + +/* + * nss_udp_st_rx_ipv4_pre_routing_hook() + * pre-routing hook into netfilter packet monitoring point for IPv4 + */ +unsigned int nss_udp_st_rx_ipv4_pre_routing_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) +{ + struct udphdr *uh; + struct iphdr *iph; + struct nss_udp_st_rules *rules = NULL; + struct nss_udp_st_rules *n = NULL; + + iph = (struct iphdr *)skb_network_header(skb); + + /* + * Not a UDP speedtest packet + */ + if (iph->protocol != IPPROTO_UDP) { + return NF_ACCEPT; + } + + uh = (struct udphdr *)skb_transport_header(skb); + + list_for_each_entry_safe(rules, n, &nust.rules.list, list) { + /* + * If incoming packet matches 5tuple, it is a speedtest packet. + * Increase Rx packet stats and drop packet. + */ + if ((rules->flags & NSS_UDP_ST_FLAG_IPV4) && + (rules->sip.ip.ipv4 == ntohl(iph->daddr)) && + (rules->dip.ip.ipv4 == ntohl(iph->saddr)) && + (rules->sport == ntohs(uh->source)) && + (rules->dport == ntohs(uh->dest)) ) { + nss_udp_st_update_stats(ntohs(iph->tot_len) + sizeof(struct ethhdr)); + kfree_skb(skb); + return NF_STOLEN; + } + } + return NF_ACCEPT; +} + +/* + * nss_udp_st_rx_ipv6_pre_routing_hook() + * pre-routing hook into netfilter packet monitoring point for IPv6 + */ +unsigned int nss_udp_st_rx_ipv6_pre_routing_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) +{ + struct udphdr *uh; + struct ipv6hdr *iph; + struct in6_addr saddr; + struct in6_addr daddr; + struct nss_udp_st_rules *rules = NULL; + struct nss_udp_st_rules *n = NULL; + + iph = (struct ipv6hdr *)skb_network_header(skb); + + /* + * Not a UDP speedtest packet + */ + if (iph->nexthdr != IPPROTO_UDP) { + return NF_ACCEPT; + } + + uh = (struct udphdr *)skb_transport_header(skb); + + nss_udp_st_get_ipv6_addr_ntoh(iph->saddr.s6_addr32, saddr.s6_addr32); + nss_udp_st_get_ipv6_addr_ntoh(iph->daddr.s6_addr32, daddr.s6_addr32); + + list_for_each_entry_safe(rules, n, &nust.rules.list, list) { + + /* + * If incoming packet matches 5tuple, it is a speedtest packet. + * Increase Rx packet stats and drop packet. + */ + if ((rules->flags & NSS_UDP_ST_FLAG_IPV6) && + (nss_udp_st_compare_ipv6(rules->sip.ip.ipv6, daddr.s6_addr32)) && + (nss_udp_st_compare_ipv6(rules->dip.ip.ipv6, saddr.s6_addr32)) && + (rules->sport == ntohs(uh->source)) && + (rules->dport == ntohs(uh->dest))) { + nss_udp_st_update_stats(ntohs(iph->payload_len) + sizeof(struct ethhdr)); + kfree_skb(skb); + return NF_STOLEN; + } + } + return NF_ACCEPT; +} diff --git a/qca/nss-udp-st-drv/src/nss_udp_st_rx.h b/qca/nss-udp-st-drv/src/nss_udp_st_rx.h new file mode 100644 index 0000000..c5fcd98 --- /dev/null +++ b/qca/nss-udp-st-drv/src/nss_udp_st_rx.h @@ -0,0 +1,28 @@ +/* + ************************************************************************** + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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_UDP_ST_RX_H +#define __NSS_UDP_ST_RX_H + +#include +#include + +unsigned int nss_udp_st_rx_ipv4_pre_routing_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); + +unsigned int nss_udp_st_rx_ipv6_pre_routing_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); + +#endif /*NSS_UDP_ST_RX_H*/ diff --git a/qca/nss-udp-st-drv/src/nss_udp_st_tx.c b/qca/nss-udp-st-drv/src/nss_udp_st_tx.c new file mode 100644 index 0000000..08a968b --- /dev/null +++ b/qca/nss-udp-st-drv/src/nss_udp_st_tx.c @@ -0,0 +1,577 @@ +/* + ************************************************************************** + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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 +#include +#include +#include +#include +#include +#include +#include +#include "nss_udp_st_public.h" + +int tx_timer_flag; +static ktime_t kt; +static struct hrtimer tx_hr_timer; +static enum hrtimer_restart tx_hr_restart = HRTIMER_NORESTART; +static struct vlan_hdr vh; +static struct net_device *xmit_dev; +static struct pppoe_opt info; + +/* + * nss_udp_st_generate_ipv4_hdr() + * generate ipv4 header + */ +static inline void nss_udp_st_generate_ipv4_hdr(struct iphdr *iph, uint16_t ip_len, struct nss_udp_st_rules *rules) +{ + iph->version = 4; + iph->ihl = 5; + iph->tos = nust.config.dscp; + iph->tot_len = htons(ip_len); + iph->id = 0; + iph->frag_off = 0; + iph->ttl = 64; + iph->protocol = IPPROTO_UDP; + iph->check = 0; + iph->saddr = htonl(rules->sip.ip.ipv4); + iph->daddr = htonl(rules->dip.ip.ipv4); + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); +} + +/* + * nss_udp_st_generate_ipv6_hdr() + * generate ipv6 header + */ +static inline void nss_udp_st_generate_ipv6_hdr(struct ipv6hdr *ipv6h, uint16_t ip_len, struct nss_udp_st_rules *rules) +{ + struct in6_addr addr; + + ipv6h->version = 6; + memset(&ipv6h->flow_lbl, 0, sizeof(ipv6h->flow_lbl)); + ipv6h->nexthdr = IPPROTO_UDP; + ipv6h->payload_len = htons(ip_len - sizeof(*ipv6h)); + ipv6h->hop_limit = 64; + nss_udp_st_get_ipv6_addr_hton(rules->sip.ip.ipv6, addr.s6_addr32); + memcpy(ipv6h->saddr.s6_addr32, addr.s6_addr32, sizeof(ipv6h->saddr.s6_addr32)); + nss_udp_st_get_ipv6_addr_hton(rules->dip.ip.ipv6, addr.s6_addr32); + memcpy(ipv6h->daddr.s6_addr32, addr.s6_addr32, sizeof(ipv6h->daddr.s6_addr32)); +} + +/* + * nss_udp_st_generate_udp_hdr() + * generate udp header + */ +static void nss_udp_st_generate_udp_hdr(struct udphdr *uh, uint16_t udp_len, struct nss_udp_st_rules *rules) +{ + + uh->source = htons(rules->sport); + uh->dest = htons(rules->dport); + uh->len = htons(udp_len); + + if (rules->flags & NSS_UDP_ST_FLAG_IPV4) { + uh->check = csum_tcpudp_magic(rules->sip.ip.ipv4, rules->dip.ip.ipv4, udp_len, IPPROTO_UDP, + csum_partial(uh, udp_len, 0)); + } else if (rules->flags & NSS_UDP_ST_FLAG_IPV6) { + struct in6_addr saddr; + struct in6_addr daddr; + + nss_udp_st_get_ipv6_addr_hton(rules->sip.ip.ipv6, saddr.s6_addr32); + nss_udp_st_get_ipv6_addr_hton(rules->dip.ip.ipv6, daddr.s6_addr32); + + uh->check = csum_ipv6_magic(&saddr, &daddr, udp_len, IPPROTO_UDP, + csum_partial(uh, udp_len, 0)); + } else { + atomic_long_inc(&nust.stats.errors[NSS_UDP_ST_ERROR_INCORRECT_IP_VERSION]); + return; + } + + if (uh->check == 0) { + uh->check = CSUM_MANGLED_0; + } +} + +/* + * nss_udp_st_generate_eth_hdr() + * generate L2 header + */ +static inline void nss_udp_st_generate_eth_hdr(struct sk_buff *skb, const uint8_t *src_mac, uint8_t *dst_mac) +{ + struct ethhdr *eh = (struct ethhdr *)skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + + eh->h_proto = skb->protocol; + memcpy(eh->h_source, src_mac, ETH_ALEN); + memcpy(eh->h_dest, dst_mac, ETH_ALEN); +} + +/* + * nss_udp_st_generate_vlan_hdr + * Generate VLAN header + */ +static void nss_udp_st_generate_vlan_hdr(struct sk_buff *skb, struct net_device *ndev) +{ + struct vlan_hdr *vhdr; + + skb_push(skb, VLAN_HLEN); + vhdr = (struct vlan_hdr *)skb->data; + vhdr->h_vlan_TCI = htons(vh.h_vlan_TCI); + vhdr->h_vlan_encapsulated_proto = skb->protocol; + skb->protocol = htons(vh.h_vlan_encapsulated_proto); +} + +/* + * nss_udp_st_generate_pppoe_hdr + * Generate PPPoE header + */ +static void nss_udp_st_generate_pppoe_hdr(struct sk_buff *skb, uint16_t ppp_protocol) +{ + struct pppoe_hdr *ph; + unsigned char *pp; + unsigned int data_len; + + /* + * Insert the PPP header protocol + */ + pp = skb_push(skb, 2); + put_unaligned_be16(ppp_protocol, pp); + + data_len = skb->len; + + ph = (struct pppoe_hdr *)skb_push(skb, sizeof(*ph)); + skb_reset_network_header(skb); + + /* + * Headers in skb will look like in below sequence + * | PPPoE hdr(6 bytes) | PPP hdr (2 bytes) | L3 hdr | + * + * The length field in the PPPoE header indicates the length of the PPPoE payload which + * consists of a 2-byte PPP header plus a skb->len. + */ + ph->ver = 1; + ph->type = 1; + ph->code = 0; + ph->sid = (uint16_t)info.pa.sid; + ph->length = htons(data_len); + + skb->protocol = htons(ETH_P_PPP_SES); +} + +/* + * nss_udp_st_tx_packets() + * allocate, populate and send tx packet + */ +static void nss_udp_st_tx_packets(struct net_device *ndev, struct nss_udp_st_rules *rules) +{ + struct sk_buff *skb; + struct udphdr *uh; + struct iphdr *iph; + struct ipv6hdr *ipv6h; + size_t align_offset; + size_t skb_sz; + size_t pkt_sz; + uint16_t ip_len; + uint16_t udp_len; + unsigned char *data; + uint16_t ppp_protocol; + + pkt_sz = nust.config.buffer_sz; + ip_len = pkt_sz; + + if (rules->flags & NSS_UDP_ST_FLAG_IPV4) { + udp_len = pkt_sz - sizeof(*iph); + } else if (rules->flags & NSS_UDP_ST_FLAG_IPV6) { + udp_len = pkt_sz - sizeof(*ipv6h); + } else { + atomic_long_inc(&nust.stats.errors[NSS_UDP_ST_ERROR_INCORRECT_IP_VERSION]); + return; + } + + skb_sz = NSS_UDP_ST_MIN_HEADROOM + pkt_sz + sizeof(struct ethhdr) + NSS_UDP_ST_MIN_TAILROOM + SMP_CACHE_BYTES; + + skb = dev_alloc_skb(skb_sz); + if (!skb) { + atomic_long_inc(&nust.stats.errors[NSS_UDP_ST_ERROR_MEMORY_FAILURE]); + return; + } + + align_offset = PTR_ALIGN(skb->data, SMP_CACHE_BYTES) - skb->data; + skb_reserve(skb, NSS_UDP_ST_MAX_HEADROOM + align_offset + sizeof(uint16_t)); + + /* + * populate udp header + */ + skb_push(skb, sizeof(*uh)); + skb_reset_transport_header(skb); + uh = udp_hdr(skb); + nss_udp_st_generate_udp_hdr(uh, udp_len, rules); + + /* + * populate ipv4 or ipv6 header + */ + if (rules->flags & NSS_UDP_ST_FLAG_IPV4) { + skb_push(skb, sizeof(*iph)); + skb_reset_network_header(skb); + iph = ip_hdr(skb); + nss_udp_st_generate_ipv4_hdr(iph, ip_len, rules); + data = skb_put(skb, pkt_sz - sizeof(*iph) - sizeof(*uh)); + memset(data, 0, pkt_sz - sizeof(*iph) - sizeof(*uh)); + } else if (rules->flags & NSS_UDP_ST_FLAG_IPV6) { + skb_push(skb, sizeof(*ipv6h)); + skb_reset_network_header(skb); + ipv6h = ipv6_hdr(skb); + nss_udp_st_generate_ipv6_hdr(ipv6h, ip_len, rules); + data = skb_put(skb, pkt_sz - sizeof(*ipv6h) - sizeof(*uh)); + memset(data, 0, pkt_sz - sizeof(*ipv6h) - sizeof(*uh)); + } else { + atomic_long_inc(&nust.stats.errors[NSS_UDP_ST_ERROR_INCORRECT_IP_VERSION]); + kfree_skb(skb); + return; + } + + switch (ndev->type) { + case ARPHRD_PPP: + if (rules->flags & NSS_UDP_ST_FLAG_IPV4) { + ppp_protocol = PPP_IP; + } else { + ppp_protocol = PPP_IPV6; + } + + nss_udp_st_generate_pppoe_hdr(skb, ppp_protocol); + + if(is_vlan_dev(info.dev)) { + nss_udp_st_generate_vlan_hdr(skb, info.dev); + } + + /* + * populate ethernet header + */ + nss_udp_st_generate_eth_hdr(skb, xmit_dev->dev_addr, info.pa.remote); + break; + + case ARPHRD_ETHER: + if (rules->flags & NSS_UDP_ST_FLAG_IPV4) { + skb->protocol = htons(ETH_P_IP); + } else { + skb->protocol = htons(ETH_P_IPV6); + } + + if(is_vlan_dev(ndev)) { + nss_udp_st_generate_vlan_hdr(skb, ndev); + } + + /* + * populate ethernet header + */ + nss_udp_st_generate_eth_hdr(skb, xmit_dev->dev_addr, rules->dst_mac); + break; + + default: + break; + } + + /* + * tx packet + */ + skb->dev = xmit_dev; + if (xmit_dev->netdev_ops->ndo_start_xmit(skb, xmit_dev) != NETDEV_TX_OK) { + kfree_skb(skb); + atomic_long_inc(&nust.stats.errors[NSS_UDP_ST_ERROR_PACKET_DROP]); + return; + } + + nss_udp_st_update_stats(ip_len + sizeof(struct ethhdr)); +} + +/* + * nss_udp_st_set_dev() + * get net_device + */ +static bool nss_udp_st_set_dev(void) +{ + nust_dev = dev_get_by_name(&init_net, nust.config.net_dev); + if (!nust_dev) { + pr_err("Cannot find the net device\n"); + return false; + } + return true; +} + +/* + * nss_udp_st_tx_valid() + * check if test time has elapsed + */ +bool nss_udp_st_tx_valid(void) +{ + long long elapsed = atomic_long_read(&nust.stats.timer_stats[NSS_UDP_ST_STATS_TIME_ELAPSED]); + + if (elapsed < (nust.time * 1000)) { + return true; + } + nust.mode = NSS_UDP_ST_STOP; + return false; +} + +/* + * nss_udp_st_vlan_iface_config + * Configure the WLAN interface as VLAN + */ +static int nss_udp_st_vlan_iface_config(struct net_device *dev) +{ + xmit_dev = vlan_dev_next_dev(dev); + if (!xmit_dev) { + pr_err("Cannot find the physical net device\n"); + return -1; + } + + if (is_vlan_dev(xmit_dev) || xmit_dev->type != ARPHRD_ETHER) { + pr_warn("%px: QinQ or non-ethernet VLAN master (%s) is not supported\n", dev, + xmit_dev->name); + return -1; + } + + vh.h_vlan_TCI = vlan_dev_vlan_id(dev); + vh.h_vlan_encapsulated_proto = ntohs(vlan_dev_vlan_proto(dev)); + + return 0; +} + +/* + * nss_udp_st_pppoe_iface_config + * Configure the WLAN interface as PPPoE + */ +static int nss_udp_st_pppoe_iface_config(struct net_device *dev) +{ + struct ppp_channel *ppp_chan[1]; + int channel_count; + int channel_protocol; + int ret = 0; + + /* + * Gets the PPPoE channel information. + */ + channel_count = ppp_hold_channels(dev, ppp_chan, 1); + if (channel_count != 1) { + pr_warn("%px: Unable to get the channel for device: %s\n", dev, dev->name); + return -1; + } + + channel_protocol = ppp_channel_get_protocol(ppp_chan[0]); + if (channel_protocol != PX_PROTO_OE) { + pr_warn("%px: PPP channel protocol is not PPPoE for device: %s\n", dev, dev->name); + ppp_release_channels(ppp_chan, 1); + return -1; + } + + if (pppoe_channel_addressing_get(ppp_chan[0], &info)) { + pr_warn("%px: Unable to get the PPPoE session information for device: %s\n", dev, dev->name); + ppp_release_channels(ppp_chan, 1); + return -1; + } + + /* + * Check if the next device is a VLAN (eth0-eth0.100-pppoe-wan) + */ + if (is_vlan_dev(info.dev)) { + /* + * Next device is a VLAN device (eth0.100) + */ + if (nss_udp_st_vlan_iface_config(info.dev) < 0) { + pr_warn("%px: Unable to get PPPoE's VLAN device's (%s) next dev\n", dev, + info.dev->name); + ret = -1; + goto fail; + } + } else { + /* + * PPPoE interface can be created on linux bridge, OVS bridge and LAG devices. + * udp_st doesn't support these hierarchies. + */ + if ((info.dev->priv_flags & (IFF_EBRIDGE | IFF_OPENVSWITCH)) + || ((info.dev->flags & IFF_MASTER) && (info.dev->priv_flags & IFF_BONDING))) { + pr_warn("%px: PPPoE over bridge and LAG interfaces are not supported, dev: %s info.dev: %s\n",dev, dev->name, info.dev->name); + ret = -1; + goto fail; + + } + + /* + * PPPoE only (eth0-pppoe-wan) + */ + xmit_dev = info.dev; + } + +fail: + dev_put(info.dev); + ppp_release_channels(ppp_chan, 1); + return ret; +} + +/* + * nss_udp_st_tx_work_send_packets() + * generate and send packets per rule + */ +static void nss_udp_st_tx_work_send_packets(void) +{ + int i = 0; + struct nss_udp_st_rules *pos = NULL; + struct nss_udp_st_rules *n = NULL; + + if (!nss_udp_st_tx_valid() || nust.mode == NSS_UDP_ST_STOP ) { + dev_put(nust_dev); + tx_hr_restart = HRTIMER_NORESTART; + return; + } + + list_for_each_entry_safe(pos, n, &nust.rules.list, list) { + for (i = 0; i < nss_udp_st_tx_num_pkt; i++) { + /* + * check if test time has elapsed or test has been stopped + */ + if (!nss_udp_st_tx_valid() || nust.mode == NSS_UDP_ST_STOP ) { + dev_put(nust_dev); + tx_hr_restart = HRTIMER_NORESTART; + return; + } + + nss_udp_st_tx_packets(nust_dev, pos); + } + } + tx_hr_restart = HRTIMER_RESTART; +} + +/* + * nss_udp_st_tx_init() + * initialize speedtest for tx + */ +static bool nss_udp_st_tx_init(void) +{ + uint64_t total_bps; + + if (nust.config.rate > NSS_UDP_ST_RATE_MAX) { + atomic_long_inc(&nust.stats.errors[NSS_UDP_ST_ERROR_INCORRECT_RATE]); + return false; + } + + if (nust.config.buffer_sz > NSS_UDP_ST_BUFFER_SIZE_MAX) { + atomic_long_inc(&nust.stats.errors[NSS_UDP_ST_ERROR_INCORRECT_BUFFER_SIZE]); + return false; + } + total_bps = (uint64_t)nust.config.rate * 1024 * 1024; + + /* + * calculate number of pkts to send per rule per 10 ms + */ + nss_udp_st_tx_num_pkt = total_bps / (nust.rule_count * (nust.config.buffer_sz + sizeof(struct ethhdr)) * 8 * NSS_UDP_ST_TX_TIMER); + nss_udp_st_tx_num_pkt ++; + pr_debug("total number of packets to tx every 100ms %llu\n",nss_udp_st_tx_num_pkt); + if(!nss_udp_st_set_dev()) { + return false; + } + + return true; +} + +/* + * nss_udp_st_hrtimer_cleanup() + * cancel hrtimer + */ +void nss_udp_st_hrtimer_cleanup(void) +{ + hrtimer_cancel(&tx_hr_timer); + tx_hr_restart = HRTIMER_NORESTART; +} + +/* + * nss_udp_st_hrtimer_callback() + * hrtimer callback function + */ +static enum hrtimer_restart nss_udp_st_hrtimer_callback(struct hrtimer *timer) +{ + nss_udp_st_tx_work_send_packets(); + if(tx_hr_restart == HRTIMER_RESTART) { + hrtimer_forward_now(timer, kt); + } + return tx_hr_restart; +} + +/* + * nss_udp_st_hrtimer_init() + * initialize hrtimer + */ +void nss_udp_st_hrtimer_init(void) +{ + tx_hr_restart = HRTIMER_RESTART; + kt = ktime_set(0,10000000); + hrtimer_init(&tx_hr_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS_HARD); + tx_hr_timer.function = &nss_udp_st_hrtimer_callback; +} + +/* + * nss_udp_st_tx() + * start speedtest for tx + */ +bool nss_udp_st_tx(void) +{ + if (!nss_udp_st_tx_init()) { + return false; + } + + switch (nust_dev->type) { + case ARPHRD_PPP: + if(nss_udp_st_pppoe_iface_config(nust_dev) < 0) { + pr_err("Could not configure pppoe, dev: %s\n", nust_dev->name); + return false; + } + break; + + case ARPHRD_ETHER: + if ((nust_dev->priv_flags & (IFF_EBRIDGE | IFF_OPENVSWITCH)) + || ((nust_dev->flags & IFF_MASTER) && (nust_dev->priv_flags & IFF_BONDING))) { + pr_err("Bridge and LAG interfaces are not supported, dev: %s\n", nust_dev->name); + return false; + } + + if (is_vlan_dev(nust_dev)) { + if (nss_udp_st_vlan_iface_config(nust_dev) < 0) { + pr_err("Could not configure vlan, dev: %s\n", nust_dev->name); + return false; + } + } else { + xmit_dev = nust_dev; + } + + break; + + default: + pr_err("Unsupported speedtest interface: %s\n", nust_dev->name); + return false; + } + + pr_debug("Speedtest interface: %s\n", nust_dev->name); + + if (!tx_timer_flag) { + nss_udp_st_hrtimer_init(); + hrtimer_start(&tx_hr_timer, kt, HRTIMER_MODE_ABS_HARD); + tx_timer_flag = 1; + } else { + hrtimer_restart(&tx_hr_timer); + } + + return true; +} diff --git a/qca/nss-udp-st-drv/src/nss_udp_st_tx.h b/qca/nss-udp-st-drv/src/nss_udp_st_tx.h new file mode 100644 index 0000000..09fab87 --- /dev/null +++ b/qca/nss-udp-st-drv/src/nss_udp_st_tx.h @@ -0,0 +1,32 @@ +/* + ************************************************************************** + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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_UDP_ST_TX_H +#define __NSS_UDP_ST_TX_H + +#define NSS_UDP_ST_TX_DEFAULT_TIMEOUT 50 /* Default Tx test duration*/ +#define NSS_UDP_ST_TX_TIMER 100 /* To caluculate pkt per 10 ms*/ +#define NSS_UDP_ST_MIN_HEADROOM 32 /* Min headroom needed */ +#define NSS_UDP_ST_MIN_TAILROOM 32 /* Min tailroom needed */ + +bool nss_udp_st_tx_valid(void); + +bool nss_udp_st_tx(void); + +void nss_udp_st_hrtimer_cleanup(void); + +#endif /*NSS_UDP_ST_TX_H*/ diff --git a/qca/nss-userspace-oss/nss-udp-st/Makefile b/qca/nss-userspace-oss/nss-udp-st/Makefile new file mode 100755 index 0000000..c216eb8 --- /dev/null +++ b/qca/nss-userspace-oss/nss-udp-st/Makefile @@ -0,0 +1,35 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=nss-udp-st +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/nss-udp-st + SECTION:=utils + CATEGORY:=Utilities + URL:=http://www.qualcomm.com + TITLE:=NSS UDP SpeedTest + DEPENDS:=@TARGET_qualcommax +kmod-nss-udp-st-drv +endef + +define Package/nss-udp-st/description/Default + A userspace utility for host data path nss udp speedtest +endef + +TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/nss-udp-st-drv/ + +define Build/Compile +$(MAKE) -C $(PKG_BUILD_DIR) \ + CC="$(TARGET_CC)" \ + CFLAGS="$(TARGET_CFLAGS)" \ + LDFLAGS="$(TARGET_LDFLAGS)" +endef + +define Package/nss-udp-st/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/nss-udp-st $(1)/usr/sbin/ +endef + +$(eval $(call BuildPackage,nss-udp-st)) diff --git a/qca/nss-userspace-oss/nss-udp-st/src/Makefile b/qca/nss-userspace-oss/nss-udp-st/src/Makefile new file mode 100644 index 0000000..182b6ed --- /dev/null +++ b/qca/nss-userspace-oss/nss-udp-st/src/Makefile @@ -0,0 +1,8 @@ +#Sources to compile +CSRCS := nss-udp-st.c + +all: + $(CC) $(CFLAGS) $(LDFLAGS) -g3 -Wall -Werror -pie -fPIC \ + $(CSRCS) -o nss-udp-st +clean: + rm -f nss-udp-st diff --git a/qca/nss-userspace-oss/nss-udp-st/src/nss-udp-st.c b/qca/nss-userspace-oss/nss-udp-st/src/nss-udp-st.c new file mode 100644 index 0000000..ae17e08 --- /dev/null +++ b/qca/nss-userspace-oss/nss-udp-st/src/nss-udp-st.c @@ -0,0 +1,461 @@ +/* + ************************************************************************** + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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 NSS UDP Speetest App + */ + +#include +#include +#include +#include "nss-udp-st.h" + +struct nss_udp_st_param st_param; +struct nss_udp_st_opt st_opt; +struct nss_udp_st_cfg st_cfg; +struct nss_udp_st_stat st_stat; + +struct option long_options[] = +{ + {"mode", required_argument, NULL, 'm'}, + {"type", required_argument, NULL, 'x'}, + {"sip", required_argument, NULL, 's'}, + {"dip", required_argument, NULL, 'd'}, + {"sport", required_argument, NULL, 'y'}, + {"dport", required_argument, NULL, 'z'}, + {"net_dev", required_argument, NULL, 'n'}, + {"version", required_argument, NULL, 'f'}, + {"time", required_argument, NULL, 't'}, + {"rate", required_argument, NULL, 'r'}, + {"buffer_sz", required_argument, NULL, 'b'}, + {"dscp", required_argument, NULL, 'c'}, + {"help", no_argument, NULL, 'h'}, + {0, 0, 0, 0} +}; + +/* + * nss_udp_st_init() + * Load nss speedtest driver and initialize test parameters + */ +static int nss_udp_st_init(void) +{ + int ret = 0; + + mkdir(NSS_UDP_ST_LOG, 0777); + ret = ioctl(st_cfg.handle, NSS_UDP_ST_IOCTL_INIT, &st_param); + if (ret < 0) { + printf("ioctl error %d\n", ret); + return -EINVAL; + } + + return 0; +} + +/* + * nss_udp_st_final() + * Unload nss speedtest driver + */ +static void nss_udp_st_final(void) +{ + system("rmmod nss-udp-st.ko"); +} + +/* + * nss_udp_st_create() + * Configure NSS UDP speedtest rules + */ +static int nss_udp_st_create(void) +{ + FILE *fp = NULL; + + fp = fopen(NSS_UDP_ST_RULES, "a+"); + if (!fp) { + printf("create/open file error\n"); + return -EINVAL; + } + + fprintf(fp, "sip=<%s> dip=<%s> sport=<%d> dport=<%d> version=<%d>\n", + st_opt.sip, st_opt.dip, st_opt.sport, st_opt.dport, st_opt.ip_version); + + fclose(fp); + return 0; +} + +/* + * nss_udp_st_list() + * List NSS UDP speedtest rules + */ +static int nss_udp_st_list(void) +{ + FILE *fp = NULL; + char *buffer = NULL; + size_t size = 0; + + fp = fopen(NSS_UDP_ST_RULES, "r"); + if (!fp) { + printf("create/open file error\n"); + return -EINVAL; + } + + while ((getline(&buffer, &size, fp)) != -1) { + printf("\n%s", buffer); + } + + free(buffer); + fclose(fp); + return 0; +} + +/* + * nss_udp_st_clear() + * Clear NSS UDP speedtest rules + */ +static int nss_udp_st_clear(void) +{ + FILE *fp = NULL; + + fp = fopen(NSS_UDP_ST_RULES, "w"); + if (!fp) { + printf("create/open file error\n"); + return -EINVAL; + } + + fclose(fp); + return 0; +} + +/* + * nss_udp_st_push_rules() + * Push NSS UDP speedtest rules to driver + */ +static int nss_udp_st_push_rules(void) +{ + FILE *fp = NULL; + char *buffer = NULL; + char *token = NULL; + char *saveptr = NULL; + size_t size = 0; + int count = 0; + int ret = 0; + bool flag = true; + + fp = fopen(NSS_UDP_ST_RULES, "r"); + if (!fp) { + printf("create/open file error\n"); + return -EINVAL; + } + + while ((getline(&buffer, &size, fp)) != -1) { + flag = true; + count = 0; + token=strtok_r(buffer, "<>", &saveptr); + memset(&st_opt, 0, sizeof(st_opt)); + while(token != NULL) { + flag = !flag; + if (flag) { + if (count == NSS_UDP_ST_SIP) + strlcpy(st_opt.sip, token, sizeof(st_opt.sip)); + if (count == NSS_UDP_ST_DIP) + strlcpy(st_opt.dip, token, sizeof(st_opt.dip)); + if (count == NSS_UDP_ST_SPORT) + st_opt.sport=atoi(token); + if (count == NSS_UDP_ST_DPORT) + st_opt.dport=atoi(token); + if (count == NSS_UDP_ST_FLAGS) + st_opt.ip_version=atoi(token); + + count ++; + } + token = strtok_r(NULL, "<>", &saveptr); + } + + ret = write(st_cfg.handle, &st_opt, sizeof(st_opt)); + if (ret < 0) { + printf("invalid rule config option\n"); + printf("write error %d\n",ret); + free(buffer); + fclose(fp); + return -EINVAL; + } + } + + free(buffer); + fclose(fp); + return 0; +} + +/* + * nss_udp_st_start() + * Send ioctl to start NSS UDP speedtest + */ +static int nss_udp_st_start(void) +{ + int ret = nss_udp_st_push_rules(); + if (ret) { + printf("invalid rule config option\n"); + return -EINVAL; + } + + if (st_cfg.type == NSS_UDP_ST_TX) { + ret = ioctl(st_cfg.handle, NSS_UDP_ST_IOCTL_START_TX, &st_cfg.time); + } else if (st_cfg.type == NSS_UDP_ST_RX) { + ret = ioctl(st_cfg.handle, NSS_UDP_ST_IOCTL_START_RX); + } else { + printf("invalid type option\n"); + return -EINVAL; + } + + if (ret < 0) { + printf("ioctl error %d\n", ret); + return -EINVAL; + } + + return 0; +} + +/* + * nss_udp_st_stop() + * Send ioctl to stop NSS USP speedtest + */ +static int nss_udp_st_stop(void) +{ + int ret = ioctl(st_cfg.handle, NSS_UDP_ST_IOCTL_STOP); + if (ret < 0) { + printf("ioctl error %d\n", ret); + return -EINVAL; + } + + return 0; +} + +/* + * nss_udp_st_stats() + * Read NSS UDP speedtest results + */ +static int nss_udp_st_stats(void) +{ + int ret = 0; + FILE *fp = NULL; + long long bytes = 0; + + ret = read(st_cfg.handle, &st_stat, sizeof(st_stat)); + if (ret < 0) { + printf("read error %d\n",ret); + return -EINVAL; + } + + if (st_cfg.type == NSS_UDP_ST_TX) { + fp = fopen(NSS_UDP_ST_TX_STATS, "w"); + if (!fp) { + printf("create/open file error\n"); + return -EINVAL; + } + + fprintf(fp, "\nPacket Stats\n"); + fprintf(fp, "\ttx_packets = %lld packets\n",st_stat.p_stats.tx_packets); + fprintf(fp, "\ttx_bytes = %lld bytes\n",st_stat.p_stats.tx_bytes); + bytes = st_stat.p_stats.tx_bytes; + } else if (st_cfg.type == NSS_UDP_ST_RX) { + fp = fopen(NSS_UDP_ST_RX_STATS, "w"); + if (!fp) { + printf("create/open file error\n"); + return -EINVAL; + } + + fprintf(fp, "\nPacket Stats\n\n"); + fprintf(fp, "\trx_packets = %lld packets\n",st_stat.p_stats.rx_packets); + fprintf(fp, "\trx_bytes = %lld bytes\n",st_stat.p_stats.rx_bytes); + bytes = st_stat.p_stats.rx_bytes; + } else { + printf("type error\n"); + return -EINVAL; + } + + fprintf(fp, "\nTime Stats\n"); + fprintf(fp, "\tstart time = %lld ms\n",st_stat.timer_stats[NSS_UDP_ST_STATS_TIME_START]); + fprintf(fp, "\tcapture time = %lld ms\n", + st_stat.timer_stats[NSS_UDP_ST_STATS_TIME_CURRENT]); + fprintf(fp, "\telapsed time = %lld ms\n", + st_stat.timer_stats[NSS_UDP_ST_STATS_TIME_ELAPSED]); + + fprintf(fp, "\nError Stats\n"); + fprintf(fp, "\tincorrect rate = %lld\n", + st_stat.errors[NSS_UDP_ST_ERROR_INCORRECT_RATE]); + fprintf(fp, "\tincorrect buffer size = %lld\n", + st_stat.errors[NSS_UDP_ST_ERROR_INCORRECT_BUFFER_SIZE]); + fprintf(fp, "\tmemory failure = %lld\n", + st_stat.errors[NSS_UDP_ST_ERROR_MEMORY_FAILURE]); + fprintf(fp, "\tpacket drop count = %lld\n", + st_stat.errors[NSS_UDP_ST_ERROR_PACKET_DROP]); + fprintf(fp, "\tincorrect ip version = %lld\n", + st_stat.errors[NSS_UDP_ST_ERROR_INCORRECT_IP_VERSION]); + + fprintf(fp, "\nThroughput Stats\n"); + fprintf(fp, "\tthroughput = %lld Mbps\n", + (bytes * 8)/(st_stat.timer_stats[NSS_UDP_ST_STATS_TIME_ELAPSED] * 1000)); + fclose(fp); + return 0; +} + +/* + * nss_udp_st_usage() + * Usage for command line arguments + */ +static void nss_udp_st_usage(void) +{ + printf("\nUsage:"); + printf("\n./nss_udp_st --mode --rate \ + --buffer_sz --dscp --net_dev "); + printf("\n./nss_udp_st --mode --sip --dip \ + --sport --dport --version <4/6>"); + printf("\n./nss_udp_st --mode --type --time