Project import generated by Copybara. GitOrigin-RevId: 4cecb617b4de4f4356c9331aaa68740a89e65a05
diff --git a/build_scripts/build_all.sh b/build_scripts/build_all.sh index 95d06f7..111d8c1 100755 --- a/build_scripts/build_all.sh +++ b/build_scripts/build_all.sh
@@ -127,10 +127,6 @@ ./build.sh ${product} ${eureka_src_path} popd - pushd ${top_dir}/sdk/nat46 - ./build.sh ${product} ${eureka_src_path} - popd - pushd ${top_dir}/sdk/qca-nss-sfe ./build.sh ${product} ${eureka_src_path} popd
diff --git a/build_scripts/release_oss.sh b/build_scripts/release_oss.sh index ffefc33..732d06e 100755 --- a/build_scripts/release_oss.sh +++ b/build_scripts/release_oss.sh
@@ -18,9 +18,9 @@ rsync -av ${src}/ ${dst} --exclude .git } -# Release source code under ./u-boot +# Release source code under ./bootloader function release_bootloader() { - src=$1/u-boot + src=$1/bootloader dst=$2/u-boot echo "Copying bootloader from $src ==> $dst..."
diff --git a/build_scripts/setup_env.sh b/build_scripts/setup_env.sh index b223268..a658366 100644 --- a/build_scripts/setup_env.sh +++ b/build_scripts/setup_env.sh
@@ -25,7 +25,7 @@ if [ ! -d "${TOP_DIR}" ]; then TOP_DIR="$(readlink -e $(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../..)" fi -ENABLE_64BIT_BUILD=${ENABLE_64BIT_BUILD:-"true"} +ENABLE_64BIT_BUILD=${ENABLE_64BIT_BUILD:-"false"} _toolchain_dir=$(readlink -e ${TOP_DIR}/prebuilt/toolchain) _num_jobs=$(grep -c processor /proc/cpuinfo)
diff --git a/qca-nss-dp/Makefile b/qca-nss-dp/Makefile index 3b00d77..157342b 100644 --- a/qca-nss-dp/Makefile +++ b/qca-nss-dp/Makefile
@@ -44,7 +44,7 @@ hal/dp_ops/syn_gmac_dp/syn_dp.o \ hal/gmac_ops/syn/gmac/syn_if.o NSS_DP_INCLUDE += -I$(obj)/hal/dp_ops/syn_gmac_dp/include -ccflags-y += -DNSS_DP_IPQ50XX -DNSS_DP_ENABLE_NAPI_GRO +ccflags-y += -DNSS_DP_IPQ50XX endif ifeq ($(SoC),$(filter $(SoC),ipq95xx)) @@ -60,7 +60,7 @@ hal/gmac_ops/syn/xgmac/syn_if.o NSS_DP_INCLUDE += -I$(obj)/hal/dp_ops/edma_dp/edma_v2 NSS_DP_INCLUDE += -I$(obj)/hal/dp_ops/edma_dp/edma_v2/include -ccflags-y += -DNSS_DP_IPQ95XX -DNSS_DP_PPE_SUPPORT -DNSS_DP_ENABLE_NAPI_GRO +ccflags-y += -DNSS_DP_IPQ95XX -DNSS_DP_PPE_SUPPORT ifneq ($(CONFIG_NET_SWITCHDEV),) qca-nss-dp-objs += nss_dp_switchdev.o ccflags-y += -DNSS_DP_PPE_SWITCHDEV
diff --git a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp.c b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp.c index ba6aa57..9f7d6eb 100644 --- a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp.c +++ b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp.c
@@ -345,7 +345,7 @@ * NETDEV_TX_BUSY. Packet will be requeued or dropped by the caller. * Queue will be re-enabled from Tx Complete. */ - if (likely(!dp_global_ctx.tx_requeue_stop)) { + if (likely(!tx_requeue_stop)) { netdev_dbg(netdev, "Stopping tx queue due to lack of tx descriptors"); atomic64_inc((atomic64_t *)&tx_info->tx_stats.tx_packets_requeued); netif_stop_queue(netdev);
diff --git a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp.h b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp.h index 7c1b81b..54c9ca3 100644 --- a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp.h +++ b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp.h
@@ -41,6 +41,9 @@ #define SYN_DP_PAGE_MODE_SKB_SIZE 256 /* SKB head buffer size for page mode */ #define SYN_DP_QUEUE_INDEX 0 /* Only one Tx DMA channel 0 enabled */ +extern int tx_requeue_stop; +extern int tx_desc_threshold_size; + /* * syn_dp_info * Synopysys GMAC Dataplane information
diff --git a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c index a695cf1..529c77e 100644 --- a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c +++ b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c
@@ -381,6 +381,7 @@ struct dma_desc_rx *rx_desc_next = NULL; uint8_t *next_skb_ptr; skb_frag_t *frag = NULL; + bool is_gro_enabled = netdev->features & NETIF_F_GRO; busy = atomic_read((atomic_t *)&rx_info->busy_rx_desc_cnt); if (unlikely(!busy)) { @@ -495,11 +496,12 @@ /* * Deliver the packet to linux */ -#if defined(NSS_DP_ENABLE_NAPI_GRO) - napi_gro_receive(&rx_info->napi_rx, rx_skb); -#else - netif_receive_skb(rx_skb); -#endif + if (is_gro_enabled) { + napi_gro_receive(&rx_info->napi_rx, rx_skb); + } else { + netif_receive_skb(rx_skb); + } + goto next_desc; } @@ -542,11 +544,13 @@ prefetch(next_skb_ptr + SYN_DP_RX_SKB_CACHE_LINE1); prefetch(next_skb_ptr + SYN_DP_RX_SKB_CACHE_LINE3); } -#if defined(NSS_DP_ENABLE_NAPI_GRO) - napi_gro_receive(&rx_info->napi_rx, rx_skb); -#else - netif_receive_skb(rx_skb); -#endif + + if (is_gro_enabled) { + napi_gro_receive(&rx_info->napi_rx, rx_skb); + } else { + netif_receive_skb(rx_skb); + } + goto next_desc; } @@ -601,11 +605,12 @@ prefetch(next_skb_ptr + SYN_DP_RX_SKB_CACHE_LINE3); } -#if defined(NSS_DP_ENABLE_NAPI_GRO) - napi_gro_receive(&rx_info->napi_rx, rx_info->head); -#else - netif_receive_skb(rx_info->head); -#endif + if (is_gro_enabled) { + napi_gro_receive(&rx_info->napi_rx, rx_info->head); + } else { + netif_receive_skb(rx_info->head); + } + rx_info->head = NULL; goto next_desc; }
diff --git a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_rx.h b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_rx.h index bb6d005..8f0105e 100644 --- a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_rx.h +++ b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_rx.h
@@ -19,8 +19,8 @@ #ifndef __NSS_DP_SYN_DP_RX__ #define __NSS_DP_SYN_DP_RX__ -#define SYN_DP_NAPI_BUDGET_RX 32 -#define SYN_DP_RX_DESC_SIZE 128 /* Rx Descriptors needed in the descriptor pool/queue */ +#define SYN_DP_NAPI_BUDGET_RX 64 +#define SYN_DP_RX_DESC_SIZE 2048 /* Rx Descriptors needed in the descriptor pool/queue */ #define SYN_DP_RX_DESC_MAX_INDEX (SYN_DP_RX_DESC_SIZE - 1) /*
diff --git a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c index 0c367b0..70c395f 100644 --- a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c +++ b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c
@@ -205,6 +205,10 @@ atomic_add(desc_needed, (atomic_t *)&tx_info->busy_tx_desc_cnt); syn_resume_dma_tx(tx_info->mac_base); + if (unlikely((SYN_DP_TX_DESC_SIZE - atomic_read((atomic_t *)&tx_info->busy_tx_desc_cnt)) < tx_desc_threshold_size)) { + netif_stop_queue(tx_info->netdev); + } + return 0; } @@ -353,6 +357,10 @@ atomic_add(desc_needed, (atomic_t *)&tx_info->busy_tx_desc_cnt); syn_resume_dma_tx(tx_info->mac_base); + if (unlikely((SYN_DP_TX_DESC_SIZE - atomic_read((atomic_t *)&tx_info->busy_tx_desc_cnt)) < tx_desc_threshold_size)) { + netif_stop_queue(tx_info->netdev); + } + return 0; } @@ -474,6 +482,11 @@ * Some error happened, collect error statistics. */ syn_dp_tx_error_cnt(tx_info, status); + + /* + * Enable DMA Tx to overcome the DMA Tx stall caused due to Jabber timeout error + */ + (status & DESC_TX_TIMEOUT) ? syn_enable_dma_tx(tx_info->mac_base) : 0; } tx_info->tx_comp_idx = syn_dp_tx_inc_index(tx_info->tx_comp_idx, 1);
diff --git a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_tx.h b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_tx.h index 458fcd4..8ffbaa8 100644 --- a/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_tx.h +++ b/qca-nss-dp/hal/dp_ops/syn_gmac_dp/syn_dp_tx.h
@@ -18,11 +18,11 @@ #ifndef __NSS_DP_SYN_DP_TX__ #define __NSS_DP_SYN_DP_TX__ -#define SYN_DP_NAPI_BUDGET_TX 32 -#define SYN_DP_TX_DESC_SIZE 1024 /* Tx Descriptors needed in the descriptor pool/queue */ +#define SYN_DP_NAPI_BUDGET_TX 64 +#define SYN_DP_TX_DESC_SIZE 8192 /* Tx Descriptors needed in the descriptor pool/queue */ #define SYN_DP_TX_DESC_MAX_INDEX (SYN_DP_TX_DESC_SIZE - 1) #define SYN_DP_TX_INVALID_DESC_INDEX SYN_DP_TX_DESC_SIZE - +#define NSS_DP_TX_MAX_DESC_SIZE SYN_DP_TX_DESC_SIZE /* * syn_dp_tx_buf */
diff --git a/qca-nss-dp/include/nss_dp_dev.h b/qca-nss-dp/include/nss_dp_dev.h index 2669639..58ac33b 100644 --- a/qca-nss-dp/include/nss_dp_dev.h +++ b/qca-nss-dp/include/nss_dp_dev.h
@@ -145,7 +145,6 @@ uint32_t jumbo_mru; /* Jumbo mru value for Rx processing */ bool overwrite_mode; /* Overwrite mode for Rx processing */ bool page_mode; /* Page mode for Rx processing */ - bool tx_requeue_stop; /* Disable queue stop for Tx processing */ }; /* Global data */
diff --git a/qca-nss-dp/nss_dp_main.c b/qca-nss-dp/nss_dp_main.c index f728c93..5ae217f 100644 --- a/qca-nss-dp/nss_dp_main.c +++ b/qca-nss-dp/nss_dp_main.c
@@ -40,6 +40,11 @@ #define NSS_DP_NETDEV_TX_QUEUE_NUM NSS_DP_QUEUE_NUM #define NSS_DP_NETDEV_RX_QUEUE_NUM NSS_DP_QUEUE_NUM +/* + * Maximum number of Tx descriptors supported + */ +#define NSS_DP_TX_DESC_SIZE NSS_DP_TX_MAX_DESC_SIZE + /* ipq40xx_mdio_data */ struct ipq40xx_mdio_data { struct mii_bus *mii_bus; @@ -64,10 +69,6 @@ module_param(jumbo_mru, int, 0); MODULE_PARM_DESC(jumbo_mru, "jumbo mode"); -int tx_requeue_stop; -module_param(tx_requeue_stop, int, 0); -MODULE_PARM_DESC(tx_requeue_stop, "disable tx requeue function"); - int nss_dp_rx_napi_budget = NSS_DP_HAL_RX_NAPI_BUDGET; module_param(nss_dp_rx_napi_budget, int, S_IRUGO); MODULE_PARM_DESC(nss_dp_rx_napi_budget, "Rx NAPI budget"); @@ -107,6 +108,126 @@ #endif /* + * Sysctl table + */ +struct ctl_table_header *nss_dp_ctl_table_header = NULL; + +int tx_requeue_stop; +int tx_desc_threshold_size = 0; + +/* + * nss_dp_tx_requeue_stop() + * Tx requeue stop sysctl handler + */ +static int nss_dp_tx_requeue_stop(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + + int current_value = tx_requeue_stop; + + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + + if (!write) { + return ret; + } + + if (ret) { + pr_err("Errno: -%d.\n", ret); + return ret; + } + + /* + * Check if tx_requeue_stop is holding a valid value + */ + if ((tx_requeue_stop != 1) && (tx_requeue_stop != 0)) { + pr_err(" Invalid input. Valid values are 0/1\n"); + tx_requeue_stop = current_value; + return -EINVAL; + } + + return ret; +} + +/* + * nss_dp_tx_desc_threshold_set() + * Set the minimum threshold limit for Tx descriptor size + */ +static int nss_dp_tx_desc_threshold_set(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + + int current_value = tx_desc_threshold_size; + + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + + if (!write) { + return ret; + } + + if (ret) { + pr_err("Errno: -%d.\n", ret); + return ret; + } + + /* + * Check if tx_desc_threshold_size is holding a valid value + */ + if ((tx_desc_threshold_size >= NSS_DP_TX_DESC_SIZE) || (tx_desc_threshold_size < 0)) { + pr_err("Invalid input. Value should be between 0 and %d, current value: %d\n", SYN_DP_TX_DESC_SIZE - 1, current_value); + tx_desc_threshold_size = current_value; + return -EINVAL; + } + + return ret; +} + +/* nss_dp_table + * Sysctl entries which are part of nss dp + */ +static struct ctl_table nss_dp_table[] = { + { + .procname = "tx_requeue_stop", + .data = &tx_requeue_stop, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &nss_dp_tx_requeue_stop, + }, + { + .procname = "tx_desc_threshold", + .data = &tx_desc_threshold_size, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &nss_dp_tx_desc_threshold_set, + }, + { } +}; + +/* nss_dp_root_dir + * Root directory for nss dp using sysctl + */ +static struct ctl_table nss_dp_root_dir[] = { + { + .procname = "nss_dp_tx", + .mode = 0666, + .child = nss_dp_table, + }, + { } +}; + +/* + * nss_dp_root + * Root for networking parameters using sysctl + */ +static struct ctl_table nss_dp_root[] = { + { + .procname = "net", + .mode = 0666, + .child = nss_dp_root_dir, + }, + {} +}; + +/* * nss_dp_do_ioctl() */ static int32_t nss_dp_do_ioctl(struct net_device *netdev, struct ifreq *ifr, @@ -1006,13 +1127,14 @@ dp_global_ctx.rx_buf_size = NSS_DP_RX_BUFFER_SIZE; /* + * By default, disabling the requeue functionality + */ + tx_requeue_stop = 1; + + /* * Get the module params. * We do not support page_mode or jumbo_mru on low memory profiles. */ - dp_global_ctx.tx_requeue_stop = false; - if (tx_requeue_stop != 0) { - dp_global_ctx.tx_requeue_stop = true; - } #if !defined(NSS_DP_MEM_PROFILE_LOW) && !defined(NSS_DP_MEM_PROFILE_MEDIUM) dp_global_ctx.overwrite_mode = overwrite_mode; dp_global_ctx.page_mode = page_mode; @@ -1032,6 +1154,13 @@ return -EFAULT; } + /* + * Register sysctl table + */ + if (!nss_dp_ctl_table_header) { + nss_dp_ctl_table_header = register_sysctl_table(nss_dp_root); + } + ret = platform_driver_register(&nss_dp_drv); if (ret) pr_info("NSS DP platform drv register failed\n"); @@ -1057,6 +1186,13 @@ dp_global_ctx.common_init_done = false; } + /* + * Unregister sysctl table + */ + if (nss_dp_ctl_table_header) { + unregister_sysctl_table(nss_dp_ctl_table_header); + } + platform_driver_unregister(&nss_dp_drv); }
diff --git a/qca-nss-ecm/Makefile b/qca-nss-ecm/Makefile index b34e832..debcc10 100644 --- a/qca-nss-ecm/Makefile +++ b/qca-nss-ecm/Makefile
@@ -19,6 +19,9 @@ # Makefile for the QCA NSS ECM # ################################################### +ifeq ($(ECM_FRONT_END_SFE_ENABLE), y) +obj-m += examples/ecm_sfe_l2.o +endif obj-m +=examples/ecm_ae_select.o obj-m += ecm.o @@ -114,6 +117,7 @@ # Define ECM_INTERFACE_PPPOE_ENABLE=y in order # to enable support for PPPoE acceleration. # ############################################################################# +ECM_INTERFACE_PPPOE_ENABLE=y ccflags-$(ECM_INTERFACE_PPPOE_ENABLE) += -DECM_INTERFACE_PPPOE_ENABLE # #############################################################################
diff --git a/qca-nss-ecm/build.sh b/qca-nss-ecm/build.sh index 0c67b1b..452d82c 100755 --- a/qca-nss-ecm/build.sh +++ b/qca-nss-ecm/build.sh
@@ -20,9 +20,8 @@ kernel_path=$(readlink -e ${sdk_top_dir}/../kernel) qca_sfe_path=$(readlink -e ${sdk_top_dir}/qca-nss-sfe/) -nat46_path=$(readlink -e ${sdk_top_dir}/nat46/nat46/modules) soc_type=ipq50xx -extra_cflags="-I${qca_sfe_path}/exports -I${nat46_path}" +extra_cflags="-I${qca_sfe_path}/exports" build_flags="ECM_CLASSIFIER_HYFI_ENABLE=n ECM_MULTICAST_ENABLE=n ECM_INTERFACE_IPSEC_ENABLE=n ECM_INTERFACE_PPTP_ENABLE=n ECM_INTERFACE_L2TPV2_ENABLE=n ECM_INTERFACE_GRE_TAP_ENABLE=n ECM_INTERFACE_GRE_TUN_ENABLE=n ECM_INTERFACE_SIT_ENABLE=n ECM_INTERFACE_TUNIPIP6_ENABLE=n ECM_INTERFACE_RAWIP_ENABLE=n ECM_INTERFACE_BOND_ENABLE=n ECM_XFRM_ENABLE=n ECM_FRONT_END_SFE_ENABLE=y ECM_NON_PORTED_SUPPORT_ENABLE=n ECM_INTERFACE_MAP_T_ENABLE=n ECM_INTERFACE_VXLAN_ENABLE=n ECM_INTERFACE_OVS_BRIDGE_ENABLE=n ECM_CLASSIFIER_OVS_ENABLE=n ECM_CLASSIFIER_DSCP_IGS=n ECM_IPV6_ENABLE=y ECM_FRONT_END_NSS_ENABLE=n EXAMPLES_BUILD_OVS=n" @@ -33,7 +32,7 @@ # make kernel module echo "Build ${MODULE_NAME}" ${CROSS_MAKE} -C ${kernel_path} M=${sdk_top_dir}/${MODULE_NAME} ${build_flags} SoC=${soc_type} EXTRA_CFLAGS="${extra_cflags}" \ - KBUILD_EXTRA_SYMBOLS="${qca_sfe_path}/Module.symvers ${nat46_path}/Module.symvers" V=1 + KBUILD_EXTRA_SYMBOLS="${qca_sfe_path}/Module.symvers" V=1 } ################################################## @@ -63,6 +62,7 @@ local module_target_dir="$(GetModulePath ${eureka_src_path} ${product})" mkdir -p ${module_target_dir} cp -f ecm.ko ${module_target_dir}/${MODULE_NAME}.ko + cp -f examples/ecm_sfe_l2.ko ${module_target_dir} } function Usage() {
diff --git a/qca-nss-ecm/ecm_db/ecm_db_connection.c b/qca-nss-ecm/ecm_db/ecm_db_connection.c index 869e35d..fd68f23 100644 --- a/qca-nss-ecm/ecm_db/ecm_db_connection.c +++ b/qca-nss-ecm/ecm_db/ecm_db_connection.c
@@ -279,20 +279,19 @@ DEBUG_INFO("%px: defunct timer expired\n", ci); - /* - * If defunct fails, return. Do not remove the last ref count. This failure means - * it will be re-tried later with the ecm_db_connection_make_defunct function - * until the total failure count reaches to the max limit which is 250. - * When the limit is reached, defunct process will return true and let - * the connection goes off. - */ ret = ci->defunct(ci->feci, &accel_mode); /* - * Release the last reference of this connection. This reference is the one - * which was held when the connection was allocated. + * If the returned 'ret' is success, this means this callback succeeded to + * defunct the connection and it can release the last reference. + * If it fails, this means that another defunct process defuncted the connection + * before this callback. In that case, we will check the accel_mode of the connection. + * If the other call defuncted the connection successfully, it will set the accel_mode to + * ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT for s short amount of time to avoid + * further accel/decel attempts. So, in this accel_mode, this callback shouldn't release the + * last reference. It will be released by the ecm_db_connection_make_defunct() function. */ - if (ret || ECM_FRONT_END_ACCELERATION_FAILED(accel_mode)) { + if (ret || (ECM_FRONT_END_ACCELERATION_FAILED(accel_mode) && (accel_mode != ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT))) { ecm_db_connection_deref(ci); } }
diff --git a/qca-nss-ecm/ecm_db/ecm_db_node.c b/qca-nss-ecm/ecm_db/ecm_db_node.c index c3d70be..18d6aba 100644 --- a/qca-nss-ecm/ecm_db/ecm_db_node.c +++ b/qca-nss-ecm/ecm_db/ecm_db_node.c
@@ -1,9 +1,12 @@ /* ************************************************************************** * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved. + * 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 @@ -712,6 +715,17 @@ EXPORT_SYMBOL(ecm_db_node_hash_index_get_first); /* + * ecm_db_node_get_connections_count() + * Returns the connections count on the node in the given direction. + */ +int ecm_db_node_get_connections_count(struct ecm_db_node_instance *ni, ecm_db_obj_dir_t dir) +{ + DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%px: magic failed\n", ni); + + return ni->connections_count[dir]; +} + +/* * ecm_db_node_alloc() * Allocate a node instance */
diff --git a/qca-nss-ecm/ecm_db/ecm_db_node.h b/qca-nss-ecm/ecm_db/ecm_db_node.h index a2c0e03..1661ccd 100644 --- a/qca-nss-ecm/ecm_db/ecm_db_node.h +++ b/qca-nss-ecm/ecm_db/ecm_db_node.h
@@ -1,9 +1,12 @@ /* ************************************************************************** * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. + * 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 @@ -125,6 +128,8 @@ int ecm_db_node_hash_index_get_first(void); #endif +int ecm_db_node_get_connections_count(struct ecm_db_node_instance *ni, ecm_db_obj_dir_t dir); + void ecm_db_node_ovs_connections_masked_defunct(int ip_ver, uint8_t *src_mac, bool src_mac_check, ip_addr_t src_addr_mask, uint16_t src_port_mask, uint8_t *dest_mac, bool dest_mac_check, ip_addr_t dest_addr_mask, uint16_t dest_port_mask,
diff --git a/qca-nss-ecm/ecm_interface.c b/qca-nss-ecm/ecm_interface.c index b46233b..52bd11b 100644 --- a/qca-nss-ecm/ecm_interface.c +++ b/qca-nss-ecm/ecm_interface.c
@@ -6424,15 +6424,12 @@ */ if ((is_ported || ecm_db_connection_is_pppoe_bridged_get(ci)) && is_valid_ether_addr(mac_addr) && ecm_front_end_is_bridge_port(dev) && rx_packets) { - DEBUG_TRACE("Update bridge fdb entry for mac: %pM\n", mac_addr); + DEBUG_TRACE("Update bridge fdb entry for mac: %pM\n", mac_addr); /* - * Update fdb entry only if it exist. Please note that br_refresh_fdb_entry() API - * creates new fdb entry if it does not exist. + * Update the existing fdb entry's timestamp only. */ - if (br_fdb_has_entry(dev, mac_addr, 0)) { - br_refresh_fdb_entry(dev, mac_addr); - } + br_fdb_entry_refresh(dev, mac_addr, 0); } } @@ -7020,7 +7017,12 @@ * FROM_NAT and TO_NAT have the same list of connections. */ for (dir = 0; dir <= ECM_DB_OBJ_DIR_TO; dir++) { - ecm_db_traverse_node_connection_list_and_defunct(ni, dir, ip_version); + /* + * If there is connection on this node, call the defunct function. + */ + if (ecm_db_node_get_connections_count(ni, dir)) { + ecm_db_traverse_node_connection_list_and_defunct(ni, dir, ip_version); + } } }
diff --git a/qca-nss-ecm/ecm_types.h b/qca-nss-ecm/ecm_types.h index 6ec2fec..3777688 100644 --- a/qca-nss-ecm/ecm_types.h +++ b/qca-nss-ecm/ecm_types.h
@@ -192,13 +192,13 @@ #define ECM_LINUX6_TO_IP_ADDR(d,s) \ { \ ecm_type_check_ecm_ip_addr(d); \ - ecm_type_check_ae_ipv6(&s); \ + ecm_type_check_ae_ipv6(s); \ __ECM_IP_ADDR_COPY_NO_CHECK(d,s); \ } #define ECM_IP_ADDR_TO_LINUX6(d,s) \ { \ - ecm_type_check_ae_ipv6(&d); \ + ecm_type_check_ae_ipv6(d); \ ecm_type_check_ecm_ip_addr(s); \ __ECM_IP_ADDR_COPY_NO_CHECK(d,s); \ }
diff --git a/qca-nss-ecm/examples/ecm_sfe_l2.c b/qca-nss-ecm/examples/ecm_sfe_l2.c new file mode 100644 index 0000000..dd64653 --- /dev/null +++ b/qca-nss-ecm/examples/ecm_sfe_l2.c
@@ -0,0 +1,1085 @@ +/* + ************************************************************************** + * 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 <linux/module.h> + +#include <linux/version.h> +#include <linux/types.h> +#include <linux/debugfs.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/etherdevice.h> +#include <linux/inet.h> + +#include "exports/ecm_sfe_common_public.h" + +/* + * Global WAN interface name parameter. + */ +char wan_name[IFNAMSIZ]; +int wan_name_len; + +/* + * DebugFS entry object. + */ +static struct dentry *ecm_sfe_l2_dentry; + +/* + * Policy rule directions. + */ +enum ecm_sfe_l2_policy_rule_dir { + ECM_SFE_L2_POLICY_RULE_EGRESS = 1, + ECM_SFE_L2_POLICY_RULE_INGRESS, + ECM_SFE_L2_POLICY_RULE_EGRESS_INGRESS, +}; + +/* + * Policy rule commands. + */ +enum ecm_sfe_l2_policy_rule_cmd { + ECM_SFE_L2_POLICY_RULE_ADD = 1, + ECM_SFE_L2_POLICY_RULE_DEL, + ECM_SFE_L2_POLICY_RULE_FLUSH_ALL +}; + +/* + * ECM tuple directions. + */ +enum ecm_sfe_l2_tuple_dir { + ECM_SFE_L2_TUPLE_DIR_ORIGINAL, + ECM_SFE_L2_TUPLE_DIR_REPLY, +}; + +/* + * Defunct by 5-tuple command option types. + */ +enum ecm_sfe_l2_defunct_by_5tuple_options { + ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_IP_VERSION, + ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_SIP, + ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_SPORT, + ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_DIP, + ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_DPORT, + ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_PROTOCOL, + ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_MAX, +}; + +/* + * Policy rule structure + */ +struct ecm_sfe_l2_policy_rule { + struct list_head list; + int protocol; + int src_port; + int dest_port; + uint32_t src_addr[4]; + uint32_t dest_addr[4]; + int ip_ver; + enum ecm_sfe_l2_policy_rule_dir direction; +}; + +LIST_HEAD(ecm_sfe_l2_policy_rules); +DEFINE_SPINLOCK(ecm_sfe_l2_policy_rules_lock); + +/* + * ecm_sfe_l2_policy_rule_find() + * Finds a policy rule with the given parameters. + */ +static struct ecm_sfe_l2_policy_rule *ecm_sfe_l2_policy_rule_find(int ip_ver, uint32_t *sip_addr, int sport, + uint32_t *dip_addr, int dport, + int protocol) +{ + struct ecm_sfe_l2_policy_rule *rule = NULL; + + list_for_each_entry(rule , &ecm_sfe_l2_policy_rules, list) { + if (rule->ip_ver != ip_ver) + continue; + + if (rule->protocol && (rule->protocol != protocol)) + continue; + + if (rule->dest_port && (rule->dest_port != dport)) + continue; + + if (rule->src_port && (rule->src_port != sport)) + continue; + + if (ip_ver == 4) { + if (rule->dest_addr[0] && (rule->dest_addr[0] != dip_addr[0])) + continue; + } else { + if (rule->dest_addr[0] && memcmp(rule->dest_addr, dip_addr, sizeof(uint32_t) * 4)) + continue; + } + + if (ip_ver == 4) { + if (rule->src_addr[0] && (rule->src_addr[0] != sip_addr[0])) + continue; + } else { + if (rule->src_addr[0] && memcmp(rule->src_addr, sip_addr, sizeof(uint32_t) * 4)) + continue; + } + + return rule; + } + return NULL; +} + +/* + * ecm_sfe_l2_connection_check_with_policy_rules() + * Checks the ECM tuple with the policy rules in our rules list and + * set the L2 acceleration accordingly, if there is a match. + */ +static uint32_t ecm_sfe_l2_connection_check_with_policy_rules(struct ecm_sfe_common_tuple *tuple, enum ecm_sfe_l2_tuple_dir tuple_dir) +{ + struct ecm_sfe_l2_policy_rule *rule = NULL; + enum ecm_sfe_l2_policy_rule_dir direction; + uint32_t l2_accel_bits = (ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED | ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED); + + if (tuple_dir == ECM_SFE_L2_TUPLE_DIR_ORIGINAL) { + spin_lock_bh(&ecm_sfe_l2_policy_rules_lock); + rule = ecm_sfe_l2_policy_rule_find(tuple->ip_ver, tuple->src_addr, tuple->src_port, + tuple->dest_addr, tuple->dest_port, tuple->protocol); + if (!rule) { + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + pr_warn("No rule with this tuple\n"); + goto done; + } + direction = rule->direction; + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + + if (direction == ECM_SFE_L2_POLICY_RULE_EGRESS) { + pr_debug("flow side should be L3 interface\n"); + l2_accel_bits &= ~ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED; + } else if (direction == ECM_SFE_L2_POLICY_RULE_INGRESS) { + pr_debug("return side should be L3 interface\n"); + l2_accel_bits &= ~ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED; + } + } else if (tuple_dir == ECM_SFE_L2_TUPLE_DIR_REPLY) { + spin_lock_bh(&ecm_sfe_l2_policy_rules_lock); + rule = ecm_sfe_l2_policy_rule_find(tuple->ip_ver, tuple->dest_addr, tuple->dest_port, + tuple->src_addr, tuple->src_port, tuple->protocol); + + if (!rule) { + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + pr_warn("No rule with this tuple\n"); + goto done; + } + direction = rule->direction; + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + + if (direction == ECM_SFE_L2_POLICY_RULE_EGRESS) { + pr_debug("return side should be L3 interface\n"); + l2_accel_bits &= ~ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED; + } else if (direction == ECM_SFE_L2_POLICY_RULE_INGRESS) { + pr_debug("flow side should be L3 interface\n"); + l2_accel_bits &= ~ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED; + } + } else { + pr_err("unknow tuple_dir: %d\n", tuple_dir); + goto done; + } + + if (direction == ECM_SFE_L2_POLICY_RULE_EGRESS_INGRESS) { + pr_debug("both sides should be L3 interface\n"); + l2_accel_bits &= ~ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED; + l2_accel_bits &= ~ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED; + } +done: + return l2_accel_bits; +} + +/* + * ecm_sfe_l2_accel_check_callback() + * L2 acceleration check function callback. + */ +uint32_t ecm_sfe_l2_accel_check_callback(struct ecm_sfe_common_tuple *tuple) +{ + struct net_device *flow_dev; + struct net_device *return_dev; + struct net_device *wan_dev; + uint32_t l2_accel_bits = (ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED | ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED); + + if (strlen(wan_name) == 0) { + pr_debug("WAN interface is not set in the debugfs\n"); + goto done; + } + + wan_dev = dev_get_by_name(&init_net, wan_name); + if (!wan_dev) { + pr_debug("WAN interface: %s couldn't be found\n", wan_name); + goto done; + } + + flow_dev = dev_get_by_index(&init_net, tuple->src_ifindex); + if (!flow_dev) { + pr_debug("flow netdevice couldn't be found with index: %d\n", tuple->src_ifindex); + dev_put(wan_dev); + goto done; + } + + return_dev = dev_get_by_index(&init_net, tuple->dest_ifindex); + if (!return_dev) { + pr_debug("return netdevice couldn't be found with index: %d\n", tuple->dest_ifindex); + dev_put(wan_dev); + dev_put(flow_dev); + goto done; + } + + if (wan_dev == return_dev) { + /* + * Check the tuple with the policy rules in the ORIGINAL direction of the tuple. + */ + l2_accel_bits = ecm_sfe_l2_connection_check_with_policy_rules(tuple, ECM_SFE_L2_TUPLE_DIR_ORIGINAL); + } else if (wan_dev == flow_dev) { + /* + * Check the tuple with the policy rules in the REPLY direction of the tuple. + */ + l2_accel_bits = ecm_sfe_l2_connection_check_with_policy_rules(tuple, ECM_SFE_L2_TUPLE_DIR_REPLY); + } + dev_put(wan_dev); + dev_put(flow_dev); + dev_put(return_dev); + +done: + return l2_accel_bits; +} + +/* + * ecm_sfe_l2_flush_policy_rules() + * Flushes all the policy rules. + */ +static void ecm_sfe_l2_flush_policy_rules(void) +{ + struct ecm_sfe_l2_policy_rule *rule; + struct ecm_sfe_l2_policy_rule *tmp; + + spin_lock_bh(&ecm_sfe_l2_policy_rules_lock); + list_for_each_entry_safe(rule , tmp, &ecm_sfe_l2_policy_rules, list) { + list_del(&rule->list); + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + kfree(rule); + spin_lock_bh(&ecm_sfe_l2_policy_rules_lock); + } + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); +} + +/* + * ecm_sfe_l2_delete_policy_rule() + * Deletes a policy rule with the given parameters. + */ +static bool ecm_sfe_l2_delete_policy_rule(int ip_ver, uint32_t *sip_addr, int sport, uint32_t *dip_addr, int dport, int protocol) +{ + struct ecm_sfe_l2_policy_rule *rule; + + spin_lock_bh(&ecm_sfe_l2_policy_rules_lock); + rule = ecm_sfe_l2_policy_rule_find(ip_ver, sip_addr, sport, dip_addr, dport, protocol); + if (!rule) { + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + pr_warn("rule cannot be found in the list\n"); + return false; + } + list_del(&rule->list); + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + kfree(rule); + + pr_info("rule deleted\n"); + return true; +} + +/* + * ecm_sfe_l2_add_policy_rule() + * Adds a policy rule with the given parameters. + */ +static bool ecm_sfe_l2_add_policy_rule(int ip_ver, uint32_t *sip_addr, int sport, uint32_t *dip_addr, int dport, int protocol, enum ecm_sfe_l2_policy_rule_dir direction) +{ + struct ecm_sfe_l2_policy_rule *rule; + + spin_lock_bh(&ecm_sfe_l2_policy_rules_lock); + rule = ecm_sfe_l2_policy_rule_find(ip_ver, sip_addr, sport, dip_addr, dport, protocol); + if (rule) { + if (rule->direction != direction) { + pr_info("Update direction of the rule from %d to %d\n", rule->direction, direction); + rule->direction = direction; + } + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + pr_warn("rule is already present\n"); + return true; + } + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + + rule = kzalloc(sizeof(struct ecm_sfe_l2_policy_rule), GFP_ATOMIC); + if (!rule) { + pr_warn("alloc failed for new rule\n"); + return false; + } + + rule->ip_ver = ip_ver; + rule->protocol = protocol; + rule->src_port = sport; + rule->dest_port = dport; + memcpy(rule->src_addr, sip_addr, sizeof(uint32_t) * 4); + memcpy(rule->dest_addr, dip_addr, sizeof(uint32_t) * 4); + rule->direction = direction; + + INIT_LIST_HEAD(&rule->list); + + spin_lock_bh(&ecm_sfe_l2_policy_rules_lock); + list_add(&rule->list, &ecm_sfe_l2_policy_rules); + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); + + pr_info("rule added\n"); + return true; +} + +/* + * ecm_sfe_l2_policy_rule_write() + * Adds a policy rule to the rule table. + * + * Policy rule must include cmd, ip_ver and direction. It can also include src/dest IP and ports, protocol. + * cmd and ip_ver MUST be the first 2 options in the command. + */ +static ssize_t ecm_sfe_l2_policy_rule_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *offset) +{ + char *cmd_buf; + char *fields; + char *token; + char *option, *value; + int cmd = 0; /* must be present in the rule */ + int ip_ver = 0; /* must be present in the rule */ + uint32_t sip_addr[4] = {0}; + uint32_t dip_addr[4] = {0}; + int sport = 0; + int dport = 0; + int protocol = 0; + int direction = 0; /* must be present in the rule */ + + /* + * Command is formed as: + * echo "cmd=1 ip_ver=4 dport=443 protocol=6 direction=1" > /sys/kernel/debug/ecm_sfe_l2/policy_rules + * + * cmd: 1 is to add, 2 is to delete a rule. + * direction: 1 is egress, 2 is ingress, 3 is both + */ + cmd_buf = kzalloc(count + 1, GFP_ATOMIC); + if (!cmd_buf) { + pr_warn("unable to allocate memory for cmd buffer\n"); + return -ENOMEM; + } + + count = simple_write_to_buffer(cmd_buf, count, offset, user_buf, count); + + /* + * Split the buffer into tokens + */ + fields = cmd_buf; + while ((token = strsep(&fields, " "))) { + pr_info("\ntoken: %s\n", token); + + option = strsep(&token, "="); + value = token; + + pr_info("\t\toption: %s\n", option); + pr_info("\t\tvalue: %s\n", value); + + if (!strcmp(option, "cmd")) { + if (sscanf(value, "%d", &cmd)) { + if (cmd != ECM_SFE_L2_POLICY_RULE_ADD && cmd != ECM_SFE_L2_POLICY_RULE_DEL && + cmd != ECM_SFE_L2_POLICY_RULE_FLUSH_ALL) { + pr_err("invalid cmd value: %d\n", cmd); + goto fail; + } + continue; + } + pr_warn("cannot read value\n"); + goto fail; + } + + if (!strcmp(option, "ip_ver")) { + if (sscanf(value, "%d", &ip_ver)) { + if (ip_ver != 4 && ip_ver != 6) { + pr_err("invalid ip_ver: %d\n", ip_ver); + goto fail; + } + continue; + } + pr_warn("cannot read value\n"); + goto fail; + } + + if (!strcmp(option, "protocol")) { + if (sscanf(value, "%d", &protocol)) { + continue; + } + pr_warn("cannot read value\n"); + goto fail; + } + + if (!strcmp(option, "sport")) { + if (sscanf(value, "%d", &sport)) { + continue; + } + pr_warn("cannot read value\n"); + goto fail; + } + + if (!strcmp(option, "dport")) { + if (sscanf(value, "%d", &dport)) { + continue; + } + pr_warn("cannot read value\n"); + goto fail; + } + + if (!strcmp(option, "direction")) { + if (cmd == ECM_SFE_L2_POLICY_RULE_DEL) { + pr_err("direction is not allowed in delete command\n"); + goto fail; + } + + if (sscanf(value, "%d", &direction)) { + if (direction != ECM_SFE_L2_POLICY_RULE_EGRESS + && direction != ECM_SFE_L2_POLICY_RULE_INGRESS + && direction != ECM_SFE_L2_POLICY_RULE_EGRESS_INGRESS) { + + pr_err("invalid direction: %d\n", direction); + goto fail; + } + continue; + } + pr_warn("cannot read value\n"); + goto fail; + } + + if (!strcmp(option, "sip")) { + if (ip_ver == 4) { + if (!in4_pton(value, -1, (uint8_t *)&sip_addr[0], -1, NULL)) { + pr_err("invalid source IP V4 value: %s\n", value); + goto fail; + } + } else if (ip_ver ==6) { + if (!in6_pton(value, -1, (uint8_t *)sip_addr, -1, NULL)) { + pr_err("invalid source IP V6 value: %s\n", value); + goto fail; + } + } else { + pr_err("ip_ver hasn't been set yet\n"); + goto fail; + } + continue; + } + + if (!strcmp(option, "dip")) { + if (ip_ver == 4) { + if (!in4_pton(value, -1, (uint8_t *)&dip_addr[0], -1, NULL)) { + pr_err("invalid destination IP V4 value: %s\n", value); + goto fail; + } + } else if (ip_ver == 6) { + if (!in6_pton(value, -1, (uint8_t *)dip_addr, -1, NULL)) { + pr_err("invalid destination IP V6 value: %s\n", value); + goto fail; + } + } else { + pr_err("ip_ver hasn't been set yet\n"); + goto fail; + } + continue; + } + + pr_warn("unrecognized option: %s\n", option); + goto fail; + } + + kfree(cmd_buf); + + if (cmd == ECM_SFE_L2_POLICY_RULE_ADD) { + if (!ecm_sfe_l2_add_policy_rule(ip_ver, sip_addr, sport, dip_addr, dport, protocol, direction)) { + pr_err("Add policy rule failed\n"); + return -ENOMEM; + } + } else if (cmd == ECM_SFE_L2_POLICY_RULE_DEL) { + if (!ecm_sfe_l2_delete_policy_rule(ip_ver, sip_addr, sport, dip_addr, dport, protocol)) { + pr_err("Delete policy rule failed\n"); + return -ENOMEM; + } + } else if (cmd == ECM_SFE_L2_POLICY_RULE_FLUSH_ALL) { + ecm_sfe_l2_flush_policy_rules(); + } + + return count; +fail: + kfree(cmd_buf); + return -EINVAL; +} + +/* + * ecm_sfe_l2_policy_rule_seq_show() + */ +static int ecm_sfe_l2_policy_rule_seq_show(struct seq_file *m, void *v) +{ + struct ecm_sfe_l2_policy_rule *rule; + + rule = list_entry(v, struct ecm_sfe_l2_policy_rule, list); + + if (rule->ip_ver == 4) { + seq_printf(m, "ip_ver: %d" + "\tprotocol: %d" + "\tsip_addr: %pI4" + "\tdip_addr: %pI4" + "\tsport: %d" + "\tdport: %d" + "\tdirection: %d\n", + rule->ip_ver, + rule->protocol, + &rule->src_addr[0], + &rule->dest_addr[0], + rule->src_port, + rule->dest_port, + rule->direction); + } else { + struct in6_addr saddr; + struct in6_addr daddr; + + memcpy(&saddr.s6_addr32, rule->src_addr, sizeof(uint32_t) * 4); + memcpy(&daddr.s6_addr32, rule->dest_addr, sizeof(uint32_t) * 4); + + seq_printf(m, "ip_ver: %d" + "\tprotocol: %d" + "\tsip_addr: %pI6" + "\tdip_addr: %pI6" + "\tsport: %d" + "\tdport: %d" + "\tdirection: %d\n", + rule->ip_ver, + rule->protocol, + &saddr, + &daddr, + rule->src_port, + rule->dest_port, + rule->direction); + } + + return 0; +} + +/* + * ecm_sfe_l2_policy_rule_seq_stop() + */ +static void ecm_sfe_l2_policy_rule_seq_stop(struct seq_file *p, void *v) +{ + spin_unlock_bh(&ecm_sfe_l2_policy_rules_lock); +} + +/* + * ecm_sfe_l2_policy_rule_seq_next() + */ +static void *ecm_sfe_l2_policy_rule_seq_next(struct seq_file *p, void *v, + loff_t *pos) +{ + return seq_list_next(v, &ecm_sfe_l2_policy_rules, pos); +} + +/* + * ecm_sfe_l2_policy_rule_seq_start() + */ +static void *ecm_sfe_l2_policy_rule_seq_start(struct seq_file *m, loff_t *_pos) +{ + spin_lock_bh(&ecm_sfe_l2_policy_rules_lock); + return seq_list_start(&ecm_sfe_l2_policy_rules, *_pos); +} + +static const struct seq_operations ecm_sfe_l2_policy_rule_seq_ops = { + .start = ecm_sfe_l2_policy_rule_seq_start, + .next = ecm_sfe_l2_policy_rule_seq_next, + .stop = ecm_sfe_l2_policy_rule_seq_stop, + .show = ecm_sfe_l2_policy_rule_seq_show, +}; + +/* + * ecm_sfe_l2_policy_rule_open() + */ +static int ecm_sfe_l2_policy_rule_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &ecm_sfe_l2_policy_rule_seq_ops); +} + +/* + * File operations for policy rules add/delete/list operations. + */ +static const struct file_operations ecm_sfe_l2_policy_rule_fops = { + .owner = THIS_MODULE, + .open = ecm_sfe_l2_policy_rule_open, + .write = ecm_sfe_l2_policy_rule_write, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* + * ecm_sfe_l2_defunct_by_5tuple_write() + * Writes the defunct by 5-tuple command to the debugfs node. + */ +static ssize_t ecm_sfe_l2_defunct_by_5tuple_write(struct file *f, const char *user_buf, + size_t count, loff_t *offset) +{ + int ret = -EINVAL; + char *cmd_buf; + int field_count; + char *fields_ptr; + char *fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_MAX]; + char *option, *value; + int ip_ver; + uint32_t sip_addr_v4; + uint32_t dip_addr_v4; + struct in6_addr sip_addr_v6; + struct in6_addr dip_addr_v6; + int sport, dport; + int protocol; + bool defunct_result; + + /* + * Command is formed as for IPv4 and IPv6 5-tuples as below respectively. + * + * echo "ip_ver=4 sip=192.168.1.100 sport=443 dip=192.168.2.100 dport=1000 protocol=6" > /sys/kernel/debug/ecm_sfe_l2/defunct_by_5tuple + * echo “ip_ver=6 sip=2aaa::100 sport=443 dip=3bbb::200 dport=1000 protocol=6” > /sys/kernel/debug/ecm_sfe_l2/defunct_by_5tuple + * + * The order of the options MUST be as above and it MUST contain all the 5-tuple fields and the ip_ver. + */ + cmd_buf = kzalloc(count + 1, GFP_ATOMIC); + if (!cmd_buf) { + pr_warn("unable to allocate memory for cmd buffer\n"); + return -ENOMEM; + } + + count = simple_write_to_buffer(cmd_buf, count, offset, user_buf, count); + + /* + * Split the buffer into its fields + */ + field_count = 0; + fields_ptr = cmd_buf; + fields[field_count] = strsep(&fields_ptr, " "); + while (fields[field_count] != NULL) { + pr_info("Field %d: %s\n", field_count, fields[field_count]); + field_count++; + if (field_count == ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_MAX) + break; + + fields[field_count] = strsep(&fields_ptr, " "); + } + + if (field_count != ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_MAX) { + kfree(cmd_buf); + pr_err("Invalid field count %d\n", field_count); + return -EINVAL; + } + + /* + * IP version (ip_ver) field validation. + */ + option = strsep(&fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_IP_VERSION], "="); + if (!option || strcmp(option, "ip_ver")) { + pr_err("invalid IP version option name: %s\n", option); + goto fail; + } + value = fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_IP_VERSION]; + if (!sscanf(value, "%d", &ip_ver)) { + pr_err("Unable to read IP version value %s\n", value); + goto fail; + } + if (ip_ver != 4 && ip_ver != 6) { + pr_err("invalid IP version: %d\n", ip_ver); + goto fail; + } + + /* + * Source IP (sip) field validation. + */ + option = strsep(&fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_SIP], "="); + if (!option || strcmp(option, "sip")) { + pr_err("invalid source IP option name: %s\n", option); + goto fail; + } + value = fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_SIP]; + + if (ip_ver == 4) { + if (!in4_pton(value, -1, (uint8_t *)&sip_addr_v4, -1, NULL)) { + pr_err("invalid source IP V4 value: %s\n", value); + goto fail; + } + } else { + if (!in6_pton(value, -1, (uint8_t *)sip_addr_v6.s6_addr, -1, NULL)) { + pr_err("invalid source IP V6 value: %s\n", value); + goto fail; + } + } + + /* + * Source port (sport) field validadtion. + */ + option = strsep(&fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_SPORT], "="); + if (!option || strcmp(option, "sport")) { + pr_err("invalid source port option name: %s\n", option); + goto fail; + } + value = fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_SPORT]; + if (!sscanf(value, "%d", &sport)) { + pr_err("Unable to read source port value %s\n", value); + goto fail; + } + + /* + * Destination IP (dip) field validation. + */ + option = strsep(&fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_DIP], "="); + if (!option || strcmp(option, "dip")) { + pr_err("invalid destination IP option name: %s\n", option); + goto fail; + } + value = fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_DIP]; + + if (ip_ver == 4) { + if (!in4_pton(value, -1, (uint8_t *)&dip_addr_v4, -1, NULL)) { + pr_err("invalid destination IP V4 value: %s\n", value); + goto fail; + } + } else { + if (!in6_pton(value, -1, (uint8_t *)dip_addr_v6.s6_addr, -1, NULL)) { + pr_err("invalid destination IP V6 value: %s\n", value); + goto fail; + } + } + + /* + * Destination port (dport) field validadtion. + */ + option = strsep(&fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_DPORT], "="); + if (!option || strcmp(option, "dport")) { + pr_err("invalid destination port option name: %s\n", option); + goto fail; + } + value = fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_DPORT]; + if (!sscanf(value, "%d", &dport)) { + pr_err("Unable to read destination port value %s\n", value); + goto fail; + } + + /* + * Protocol field validadtion. + */ + option = strsep(&fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_PROTOCOL], "="); + if (!option || strcmp(option, "protocol")) { + pr_err("invalid protocol option name: %s\n", option); + goto fail; + } + value = fields[ECM_SFE_L2_DEFUNCT_BY_5TUPLE_OPTION_PROTOCOL]; + if (!sscanf(value, "%d", &protocol)) { + pr_err("Unable to read protocol value %s\n", value); + goto fail; + } + + /* + * Call 5-tuple defunct functions. + */ + if (ip_ver == 4) { + pr_debug("sip: %pI4 sport: %d dip: %pI4 dport: %d protocol: %d\n", &sip_addr_v4, sport, &dip_addr_v4, dport, protocol); + defunct_result = ecm_sfe_common_defunct_ipv4_connection(sip_addr_v4, htons(sport), dip_addr_v4, htons(dport), protocol); + } else { + pr_debug("sip: %pI6 sport: %d dip: %pI6 dport: %d protocol: %d\n", &sip_addr_v6, sport, &dip_addr_v6, dport, protocol); + defunct_result = ecm_sfe_common_defunct_ipv6_connection(&sip_addr_v6, htons(sport), &dip_addr_v6, htons(dport), protocol); + } + + if (!defunct_result) { + pr_warn("No connection found with this 5-tuple\n"); + } + + ret = count; +fail: + kfree(cmd_buf); + + return ret; +} + +/* + * File operations for defunct by 5-tuple operations. + */ +static struct file_operations ecm_sfe_l2_defunct_by_5tuple_fops = { + .owner = THIS_MODULE, + .write = ecm_sfe_l2_defunct_by_5tuple_write, +}; + +/* + * ecm_sfe_l2_defunct_by_port_write() + * Writes the defunct by port command to the debugfs node. + */ +static ssize_t ecm_sfe_l2_defunct_by_port_write(struct file *f, const char *user_buf, + size_t count, loff_t *offset) +{ + char *cmd_buf; + char *fields; + char *option, *value; + int port; + int direction; + + /* + * Command is formed as: + * + * echo “sport=443” > /sys/kernel/debug/ecm_sfe_l2/defunct_by_port + * echo “dport=443” > /sys/kernel/debug/ecm_sfe_l2/defunct_by_port + */ + cmd_buf = kzalloc(count + 1, GFP_ATOMIC); + if (!cmd_buf) { + pr_warn("unable to allocate memory for cmd buffer\n"); + return -ENOMEM; + } + + count = simple_write_to_buffer(cmd_buf, count, offset, user_buf, count); + + /* + * Split the buffer into its fields + */ + fields = cmd_buf; + option = strsep(&fields, "="); + if (!strcmp(option, "sport")) { + direction = 0; + } else if (!strcmp(option, "dport")) { + direction = 1; + } else { + pr_err("invalid option name: %s\n", option); + kfree(cmd_buf); + return -EINVAL; + } + + value = fields; + if (!sscanf(value, "%d", &port)) { + pr_err("Unable to read port value %s\n", value); + kfree(cmd_buf); + return -EINVAL; + } + pr_debug("option: %s value: %d\n", option, port); + + kfree(cmd_buf); + + /* + * Call port based defunct function. + */ + ecm_sfe_common_defunct_by_port(port, direction, wan_name); + + return count; +} + +/* + * File operations for defunct by port operations. + */ +static struct file_operations ecm_sfe_l2_defunct_by_port_fops = { + .owner = THIS_MODULE, + .write = ecm_sfe_l2_defunct_by_port_write, +}; + +/* + * ecm_sfe_l2_defunct_by_protocol_write() + * Writes the defunct by protocol command to the debugfs node. + */ +static ssize_t ecm_sfe_l2_defunct_by_protocol_write(struct file *f, const char *user_buf, + size_t count, loff_t *offset) +{ + char *cmd_buf; + char *fields; + char *option, *value; + int protocol; + + /* + * Command is formed as: + * + * echo “protocol=6” > /sys/kernel/debug/ecm_sfe_l2/defunct_by_protocol + */ + cmd_buf = kzalloc(count + 1, GFP_ATOMIC); + if (!cmd_buf) { + pr_warn("unable to allocate memory for cmd buffer\n"); + return -ENOMEM; + } + + count = simple_write_to_buffer(cmd_buf, count, offset, user_buf, count); + + /* + * Split the buffer into its fields + */ + fields = cmd_buf; + option = strsep(&fields, "="); + if (strcmp(option, "protocol")) { + pr_err("invalid option name: %s\n", option); + kfree(cmd_buf); + return -EINVAL; + } + + value = fields; + if (!sscanf(value, "%d", &protocol)) { + pr_err("Unable to read protocol value %s\n", value); + kfree(cmd_buf); + return -EINVAL; + } + pr_debug("option: %s value: %d\n", option, protocol); + + kfree(cmd_buf); + + /* + * Defunct the connections which has this protocol number. + */ + ecm_sfe_common_defunct_by_protocol(protocol); + + return count; +} + +/* + * File operations for defunct by protocol operations. + */ +static struct file_operations ecm_sfe_l2_defunct_by_protocol_fops = { + .owner = THIS_MODULE, + .write = ecm_sfe_l2_defunct_by_protocol_write, +}; + +/* + * ecm_sfe_l2_wan_name_read() + * Reads the WAN interface name from the debugfs node wan_name + */ +static ssize_t ecm_sfe_l2_wan_name_read(struct file *f, char *buffer, + size_t len, loff_t *offset) +{ + return simple_read_from_buffer(buffer, len, offset, wan_name, wan_name_len); +} + +/* + * ecm_sfe_l2_wan_name_write() + * Writes the WAN interface name to the debugfs node wan_name + */ +static ssize_t ecm_sfe_l2_wan_name_write(struct file *f, const char *buffer, + size_t len, loff_t *offset) +{ + ssize_t ret; + + if (len > IFNAMSIZ) { + pr_err("WAN interface name is too long\n"); + return -EINVAL; + } + + ret = simple_write_to_buffer(wan_name, IFNAMSIZ, offset, buffer, len); + if (ret < 0) { + pr_err("WAN interface name cannot be written\n"); + return ret; + } + + wan_name[ret - 1] = '\0'; + wan_name_len = ret; + + return ret; +} + +/* + * File operations for wan interface name. + */ +static struct file_operations ecm_sfe_l2_wan_name_fops = { + .owner = THIS_MODULE, + .write = ecm_sfe_l2_wan_name_write, + .read = ecm_sfe_l2_wan_name_read, +}; + +struct ecm_sfe_common_callbacks sfe_cbs = { + .l2_accel_check = ecm_sfe_l2_accel_check_callback, /**< Callback to decide if L2 acceleration is wanted for the flow. */ +}; + +/* + * ecm_sfe_l2_init() + */ +static int __init ecm_sfe_l2_init(void) +{ + pr_debug("ECM SFE L2 module INIT\n"); + + /* + * Create entries in DebugFS for control functions + */ + ecm_sfe_l2_dentry = debugfs_create_dir("ecm_sfe_l2", NULL); + if (!ecm_sfe_l2_dentry) { + pr_info("Failed to create SFE L2 directory entry\n"); + return -1; + } + + if (!debugfs_create_file("wan_name", S_IWUSR, ecm_sfe_l2_dentry, + NULL, &ecm_sfe_l2_wan_name_fops)) { + pr_debug("Failed to create ecm wan interface file in debugfs\n"); + debugfs_remove_recursive(ecm_sfe_l2_dentry); + return -1; + } + + if (!debugfs_create_file("policy_rules", S_IWUSR, ecm_sfe_l2_dentry, + NULL, &ecm_sfe_l2_policy_rule_fops)) { + pr_debug("Failed to create ecm SFE L2 policy rules file in debugfs\n"); + debugfs_remove_recursive(ecm_sfe_l2_dentry); + return -1; + } + + if (!debugfs_create_file("defunct_by_protocol", S_IWUSR, ecm_sfe_l2_dentry, + NULL, &ecm_sfe_l2_defunct_by_protocol_fops)) { + pr_debug("Failed to create ecm defunct by protocol file in debugfs\n"); + debugfs_remove_recursive(ecm_sfe_l2_dentry); + return -1; + } + + if (!debugfs_create_file("defunct_by_5tuple", S_IWUSR, ecm_sfe_l2_dentry, + NULL, &ecm_sfe_l2_defunct_by_5tuple_fops)) { + pr_debug("Failed to create ecm defunct by 5tuple file in debugfs\n"); + debugfs_remove_recursive(ecm_sfe_l2_dentry); + return -1; + } + + if (!debugfs_create_file("defunct_by_port", S_IWUSR, ecm_sfe_l2_dentry, + NULL, &ecm_sfe_l2_defunct_by_port_fops)) { + pr_debug("Failed to create ecm defunct by port file in debugfs\n"); + debugfs_remove_recursive(ecm_sfe_l2_dentry); + return -1; + } + + if (ecm_sfe_common_callbacks_register(&sfe_cbs)) { + pr_debug("Failed to register callbacks\n"); + debugfs_remove_recursive(ecm_sfe_l2_dentry); + return -1; + } + return 0; +} + +/* + * ecm_sfe_l2_exit() + */ +static void __exit ecm_sfe_l2_exit(void) +{ + pr_debug("ECM SFE L2 check module EXIT\n"); + + ecm_sfe_common_callbacks_unregister(); + + /* + * Remove the debugfs files recursively. + */ + debugfs_remove_recursive(ecm_sfe_l2_dentry); +} + +module_init(ecm_sfe_l2_init) +module_exit(ecm_sfe_l2_exit) + +MODULE_DESCRIPTION("ECM SFE L2 Module"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif
diff --git a/qca-nss-ecm/exports/ecm_sfe_common_public.h b/qca-nss-ecm/exports/ecm_sfe_common_public.h new file mode 100644 index 0000000..41f6b6e --- /dev/null +++ b/qca-nss-ecm/exports/ecm_sfe_common_public.h
@@ -0,0 +1,136 @@ +/* + ************************************************************************** + * 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 ecm_fe_common_public.h + * ECM SFE frontend public APIs and data structures. + */ + +#ifndef __ECM_SFE_COMMON_PUBLIC_H__ +#define __ECM_SFE_COMMON_PUBLIC_H__ + +/** + * @addtogroup ecm_sfe_common_subsystem + * @{ + */ + +#define ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED (1 << 0) /**< L2 acceleration is allowed on the flow interface. */ +#define ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED (1 << 1) /**< L2 acceleration is allowed on the return interface. */ + +/** + * SFE common 5-tuple for external use. + */ +struct ecm_sfe_common_tuple { + uint32_t src_addr[4]; /**< Source IP in host order. */ + uint32_t dest_addr[4]; /**< Destination IP in host order. */ + + uint16_t src_port; /**< Source port port in host order. */ + uint16_t dest_port; /**< Destination port in host order. */ + uint32_t src_ifindex; /**< Source L2 interface index */ + uint32_t dest_ifindex; /**< Destination L2 interface index */ + uint8_t protocol; /**< Next protocol header number. */ + uint8_t ip_ver; /**< IP version 4 or 6. */ +}; + +/** + * Callback to which SFE clients will register and return bitmap of values that indicate L2 acceleration for each direction. + */ +typedef uint32_t (*ecm_sfe_common_l2_accel_check_callback_t)(struct ecm_sfe_common_tuple *tuple); + +/** + * Data structure for SFE common callbacks. + */ +struct ecm_sfe_common_callbacks { + ecm_sfe_common_l2_accel_check_callback_t l2_accel_check; /**< Callback to decide if L2 acceleration is wanted for the flow. */ +}; + +/** + * Defuncts an IPv4 5-tuple connection. + * + * @param src_ip The source IP address. + * @param src_port The source port. + * @param dest_ip The destination IP address. + * @param dest_port The destination port. + * @param protocol The protocol. + * + * @return + * True if defuncted; false if not. + */ +bool ecm_sfe_common_defunct_ipv4_connection(__be32 src_ip, int src_port, + __be32 dest_ip, int dest_port, int protocol); + +/** + * Defuncts an IPv6 5-tuple connection. + * + * @param src_ip The source IP address. + * @param src_port The source port. + * @param dest_ip The destination IP address. + * @param dest_port The destination port. + * @param protocol The protocol. + * + * @return + * True if defuncted; false if not. + */ +bool ecm_sfe_common_defunct_ipv6_connection(struct in6_addr *src_ip, int src_port, + struct in6_addr *dest_ip, int dest_port, int protocol); + +/** + * Defuncts all the connections with this protocol type. + * + * @param protocol Protocol type. + * + * @return + * None. + */ +void ecm_sfe_common_defunct_by_protocol(int protocol); + +/** + * Defuncts all the connections with this port number in the correct direction. + * + * @param port The port number. + * @param direction The direction of the port (source (1) or destination (2)) + * @param wan_name The WAN port interface name. + * + * @return + * None. + */ +void ecm_sfe_common_defunct_by_port(int port, int direction, char *wan_name); + +/** + * Registers a client for SFE common callbacks. + * + * @param sfe_cb SFE common callback pointer. + * + * @return + * 0 if success, error value if fails. + */ +int ecm_sfe_common_callbacks_register(struct ecm_sfe_common_callbacks *sfe_cb); + +/** + * Unregisters a client from SFE common callbacks. + * + * @return + * None. + */ +void ecm_sfe_common_callbacks_unregister(void); + +/** + * @} + */ + +#endif /* __ECM_SFE_COMMON_PUBLIC_H__ */
diff --git a/qca-nss-ecm/frontends/cmn/ecm_non_ported_ipv4.c b/qca-nss-ecm/frontends/cmn/ecm_non_ported_ipv4.c index 20f7d0d..c28f770 100644 --- a/qca-nss-ecm/frontends/cmn/ecm_non_ported_ipv4.c +++ b/qca-nss-ecm/frontends/cmn/ecm_non_ported_ipv4.c
@@ -295,7 +295,7 @@ case ECM_AE_CLASSIFIER_RESULT_NSS: if (!ecm_nss_feature_check(skb, ip_hdr)) { DEBUG_WARN("Unsupported feature found for NSS acceleration\n"); - goto fail_1; + return NF_ACCEPT; } defunct_callback = ecm_nss_non_ported_ipv4_connection_defunct_callback; feci = (struct ecm_front_end_connection_instance *)ecm_nss_non_ported_ipv4_connection_instance_alloc(can_accel, protocol, &nci);
diff --git a/qca-nss-ecm/frontends/cmn/ecm_non_ported_ipv6.c b/qca-nss-ecm/frontends/cmn/ecm_non_ported_ipv6.c index 7658828..ba50952 100644 --- a/qca-nss-ecm/frontends/cmn/ecm_non_ported_ipv6.c +++ b/qca-nss-ecm/frontends/cmn/ecm_non_ported_ipv6.c
@@ -294,7 +294,7 @@ case ECM_AE_CLASSIFIER_RESULT_NSS: if (!ecm_nss_feature_check(skb, ip_hdr)) { DEBUG_WARN("Unsupported feature found for NSS acceleration\n"); - goto fail_1; + return NF_ACCEPT; } defunct_callback = ecm_nss_non_ported_ipv6_connection_defunct_callback; feci = (struct ecm_front_end_connection_instance *)ecm_nss_non_ported_ipv6_connection_instance_alloc(can_accel, protocol, &nci);
diff --git a/qca-nss-ecm/frontends/ecm_front_end_common.c b/qca-nss-ecm/frontends/ecm_front_end_common.c index 40e6abe..f6b5300 100644 --- a/qca-nss-ecm/frontends/ecm_front_end_common.c +++ b/qca-nss-ecm/frontends/ecm_front_end_common.c
@@ -84,7 +84,7 @@ ECM_FE_FEATURE_SFE | ECM_FE_FEATURE_NON_PORTED | ECM_FE_FEATURE_CONN_LIMIT | /* SFE type */ ECM_FE_FEATURE_OVS_BRIDGE | ECM_FE_FEATURE_OVS_VLAN | ECM_FE_FEATURE_BRIDGE | - ECM_FE_FEATURE_BONDING, + ECM_FE_FEATURE_BONDING | ECM_FE_FEATURE_SRC_IF_CHECK, ECM_FE_FEATURE_NSS | ECM_FE_FEATURE_SFE | ECM_FE_FEATURE_NON_PORTED | ECM_FE_FEATURE_BRIDGE | ECM_FE_FEATURE_MULTICAST | ECM_FE_FEATURE_BONDING | ECM_FE_FEATURE_IGS | @@ -612,6 +612,9 @@ * Unregister sysctl table. */ if (ecm_front_end_ctl_tbl_hdr) { +#ifdef ECM_FRONT_END_SFE_ENABLE + ecm_sfe_sysctl_tbl_exit(); +#endif unregister_sysctl_table(ecm_front_end_ctl_tbl_hdr); } }
diff --git a/qca-nss-ecm/frontends/include/ecm_front_end_common.h b/qca-nss-ecm/frontends/include/ecm_front_end_common.h index 46c50d8..0c70a1d 100644 --- a/qca-nss-ecm/frontends/include/ecm_front_end_common.h +++ b/qca-nss-ecm/frontends/include/ecm_front_end_common.h
@@ -312,6 +312,6 @@ void ecm_front_end_common_sysctl_register(void); void ecm_front_end_common_sysctl_unregister(void); int ecm_sfe_sysctl_tbl_init(void); +void ecm_sfe_sysctl_tbl_exit(void); #endif /* __ECM_FRONT_END_COMMON_H */ -
diff --git a/qca-nss-ecm/frontends/include/ecm_front_end_types.h b/qca-nss-ecm/frontends/include/ecm_front_end_types.h index f7e2de1..2dcf3c1 100644 --- a/qca-nss-ecm/frontends/include/ecm_front_end_types.h +++ b/qca-nss-ecm/frontends/include/ecm_front_end_types.h
@@ -108,10 +108,11 @@ * An acceleration mode less than zero indicates a connection that cannot be accelerated, maybe due to error. */ enum ecm_front_end_acceleration_modes { + ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT = -8,/* Acceleration has failed for a short time due to the connection has become defunct and waiting for the removal */ ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT = -7, /* Acceleration has permanently failed due to the connection has become defunct */ ECM_FRONT_END_ACCELERATION_MODE_FAIL_DECEL = -6, /* Acceleration has permanently failed due to deceleration malfunction */ ECM_FRONT_END_ACCELERATION_MODE_FAIL_NO_ACTION = -5, /* Acceleration has permanently failed due to too many offloads that were rejected without any packets being offloaded */ - ECM_FRONT_END_ACCELERATION_MODE_FAIL_ACCEL_ENGINE = -4, /* Acceleration has permanently failed due to too many accel engine NAK's */ + ECM_FRONT_END_ACCELERATION_MODE_FAIL_ACCEL_ENGINE = -4, /* Acceleration has permanently failed due to too many accel engine NAK's */ ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER = -3, /* Acceleration has permanently failed due to too many driver interaction failures */ ECM_FRONT_END_ACCELERATION_MODE_FAIL_RULE = -2, /* Acceleration has permanently failed due to bad rule data */ ECM_FRONT_END_ACCELERATION_MODE_FAIL_DENIED = -1, /* Acceleration has permanently failed due to can_accel denying accel */
diff --git a/qca-nss-ecm/frontends/nss/ecm_nss_multicast_ipv4.c b/qca-nss-ecm/frontends/nss/ecm_nss_multicast_ipv4.c index 405c5f5..7591331 100644 --- a/qca-nss-ecm/frontends/nss/ecm_nss_multicast_ipv4.c +++ b/qca-nss-ecm/frontends/nss/ecm_nss_multicast_ipv4.c
@@ -1584,7 +1584,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/nss/ecm_nss_multicast_ipv6.c b/qca-nss-ecm/frontends/nss/ecm_nss_multicast_ipv6.c index 29a490c..91d3a9d 100644 --- a/qca-nss-ecm/frontends/nss/ecm_nss_multicast_ipv6.c +++ b/qca-nss-ecm/frontends/nss/ecm_nss_multicast_ipv6.c
@@ -1539,7 +1539,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/nss/ecm_nss_non_ported_ipv4.c b/qca-nss-ecm/frontends/nss/ecm_nss_non_ported_ipv4.c index 927355c..ec4f365 100644 --- a/qca-nss-ecm/frontends/nss/ecm_nss_non_ported_ipv4.c +++ b/qca-nss-ecm/frontends/nss/ecm_nss_non_ported_ipv4.c
@@ -1460,7 +1460,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/nss/ecm_nss_non_ported_ipv6.c b/qca-nss-ecm/frontends/nss/ecm_nss_non_ported_ipv6.c index 086d108..13e0cc5 100644 --- a/qca-nss-ecm/frontends/nss/ecm_nss_non_ported_ipv6.c +++ b/qca-nss-ecm/frontends/nss/ecm_nss_non_ported_ipv6.c
@@ -1321,7 +1321,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/nss/ecm_nss_ported_ipv4.c b/qca-nss-ecm/frontends/nss/ecm_nss_ported_ipv4.c index 8d87036..6375b6b 100644 --- a/qca-nss-ecm/frontends/nss/ecm_nss_ported_ipv4.c +++ b/qca-nss-ecm/frontends/nss/ecm_nss_ported_ipv4.c
@@ -1557,7 +1557,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/nss/ecm_nss_ported_ipv6.c b/qca-nss-ecm/frontends/nss/ecm_nss_ported_ipv6.c index 886149f..1be8a58 100644 --- a/qca-nss-ecm/frontends/nss/ecm_nss_ported_ipv6.c +++ b/qca-nss-ecm/frontends/nss/ecm_nss_ported_ipv6.c
@@ -1467,7 +1467,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/sfe/ecm_sfe_common.c b/qca-nss-ecm/frontends/sfe/ecm_sfe_common.c index f7aee0d..6cb1473 100644 --- a/qca-nss-ecm/frontends/sfe/ecm_sfe_common.c +++ b/qca-nss-ecm/frontends/sfe/ecm_sfe_common.c
@@ -41,6 +41,18 @@ #include "ecm_sfe_ipv4.h" #include "ecm_sfe_ipv6.h" #include "ecm_sfe_common.h" +#include "exports/ecm_sfe_common_public.h" + + +/* + * Callback object to support SFE frontend interaction with external code + */ +struct ecm_sfe_common_callbacks ecm_sfe_cb; + +/* + * Sysctl table + */ +static struct ctl_table_header *ecm_sfe_ctl_tbl_hdr; static bool ecm_sfe_fast_xmit_enable = true; @@ -278,7 +290,8 @@ */ int ecm_sfe_sysctl_tbl_init() { - if (!register_sysctl(ECM_FRONT_END_SYSCTL_PATH, ecm_sfe_sysctl_tbl)) { + ecm_sfe_ctl_tbl_hdr = register_sysctl(ECM_FRONT_END_SYSCTL_PATH, ecm_sfe_sysctl_tbl); + if (!ecm_sfe_ctl_tbl_hdr) { DEBUG_WARN("Unable to register ecm_sfe_sysctl_tbl"); return -EINVAL; } @@ -287,6 +300,17 @@ } /* + * ecm_sfe_sysctl_tbl_exit() + * Unregister sysctl for SFE + */ +void ecm_sfe_sysctl_tbl_exit() +{ + if (ecm_sfe_ctl_tbl_hdr) { + unregister_sysctl_table(ecm_sfe_ctl_tbl_hdr); + } +} + +/* * ecm_sfe_common_init_fe_info() * Initialize common fe info */ @@ -371,3 +395,119 @@ break; } } + +/* + * ecm_sfe_common_tuple_set() + * Sets the SFE common tuple object with the ECM connection rule paramaters. + * + * This tuple object will be used by external module to make decision on L2 acceleration. + */ +void ecm_sfe_common_tuple_set(struct ecm_front_end_connection_instance *feci, + int32_t from_iface_id, int32_t to_iface_id, + struct ecm_sfe_common_tuple *tuple) +{ + ip_addr_t saddr; + ip_addr_t daddr; + + tuple->protocol = ecm_db_connection_protocol_get(feci->ci); + tuple->ip_ver = feci->ip_version; + + tuple->src_port = ecm_db_connection_port_get(feci->ci, ECM_DB_OBJ_DIR_FROM); + tuple->dest_port = ecm_db_connection_port_get(feci->ci, ECM_DB_OBJ_DIR_TO); + + tuple->src_ifindex = from_iface_id; + tuple->dest_ifindex = to_iface_id; + + ecm_db_connection_address_get(feci->ci, ECM_DB_OBJ_DIR_FROM, saddr); + ecm_db_connection_address_get(feci->ci, ECM_DB_OBJ_DIR_TO, daddr); + + if (feci->ip_version == 4) { + ECM_IP_ADDR_TO_NIN4_ADDR(tuple->src_addr[0], saddr); + ECM_IP_ADDR_TO_NIN4_ADDR(tuple->dest_addr[0], daddr); + } else { + ECM_IP_ADDR_TO_SFE_IPV6_ADDR(tuple->src_addr, saddr); + ECM_IP_ADDR_TO_SFE_IPV6_ADDR(tuple->dest_addr, daddr); + } +} + +/* + * ecm_sfe_common_defunct_ipv4_connection() + * Defunct an IPv4 5-tuple connection. + */ +bool ecm_sfe_common_defunct_ipv4_connection(__be32 src_ip, int src_port, + __be32 dest_ip, int dest_port, int protocol) +{ + return ecm_db_connection_decel_v4(src_ip, src_port, dest_ip, dest_port, protocol); +} +EXPORT_SYMBOL(ecm_sfe_common_defunct_ipv4_connection); + +/* + * ecm_sfe_common_defunct_ipv6_connection() + * Defunct an IPv6 5-tuple connection. + */ +bool ecm_sfe_common_defunct_ipv6_connection(struct in6_addr *src_ip, int src_port, + struct in6_addr *dest_ip, int dest_port, int protocol) +{ + return ecm_db_connection_decel_v6(src_ip, src_port, dest_ip, dest_port, protocol); +} +EXPORT_SYMBOL(ecm_sfe_common_defunct_ipv6_connection); + +/* + * ecm_sfe_common_defunct_by_protocol() + * Defunct the connections by the protocol type (e.g:TCP, UDP) + */ +void ecm_sfe_common_defunct_by_protocol(int protocol) +{ + ecm_db_connection_defunct_by_protocol(protocol); +} +EXPORT_SYMBOL(ecm_sfe_common_defunct_by_protocol); + +/* + * ecm_sfe_common_defunct_by_port() + * Defunct the connections associated with this port in the direction + * relative to the ECM's connection direction as well. + * + * TODO: + * For now, all the connections from/to this port number are defuncted. + * Directional defunct can be implemented later, but there is a trade of here: + * For each connection in the database, the connection's from/to interfaces will + * be checked with the wan_name and direction will be determined and then the connection + * will be defuncted if there is a match with this port number. This process may be heavier + * than defuncting all the connections from/to this port number. So, the direction and wan_name + * are optional for this API for now. + */ +void ecm_sfe_common_defunct_by_port(int port, int direction, char *wan_name) +{ + ecm_db_connection_defunct_by_port(htons(port), ECM_DB_OBJ_DIR_FROM); + ecm_db_connection_defunct_by_port(htons(port), ECM_DB_OBJ_DIR_TO); +} +EXPORT_SYMBOL(ecm_sfe_common_defunct_by_port); + +/* + * ecm_sfe_common_callbacks_register() + * Registers SFE common callbacks. + */ +int ecm_sfe_common_callbacks_register(struct ecm_sfe_common_callbacks *sfe_cb) +{ + if (!sfe_cb || !sfe_cb->l2_accel_check) { + DEBUG_ERROR("SFE L2 acceleration check callback is NULL\n"); + return -EINVAL; + } + + rcu_assign_pointer(ecm_sfe_cb.l2_accel_check, sfe_cb->l2_accel_check); + synchronize_rcu(); + + return 0; +} +EXPORT_SYMBOL(ecm_sfe_common_callbacks_register); + +/* + * ecm_sfe_common_callbacks_unregister() + * Unregisters SFE common callbacks. + */ +void ecm_sfe_common_callbacks_unregister(void) +{ + rcu_assign_pointer(ecm_sfe_cb.l2_accel_check, NULL); + synchronize_rcu(); +} +EXPORT_SYMBOL(ecm_sfe_common_callbacks_unregister);
diff --git a/qca-nss-ecm/frontends/sfe/ecm_sfe_common.h b/qca-nss-ecm/frontends/sfe/ecm_sfe_common.h index cb240f9..54625f3 100644 --- a/qca-nss-ecm/frontends/sfe/ecm_sfe_common.h +++ b/qca-nss-ecm/frontends/sfe/ecm_sfe_common.h
@@ -15,6 +15,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "ecm_sfe_common_public.h" + +/* + * Export the callback object for frontend usage. + */ +extern struct ecm_sfe_common_callbacks ecm_sfe_cb; + #ifdef CONFIG_XFRM /* * Which type of ipsec process traffic need. @@ -148,3 +155,6 @@ uint32_t ecm_sfe_common_get_stats_bitmap(struct ecm_sfe_common_fe_info *fe_info, ecm_db_obj_dir_t dir); void ecm_sfe_common_set_stats_bitmap(struct ecm_sfe_common_fe_info *fe_info, ecm_db_obj_dir_t dir, uint8_t bit); void ecm_sfe_common_update_rule(struct ecm_front_end_connection_instance *feci, enum ecm_rule_update_type type, void *arg); +void ecm_sfe_common_tuple_set(struct ecm_front_end_connection_instance *feci, + int32_t from_iface_id, int32_t to_iface_id, + struct ecm_sfe_common_tuple *tuple);
diff --git a/qca-nss-ecm/frontends/sfe/ecm_sfe_non_ported_ipv4.c b/qca-nss-ecm/frontends/sfe/ecm_sfe_non_ported_ipv4.c index 9ebc5d4..ab92a8a 100644 --- a/qca-nss-ecm/frontends/sfe/ecm_sfe_non_ported_ipv4.c +++ b/qca-nss-ecm/frontends/sfe/ecm_sfe_non_ported_ipv4.c
@@ -1101,9 +1101,8 @@ if (ecm_interface_src_check || ecm_db_connection_is_pppoe_bridged_get(feci->ci)) { DEBUG_INFO("%px: Source interface check flag is enabled\n", nnpci); - /* - * TO DO: No interface check rule create message type defined in SFE's API. - */ + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK; + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK; } if (pr->process_actions & ECM_CLASSIFIER_PROCESS_ACTION_QOS_TAG) { @@ -1461,7 +1460,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/sfe/ecm_sfe_non_ported_ipv6.c b/qca-nss-ecm/frontends/sfe/ecm_sfe_non_ported_ipv6.c index afc33ce..9c99fae 100644 --- a/qca-nss-ecm/frontends/sfe/ecm_sfe_non_ported_ipv6.c +++ b/qca-nss-ecm/frontends/sfe/ecm_sfe_non_ported_ipv6.c
@@ -1018,6 +1018,8 @@ */ if (ecm_interface_src_check || ecm_db_connection_is_pppoe_bridged_get(feci->ci)) { DEBUG_INFO("%px: Source interface check flag is enabled\n", nnpci); + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK; + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK; } /* @@ -1340,7 +1342,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/sfe/ecm_sfe_ported_ipv4.c b/qca-nss-ecm/frontends/sfe/ecm_sfe_ported_ipv4.c index 524e118..c3f5d73 100644 --- a/qca-nss-ecm/frontends/sfe/ecm_sfe_ported_ipv4.c +++ b/qca-nss-ecm/frontends/sfe/ecm_sfe_ported_ipv4.c
@@ -165,6 +165,7 @@ struct ecm_front_end_connection_instance *feci; struct ecm_sfe_ported_ipv4_connection_instance *npci; ecm_front_end_acceleration_mode_t result_mode; + bool is_defunct = false; /* * Is this a response to a create message? @@ -327,10 +328,28 @@ DEBUG_INFO("%px: Decelerate was pending\n", ci); + /* + * Check if the pending decelerate was done with the defunct process. + * If it was, set the is_defunct flag of the feci to false for re-try. + */ + if (feci->is_defunct) { + is_defunct = feci->is_defunct; + feci->is_defunct = false; + } + spin_unlock_bh(&ecm_sfe_ipv4_lock); spin_unlock_bh(&feci->lock); - feci->decelerate(feci); + /* + * If the pending decelerate was done through defunct process, we should + * re-try it here with the same defunct function, because the purpose of that + * process is to remove the connection from the database as well after decelerating it. + */ + if (is_defunct) { + ecm_db_connection_make_defunct(ci); + } else { + feci->decelerate(feci); + } /* * Release the connection. @@ -354,7 +373,7 @@ interface_num = msg->conn_rule.flow_interface_num; } if (ecm_sfe_common_fast_xmit_check(interface_num)) { - msg->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_TRANSMIT_FAST; + msg->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_TRANSMIT_FAST; } interface_num = msg->conn_rule.return_top_interface_num; @@ -362,7 +381,7 @@ interface_num = msg->conn_rule.return_interface_num; } if (ecm_sfe_common_fast_xmit_check(interface_num)) { - msg->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_TRANSMIT_FAST; + msg->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_TRANSMIT_FAST; } rcu_read_unlock_bh(); @@ -402,6 +421,8 @@ uint8_t dest_mac_xlate[ETH_ALEN]; ecm_db_direction_t ecm_dir; ecm_front_end_acceleration_mode_t result_mode; + uint32_t l2_accel_bits = (ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED | ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED); + ecm_sfe_common_l2_accel_check_callback_t l2_accel_check; DEBUG_CHECK_MAGIC(npci, ECM_SFE_PORTED_IPV4_CONNECTION_INSTANCE_MAGIC, "%px: magic failed", npci); @@ -488,6 +509,23 @@ nircm->conn_rule.return_interface_num = to_sfe_iface_id; /* + * Check which side of the connection can support L2 acceleration. + * The check is done only for the routed flows and if the L2 feature is enabled. + */ + if (sfe_is_l2_feature_enabled() && ecm_db_connection_is_routed_get(feci->ci)) { + rcu_read_lock(); + l2_accel_check = rcu_dereference(ecm_sfe_cb.l2_accel_check); + if (l2_accel_check) { + struct ecm_sfe_common_tuple l2_accel_tuple; + + ecm_sfe_common_tuple_set(feci, from_sfe_iface_id, to_sfe_iface_id, &l2_accel_tuple); + + l2_accel_bits = l2_accel_check(&l2_accel_tuple); + } + rcu_read_unlock(); + } + + /* * Set interface numbers involved in accelerating this connection. * These are the inner facing addresses from the heirarchy interface lists we got above. */ @@ -544,7 +582,7 @@ break; } - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_BRIDGE, list_index, from_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_BRIDGE, list_index, from_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_BRIDGE); } @@ -570,7 +608,7 @@ break; } - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_OVS_BRIDGE, list_index, from_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_OVS_BRIDGE, list_index, from_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_OVS_BRIDGE); } @@ -623,6 +661,15 @@ break; } + /* + * If external module decide that L2 acceleration is not allowed, we should return + * without setting PPPoE parameters. + */ + if (!(l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { + DEBUG_TRACE("%px: L2 acceleration is not allowed for the PPPoE interface\n", npci); + break; + } + feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_PPPOE); /* @@ -676,7 +723,7 @@ } nircm->valid_flags |= SFE_RULE_CREATE_VLAN_VALID; - if (sfe_is_l2_feature_enabled()) { + if (sfe_is_l2_feature_enabled() && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_VLAN); @@ -703,7 +750,7 @@ break; case ECM_DB_IFACE_TYPE_MACVLAN: #ifdef ECM_INTERFACE_MACVLAN_ENABLE - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_MACVLAN, list_index, from_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_MACVLAN, list_index, from_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_MACVLAN); } @@ -740,7 +787,7 @@ case ECM_DB_IFACE_TYPE_LAG: #ifdef ECM_INTERFACE_BOND_ENABLE - if (sfe_is_l2_feature_enabled()) { + if (sfe_is_l2_feature_enabled() && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; /* * LAG device gets its stats by summing up all stats of its @@ -822,7 +869,7 @@ break; } - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_BRIDGE, list_index, to_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_BRIDGE, list_index, to_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_BRIDGE); } @@ -849,7 +896,7 @@ break; } - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_OVS_BRIDGE, list_index, to_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_OVS_BRIDGE, list_index, to_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_OVS_BRIDGE); } @@ -902,6 +949,15 @@ break; } + /* + * If external module decide that L2 acceleration is not allowed, we should return + * without setting PPPoE parameters. + */ + if (!(l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { + DEBUG_TRACE("%px: L2 acceleration is not allowed for the PPPoE interface\n", npci); + break; + } + feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_PPPOE); /* @@ -954,7 +1010,7 @@ } nircm->valid_flags |= SFE_RULE_CREATE_VLAN_VALID; - if (sfe_is_l2_feature_enabled()) { + if (sfe_is_l2_feature_enabled() && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_VLAN); @@ -982,7 +1038,7 @@ case ECM_DB_IFACE_TYPE_MACVLAN: #ifdef ECM_INTERFACE_MACVLAN_ENABLE - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_MACVLAN, list_index, to_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_MACVLAN, list_index, to_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_MACVLAN); } @@ -1019,7 +1075,7 @@ case ECM_DB_IFACE_TYPE_LAG: #ifdef ECM_INTERFACE_BOND_ENABLE - if (sfe_is_l2_feature_enabled()) { + if (sfe_is_l2_feature_enabled() && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; /* * LAG device gets its stats by summing up all stats of its @@ -1070,6 +1126,29 @@ } } + if (ecm_interface_src_check) { + DEBUG_INFO("%px: Source interface check flag is enabled\n", npci); + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK; + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK; + } + + /* + * Enable source interface check without flushing the rule for this flow to re-inject the packet to + * the network stack in SFE driver after the first pass of the packet coming with the L2 interface. + * In the second pass, the packet will come to SFE with the L3 interface. If there are more than 3 interfaces + * in the hierarchy, the packet will be re-injected to the stack until the flows input interface matches with the + * rule's match_dev. + */ + if (!(l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK; + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK_NO_FLUSH; + } + + if (!(l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK; + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK_NO_FLUSH; + } + /* * Set up the flow and return qos tags */ @@ -1578,7 +1657,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-ecm/frontends/sfe/ecm_sfe_ported_ipv6.c b/qca-nss-ecm/frontends/sfe/ecm_sfe_ported_ipv6.c index fe0d642..a01ee76 100644 --- a/qca-nss-ecm/frontends/sfe/ecm_sfe_ported_ipv6.c +++ b/qca-nss-ecm/frontends/sfe/ecm_sfe_ported_ipv6.c
@@ -168,6 +168,7 @@ ip_addr_t flow_ip; ip_addr_t return_ip; ecm_front_end_acceleration_mode_t result_mode; + bool is_defunct = false; /* * Is this a response to a create message? @@ -333,10 +334,28 @@ DEBUG_INFO("%px: Decelerate was pending\n", ci); + /* + * Check if the pending decelerate was done with the defunct process. + * If it was, set the is_defunct flag of the feci to false for re-try. + */ + if (feci->is_defunct) { + is_defunct = feci->is_defunct; + feci->is_defunct = false; + } + spin_unlock_bh(&ecm_sfe_ipv6_lock); spin_unlock_bh(&feci->lock); - feci->decelerate(feci); + /* + * If the pending decelerate was done through defunct process, we should + * re-try it here with the same defunct function, because the purpose of that + * process is to remove the connection from the database as well after decelerating it. + */ + if (is_defunct) { + ecm_db_connection_make_defunct(ci); + } else { + feci->decelerate(feci); + } /* * Release the connection. @@ -360,7 +379,7 @@ interface_num = msg->conn_rule.flow_interface_num; } if (ecm_sfe_common_fast_xmit_check(interface_num)) { - msg->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_TRANSMIT_FAST; + msg->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_TRANSMIT_FAST; } interface_num = msg->conn_rule.return_top_interface_num; @@ -368,7 +387,7 @@ interface_num = msg->conn_rule.return_interface_num; } if (ecm_sfe_common_fast_xmit_check(interface_num)) { - msg->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_TRANSMIT_FAST; + msg->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_TRANSMIT_FAST; } rcu_read_unlock_bh(); @@ -407,6 +426,8 @@ ip_addr_t src_ip; ip_addr_t dest_ip; ecm_front_end_acceleration_mode_t result_mode; + uint32_t l2_accel_bits = (ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED | ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED); + ecm_sfe_common_l2_accel_check_callback_t l2_accel_check; DEBUG_CHECK_MAGIC(npci, ECM_SFE_PORTED_IPV6_CONNECTION_INSTANCE_MAGIC, "%px: magic failed", npci); @@ -493,6 +514,23 @@ nircm->conn_rule.return_interface_num = to_sfe_iface_id; /* + * Check which side of the connection can support L2 acceleration. + * The check is done only for the routed flows and if the L2 feature is enabled. + */ + if (sfe_is_l2_feature_enabled() && ecm_db_connection_is_routed_get(feci->ci)) { + rcu_read_lock(); + l2_accel_check = rcu_dereference(ecm_sfe_cb.l2_accel_check); + if (l2_accel_check) { + struct ecm_sfe_common_tuple l2_accel_tuple; + + ecm_sfe_common_tuple_set(feci, from_sfe_iface_id, to_sfe_iface_id, &l2_accel_tuple); + + l2_accel_bits = l2_accel_check(&l2_accel_tuple); + } + rcu_read_unlock(); + } + + /* * Set interface numbers involved in accelerating this connection. * These are the inner facing addresses from the heirarchy interface lists we got above. */ @@ -549,7 +587,7 @@ break; } - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_BRIDGE, list_index, from_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_BRIDGE, list_index, from_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_BRIDGE); } @@ -575,7 +613,7 @@ break; } - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_OVS_BRIDGE, list_index, from_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_OVS_BRIDGE, list_index, from_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_OVS_BRIDGE); } @@ -628,6 +666,15 @@ break; } + /* + * If external module decide that L2 acceleration is not allowed, we should return + * without setting PPPoE parameters. + */ + if (!(l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { + DEBUG_TRACE("%px: L2 acceleration is not allowed for the PPPoE interface\n", npci); + break; + } + feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_PPPOE); /* @@ -681,7 +728,7 @@ } nircm->valid_flags |= SFE_RULE_CREATE_VLAN_VALID; - if (sfe_is_l2_feature_enabled()) { + if (sfe_is_l2_feature_enabled() && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_VLAN); @@ -709,7 +756,7 @@ case ECM_DB_IFACE_TYPE_MACVLAN: #ifdef ECM_INTERFACE_MACVLAN_ENABLE - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_MACVLAN, list_index, from_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_MACVLAN, list_index, from_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_MACVLAN); } @@ -746,7 +793,7 @@ case ECM_DB_IFACE_TYPE_LAG: #ifdef ECM_INTERFACE_BOND_ENABLE - if (sfe_is_l2_feature_enabled()) { + if (sfe_is_l2_feature_enabled() && (l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE; /* * LAG device gets its stats by summing up all stats of its @@ -828,7 +875,7 @@ break; } - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_BRIDGE, list_index, to_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_BRIDGE, list_index, to_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_BRIDGE); } @@ -855,7 +902,7 @@ break; } - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_OVS_BRIDGE, list_index, to_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_OVS_BRIDGE, list_index, to_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_OVS_BRIDGE); } @@ -908,6 +955,15 @@ break; } + /* + * If external module decide that L2 acceleration is not allowed, we should return + * without setting PPPoE parameters. + */ + if (!(l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { + DEBUG_TRACE("%px: L2 acceleration is not allowed for the PPPoE interface\n", npci); + break; + } + feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_FROM, ECM_DB_IFACE_TYPE_PPPOE); /* @@ -960,7 +1016,7 @@ } nircm->valid_flags |= SFE_RULE_CREATE_VLAN_VALID; - if (sfe_is_l2_feature_enabled()) { + if (sfe_is_l2_feature_enabled() && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_VLAN); @@ -988,7 +1044,7 @@ case ECM_DB_IFACE_TYPE_MACVLAN: #ifdef ECM_INTERFACE_MACVLAN_ENABLE - if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_MACVLAN, list_index, to_ifaces_first)) { + if (ecm_sfe_common_is_l2_iface_supported(ECM_DB_IFACE_TYPE_MACVLAN, list_index, to_ifaces_first) && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; feci->set_stats_bitmap(feci, ECM_DB_OBJ_DIR_TO, ECM_DB_IFACE_TYPE_MACVLAN); } @@ -1025,7 +1081,7 @@ case ECM_DB_IFACE_TYPE_LAG: #ifdef ECM_INTERFACE_BOND_ENABLE - if (sfe_is_l2_feature_enabled()) { + if (sfe_is_l2_feature_enabled() && (l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { nircm->rule_flags |= SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE; /* * LAG device gets its stats by summing up all stats of its @@ -1076,6 +1132,29 @@ } } + if (ecm_interface_src_check) { + DEBUG_INFO("%px: Source interface check flag is enabled\n", npci); + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK; + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK; + } + + /* + * Enable source interface check without flushing the rule for this flow to re-inject the packet to + * the network stack in SFE driver after the first pass of the packet coming with the L2 interface. + * In the second pass, the packet will come to SFE with the L3 interface. If there are more than 3 interfaces + * in the hierarchy, the packet will be re-injected to the stack until the flows input interface matches with the + * rule's match_dev. + */ + if (!(l2_accel_bits & ECM_SFE_COMMON_FLOW_L2_ACCEL_ALLOWED)) { + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK; + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK_NO_FLUSH; + } + + if (!(l2_accel_bits & ECM_SFE_COMMON_RETURN_L2_ACCEL_ALLOWED)) { + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK; + nircm->rule_flags |= SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK_NO_FLUSH; + } + /* * Set up the flow and return qos tags */ @@ -1522,7 +1601,7 @@ * If connection became defunct then set mode so that no further accel/decel attempts occur. */ if (feci->is_defunct) { - feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT; + feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DEFUNCT_SHORT; } spin_unlock_bh(&feci->lock);
diff --git a/qca-nss-sfe/exports/sfe_api.h b/qca-nss-sfe/exports/sfe_api.h index d0d28d0..1bcfedc 100644 --- a/qca-nss-sfe/exports/sfe_api.h +++ b/qca-nss-sfe/exports/sfe_api.h
@@ -44,9 +44,12 @@ #define SFE_RULE_CREATE_FLAG_L2_ENCAP (1<<7) /**< consists of an encapsulating protocol that carries an IPv4 payload within it. */ #define SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE (1<<8) /**< Use flow interface number instead of top interface. */ #define SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE (1<<9) /**< Use return interface number instead of top interface. */ -#define SFE_RULE_CREATE_FLAG_SRC_INTERFACE_CHECK (1<<10) /**< Check source interface. */ -#define SFE_RULE_CREATE_FLAG_FLOW_TRANSMIT_FAST (1<<11) /**< original flow transmit fast. */ -#define SFE_RULE_CREATE_FLAG_RETURN_TRANSMIT_FAST (1<<12) /**< return flow transmit fast. */ +#define SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK (1<<10) /**< Check source interface on the flow direction . */ +#define SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK (1<<11) /**< Check source interface on the return direction . */ +#define SFE_RULE_CREATE_FLAG_FLOW_TRANSMIT_FAST (1<<12) /**< original flow transmit fast. */ +#define SFE_RULE_CREATE_FLAG_RETURN_TRANSMIT_FAST (1<<13) /**< return flow transmit fast. */ +#define SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK_NO_FLUSH (1<<14) /**< Check source interface on the flow direction but do not flush the connection. */ +#define SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK_NO_FLUSH (1<<15) /**< Check source interface on the return direction but do not flush the connection. */ /** * Rule creation validity flags.
diff --git a/qca-nss-sfe/sfe.c b/qca-nss-sfe/sfe.c index f665318..0195d41 100644 --- a/qca-nss-sfe/sfe.c +++ b/qca-nss-sfe/sfe.c
@@ -119,6 +119,15 @@ int32_t l2_feature_support; /* L2 feature support */ + /* + * SFE Bypass Mode. + * When enabled, SFE's shortcut path will be bypassed. + * 0: Disabled. + * 1: Bypass all packets. + * 2: Bypass only packets with fwmark matches bypass_mark. + */ + int bypass_mode; + u32 bypass_mark; }; static struct sfe_ctx_instance_internal __sfe_ctx; @@ -1261,20 +1270,25 @@ */ int sfe_recv(struct sk_buff *skb) { + struct sfe_ctx_instance_internal *sfe_ctx = &__sfe_ctx; struct net_device *dev; struct sfe_l2_info l2_info; int ret; - /* - * We know that for the vast majority of packets we need the transport - * layer header so we may as well start to fetch it now! - */ - prefetch(skb->data + 32); - barrier(); - dev = skb->dev; /* + * Apply SFE Bypass Mode policy. + */ + if (unlikely(sfe_ctx->bypass_mode == 1)) { + return 0; + } + if (unlikely(sfe_ctx->bypass_mode == 2 && sfe_ctx->bypass_mark && + skb->mark == sfe_ctx->bypass_mark)) { + return 0; + } + + /* * Setting parse flags to 0 since l2_info is passed for non L2.5 header case as well */ l2_info.parse_flags = 0; @@ -1475,6 +1489,69 @@ __ATTR(l2_feature, 0644, sfe_get_l2_feature, sfe_set_l2_feature); /* + * SFE Bypass Mode + */ +static ssize_t +sfe_get_bypass_mode(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sfe_ctx_instance_internal *sfe_ctx = &__sfe_ctx; + return snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", sfe_ctx->bypass_mode); +} + +static ssize_t +sfe_set_bypass_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sfe_ctx_instance_internal *sfe_ctx = &__sfe_ctx; + int ret; + int bypass_mode; + + ret = kstrtou32(buf, 0, &bypass_mode); + if (ret) { + return ret; + } + if (bypass_mode > 2 || bypass_mode < 0) { + return -EINVAL; + } + sfe_ctx->bypass_mode = bypass_mode; + return count; +} + +static const struct device_attribute sfe_bypass_mode_attr = + __ATTR(bypass_mode, S_IWUSR | S_IRUGO, sfe_get_bypass_mode, + sfe_set_bypass_mode); + +static ssize_t +sfe_get_bypass_mark(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sfe_ctx_instance_internal *sfe_ctx = &__sfe_ctx; + return snprintf(buf, (ssize_t)PAGE_SIZE, "0x%x\n", + sfe_ctx->bypass_mark); +} + +static ssize_t +sfe_set_bypass_mark(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sfe_ctx_instance_internal *sfe_ctx = &__sfe_ctx; + int ret; + int bypass_mark; + + ret = kstrtou32(buf, 0, &bypass_mark); + if (ret) { + return ret; + } + sfe_ctx->bypass_mark = bypass_mark; + return count; +} + +static const struct device_attribute sfe_bypass_mark_attr = + __ATTR(bypass_mark, S_IWUSR | S_IRUGO, sfe_get_bypass_mark, + sfe_set_bypass_mark); + +/* * sfe_init_if() */ int sfe_init_if(void) @@ -1511,6 +1588,21 @@ goto exit2; } + result = sysfs_create_file(sfe_ctx->sys_sfe, + &sfe_bypass_mode_attr.attr); + if (result) { + DEBUG_ERROR("failed to register Bypass Mode sysfs file: %d\n", + result); + goto exit2; + } + result = sysfs_create_file(sfe_ctx->sys_sfe, + &sfe_bypass_mark_attr.attr); + if (result) { + DEBUG_ERROR("failed to register Bypass Mark sysfs file: %d\n", + result); + goto exit2; + } + spin_lock_init(&sfe_ctx->lock); INIT_LIST_HEAD(&sfe_ctx->msg_queue);
diff --git a/qca-nss-sfe/sfe_ipv4.c b/qca-nss-sfe/sfe_ipv4.c index 0598b68..1fd2883 100644 --- a/qca-nss-sfe/sfe_ipv4.c +++ b/qca-nss-sfe/sfe_ipv4.c
@@ -1121,7 +1121,7 @@ /* * Allocate the various connection tracking objects. */ - c = (struct sfe_ipv4_connection *)kmalloc(sizeof(struct sfe_ipv4_connection), GFP_ATOMIC); + c = (struct sfe_ipv4_connection *)kzalloc(sizeof(struct sfe_ipv4_connection), GFP_ATOMIC); if (unlikely(!c)) { DEBUG_WARN("%px: memory allocation of connection entry failed\n", msg); this_cpu_inc(si->stats_pcpu->connection_create_failures64); @@ -1130,7 +1130,7 @@ return -ENOMEM; } - original_cm = (struct sfe_ipv4_connection_match *)kmalloc(sizeof(struct sfe_ipv4_connection_match), GFP_ATOMIC); + original_cm = (struct sfe_ipv4_connection_match *)kzalloc(sizeof(struct sfe_ipv4_connection_match), GFP_ATOMIC); if (unlikely(!original_cm)) { DEBUG_WARN("%px: memory allocation of connection match entry failed\n", msg); this_cpu_inc(si->stats_pcpu->connection_create_failures64); @@ -1140,7 +1140,7 @@ return -ENOMEM; } - reply_cm = (struct sfe_ipv4_connection_match *)kmalloc(sizeof(struct sfe_ipv4_connection_match), GFP_ATOMIC); + reply_cm = (struct sfe_ipv4_connection_match *)kzalloc(sizeof(struct sfe_ipv4_connection_match), GFP_ATOMIC); if (unlikely(!reply_cm)) { DEBUG_WARN("%px: memory allocation of connection match entry failed\n", msg); this_cpu_inc(si->stats_pcpu->connection_create_failures64); @@ -1221,18 +1221,11 @@ original_cm->xlate_src_port = 0; } - atomic_set(&original_cm->rx_packet_count, 0); - original_cm->rx_packet_count64 = 0; - atomic_set(&original_cm->rx_byte_count, 0); - original_cm->rx_byte_count64 = 0; - original_cm->xmit_dev = dest_dev; original_cm->xmit_dev_mtu = msg->conn_rule.return_mtu; original_cm->connection = c; original_cm->counter_match = reply_cm; - original_cm->l2_hdr_size = 0; - original_cm->flags = 0; /* * UDP Socket is valid only in decap direction. @@ -1310,12 +1303,13 @@ } } - reply_cm->l2_hdr_size = 0; - if (msg->rule_flags & SFE_RULE_CREATE_FLAG_SRC_INTERFACE_CHECK) { + if (msg->rule_flags & SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK) { original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK; } - reply_cm->flags = 0; + if (msg->rule_flags & SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK_NO_FLUSH) { + original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH; + } /* * Adding PPPoE parameters to original and reply entries based on the direction where @@ -1351,10 +1345,14 @@ ether_addr_copy(reply_cm->pppoe_remote_mac, msg->pppoe_rule.return_pppoe_remote_mac); } - if (msg->rule_flags & SFE_RULE_CREATE_FLAG_SRC_INTERFACE_CHECK) { + if (msg->rule_flags & SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK) { reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK; } + if (msg->rule_flags & SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK_NO_FLUSH) { + reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH; + } + /* * For the non-arp interface, we don't write L2 HDR. */ @@ -1426,11 +1424,6 @@ reply_cm->xlate_src_port = 0; } - atomic_set(&reply_cm->rx_packet_count, 0); - reply_cm->rx_packet_count64 = 0; - atomic_set(&reply_cm->rx_byte_count, 0); - reply_cm->rx_byte_count64 = 0; - reply_cm->xmit_dev = src_dev; reply_cm->xmit_dev_mtu = msg->conn_rule.flow_mtu; @@ -2512,6 +2505,71 @@ __ATTR(stats_work_cpu, S_IWUSR | S_IRUGO, sfe_ipv4_get_cpu, sfe_ipv4_set_cpu); /* + * DSCP rewrite table + */ +static ssize_t +sfe_ipv4_get_dscp_rewrite_mark_to_match(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sfe_ipv4 *si = &__si; + return snprintf(buf, (ssize_t)PAGE_SIZE, "0x%x\n", + si->dscp_rewrite_mark_to_match); +} + +static ssize_t +sfe_ipv4_set_dscp_rewrite_mark_to_match(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct sfe_ipv4 *si = &__si; + int ret; + u32 mark_to_match; + + ret = kstrtou32(buf, 0, &mark_to_match); + if (ret) + return ret; + si->dscp_rewrite_mark_to_match = mark_to_match; + return size; +} + +static const struct device_attribute sfe_ipv4_dscp_rewrite_mark_to_match_attr = + __ATTR(dscp_rewrite_mark_to_match, S_IWUSR | S_IRUGO, + sfe_ipv4_get_dscp_rewrite_mark_to_match, + sfe_ipv4_set_dscp_rewrite_mark_to_match); + +static ssize_t +sfe_ipv4_get_dscp_rewrite_dscp_to_set(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sfe_ipv4 *si = &__si; + return snprintf(buf, (ssize_t)PAGE_SIZE, "0x%x\n", + si->dscp_rewrite_dscp_to_set >> SFE_IPV4_DSCP_SHIFT); +} + +static ssize_t +sfe_ipv4_set_dscp_rewrite_dscp_to_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct sfe_ipv4 *si = &__si; + int ret; + u32 dscp_to_set; + + ret = kstrtou32(buf, 0, &dscp_to_set); + if (ret) + return ret; + si->dscp_rewrite_dscp_to_set = dscp_to_set << SFE_IPV4_DSCP_SHIFT; + return size; +} + +static const struct device_attribute sfe_ipv4_dscp_rewrite_dscp_to_set_attr = + __ATTR(dscp_rewrite_dscp_to_set, S_IWUSR | S_IRUGO, + sfe_ipv4_get_dscp_rewrite_dscp_to_set, + sfe_ipv4_set_dscp_rewrite_dscp_to_set); + +/* * sfe_ipv4_conn_match_hash_init() * Initialize conn match hash lists */ @@ -2603,11 +2661,26 @@ goto exit3; } + result = sysfs_create_file(si->sys_ipv4, + &sfe_ipv4_dscp_rewrite_mark_to_match_attr.attr); + if (result) { + DEBUG_ERROR("failed to register DSCP rewrite mark_to_match file: %d\n", + result); + goto exit4; + } + result = sysfs_create_file(si->sys_ipv4, + &sfe_ipv4_dscp_rewrite_dscp_to_set_attr.attr); + if (result) { + DEBUG_ERROR("failed to register DSCP rewrite dscp_to_set file: %d\n", + result); + goto exit5; + } + #ifdef CONFIG_NF_FLOW_COOKIE result = sysfs_create_file(si->sys_ipv4, &sfe_ipv4_flow_cookie_attr.attr); if (result) { DEBUG_ERROR("failed to register flow cookie enable file: %d\n", result); - goto exit4; + goto exit6; } #endif /* CONFIG_NF_FLOW_COOKIE */ @@ -2619,7 +2692,7 @@ #endif if (result < 0) { DEBUG_ERROR("can't register nf local out hook: %d\n", result); - goto exit5; + goto exit7; } DEBUG_INFO("Register nf local out hook success: %d\n", result); #endif @@ -2629,7 +2702,7 @@ result = register_chrdev(0, "sfe_ipv4", &sfe_ipv4_debug_dev_fops); if (result < 0) { DEBUG_ERROR("Failed to register chrdev: %d\n", result); - goto exit6; + goto exit8; } si->debug_dev = result; @@ -2644,7 +2717,7 @@ spin_lock_init(&si->lock); return 0; -exit6: +exit8: #ifdef SFE_PROCESS_LOCAL_OUT DEBUG_TRACE("sfe: Unregister local out hook\n"); #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)) @@ -2652,13 +2725,19 @@ #else nf_unregister_net_hooks(&init_net, sfe_ipv4_ops_local_out, ARRAY_SIZE(sfe_ipv4_ops_local_out)); #endif -exit5: +exit7: #endif #ifdef CONFIG_NF_FLOW_COOKIE sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_flow_cookie_attr.attr); -exit4: +exit6: #endif /* CONFIG_NF_FLOW_COOKIE */ + sysfs_remove_file(si->sys_ipv4, + &sfe_ipv4_dscp_rewrite_dscp_to_set_attr.attr); +exit5: + sysfs_remove_file(si->sys_ipv4, + &sfe_ipv4_dscp_rewrite_mark_to_match_attr.attr); +exit4: sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_cpu_attr.attr); exit3: sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_debug_dev_attr.attr); @@ -2702,6 +2781,10 @@ #ifdef CONFIG_NF_FLOW_COOKIE sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_flow_cookie_attr.attr); #endif /* CONFIG_NF_FLOW_COOKIE */ + sysfs_remove_file(si->sys_ipv4, + &sfe_ipv4_dscp_rewrite_dscp_to_set_attr.attr); + sysfs_remove_file(si->sys_ipv4, + &sfe_ipv4_dscp_rewrite_mark_to_match_attr.attr); sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_debug_dev_attr.attr); sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_cpu_attr.attr);
diff --git a/qca-nss-sfe/sfe_ipv4.h b/qca-nss-sfe/sfe_ipv4.h index 48630db..4e8169b 100644 --- a/qca-nss-sfe/sfe_ipv4.h +++ b/qca-nss-sfe/sfe_ipv4.h
@@ -69,7 +69,7 @@ #define SFE_IPV4_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG (1<<12) /* Insert VLAN tag */ #define SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK (1<<13) - /* Source interface check.*/ + /* Source interface check */ #define SFE_IPV4_CONNECTION_MATCH_FLAG_PASSTHROUGH (1<<14) /* passthrough flow: encap/decap to be skipped for this flow */ #define SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT (1<<15) @@ -78,6 +78,8 @@ /* Fast xmit flow checked or not */ #define SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT_DEV_ADMISSION (1<<17) /* Fast xmit may be possible for this flow, if SFE check passes */ +#define SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH (1<<18) + /* Source interface check but do not flush the connection */ /* * IPv4 connection matching structure. @@ -363,6 +365,16 @@ struct kobject *sys_ipv4; /* sysfs linkage */ int debug_dev; /* Major number of the debug char device */ u32 debug_read_seq; /* sequence number for debug dump */ + + /* + * DSCP rewrite table + * When `mark_to_match` is set non-zero then any packet with the + * specified skb->mark will override flow DSCP policy with + * `dscp_to_set` value. i.e. basically equivalent to `iptables -m mark + * --mark <mark_to_match> -j DSCP --set-dscp <dscp_to_set>` + */ + u32 dscp_rewrite_mark_to_match; + u32 dscp_rewrite_dscp_to_set; }; /*
diff --git a/qca-nss-sfe/sfe_ipv4_gre.c b/qca-nss-sfe/sfe_ipv4_gre.c index 084ea3b..9626a58 100644 --- a/qca-nss-sfe/sfe_ipv4_gre.c +++ b/qca-nss-sfe/sfe_ipv4_gre.c
@@ -100,19 +100,22 @@ * Source interface validate. */ if (unlikely((cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK) && (cm->match_dev != dev))) { - struct sfe_ipv4_connection *c = cm->connection; - int ret; + if (!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH)) { + struct sfe_ipv4_connection *c = cm->connection; + int ret; - spin_lock_bh(&si->lock); - ret = sfe_ipv4_remove_connection(si, c); - spin_unlock_bh(&si->lock); + DEBUG_TRACE("flush on source interface check failure\n"); + spin_lock_bh(&si->lock); + ret = sfe_ipv4_remove_connection(si, c); + spin_unlock_bh(&si->lock); - if (ret) { - sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + if (ret) { + sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + } } rcu_read_unlock(); sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INVALID_SRC_IFACE); - DEBUG_TRACE("flush on wrong source interface check failure\n"); + DEBUG_TRACE("exception the packet on source interface check failure\n"); return 0; }
diff --git a/qca-nss-sfe/sfe_ipv4_tcp.c b/qca-nss-sfe/sfe_ipv4_tcp.c index 8de3269..b2d5ec9 100644 --- a/qca-nss-sfe/sfe_ipv4_tcp.c +++ b/qca-nss-sfe/sfe_ipv4_tcp.c
@@ -194,17 +194,20 @@ * Source interface validate. */ if (unlikely((cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK) && (cm->match_dev != dev))) { - struct sfe_ipv4_connection *c = cm->connection; - spin_lock_bh(&si->lock); - ret = sfe_ipv4_remove_connection(si, c); - spin_unlock_bh(&si->lock); + if (!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH)) { + struct sfe_ipv4_connection *c = cm->connection; + DEBUG_TRACE("flush on source interface check failure\n"); + spin_lock_bh(&si->lock); + ret = sfe_ipv4_remove_connection(si, c); + spin_unlock_bh(&si->lock); - if (ret) { - sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + if (ret) { + sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + } } rcu_read_unlock(); sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INVALID_SRC_IFACE); - DEBUG_TRACE("flush on wrong source interface check failure\n"); + DEBUG_TRACE("exception the packet on source interface check failure\n"); return 0; } @@ -564,9 +567,26 @@ } /* - * Update DSCP + * Apply packet Mark. + * If Mark was set by the Ingress Qdisc that takes precedence over + * flow policy. */ - if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK)) { + if (likely(skb->mark == 0)) { + if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_MARK)) { + skb->mark = cm->mark; + } + } + + /* + * Update DSCP + * DSCP rewrite table takes precedence over flow policy. + */ + if (unlikely(si->dscp_rewrite_mark_to_match != 0 && + si->dscp_rewrite_mark_to_match == skb->mark)) { + iph->tos = (iph->tos & SFE_IPV4_DSCP_MASK) | + si->dscp_rewrite_dscp_to_set; + } else if (unlikely(cm->flags & + SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK)) { iph->tos = (iph->tos & SFE_IPV4_DSCP_MASK) | cm->dscp; } @@ -686,13 +706,6 @@ } /* - * Mark outgoing packet - */ - if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_MARK)) { - skb->mark = cm->mark; - } - - /* * For the first packets, check if it could got fast xmit. */ if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT_FLOW_CHECKED)
diff --git a/qca-nss-sfe/sfe_ipv4_udp.c b/qca-nss-sfe/sfe_ipv4_udp.c index 1762d74..4b15f7c 100644 --- a/qca-nss-sfe/sfe_ipv4_udp.c +++ b/qca-nss-sfe/sfe_ipv4_udp.c
@@ -190,17 +190,20 @@ * Source interface validate. */ if (unlikely((cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK) && (cm->match_dev != dev))) { - struct sfe_ipv4_connection *c = cm->connection; - spin_lock_bh(&si->lock); - ret = sfe_ipv4_remove_connection(si, c); - spin_unlock_bh(&si->lock); + if (!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH)) { + struct sfe_ipv4_connection *c = cm->connection; + DEBUG_TRACE("flush on source interface check failure\n"); + spin_lock_bh(&si->lock); + ret = sfe_ipv4_remove_connection(si, c); + spin_unlock_bh(&si->lock); - if (ret) { - sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + if (ret) { + sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + } } rcu_read_unlock(); sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INVALID_SRC_IFACE); - DEBUG_TRACE("flush on wrong source interface check failure\n"); + DEBUG_TRACE("exception the packet on source interface check failure\n"); return 0; } @@ -470,9 +473,26 @@ } /* - * Update DSCP + * Apply packet Mark. + * If Mark was set by the Ingress Qdisc that takes precedence over + * flow policy. */ - if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK)) { + if (likely(skb->mark == 0)) { + if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_MARK)) { + skb->mark = cm->mark; + } + } + + /* + * Update DSCP + * DSCP rewrite table takes precedence over flow policy. + */ + if (unlikely(si->dscp_rewrite_mark_to_match != 0 && + si->dscp_rewrite_mark_to_match == skb->mark)) { + iph->tos = (iph->tos & SFE_IPV4_DSCP_MASK) | + si->dscp_rewrite_dscp_to_set; + } else if (unlikely(cm->flags & + SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK)) { iph->tos = (iph->tos & SFE_IPV4_DSCP_MASK) | cm->dscp; } @@ -529,13 +549,6 @@ } /* - * Mark outgoing packet. - */ - if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_MARK)) { - skb->mark = cm->mark; - } - - /* * For the first packets, check if it could got fast xmit. */ if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT_FLOW_CHECKED)
diff --git a/qca-nss-sfe/sfe_ipv6.c b/qca-nss-sfe/sfe_ipv6.c index cbd67ec..488f3a4 100644 --- a/qca-nss-sfe/sfe_ipv6.c +++ b/qca-nss-sfe/sfe_ipv6.c
@@ -159,10 +159,10 @@ */ hlist_for_each_entry_rcu(cm, lhead, hnode) { if ((cm->match_dest_port != dest_port) || + (cm->match_src_port != src_port) || (!sfe_ipv6_addr_equal(cm->match_src_ip, src_ip)) || (!sfe_ipv6_addr_equal(cm->match_dest_ip, dest_ip)) || - (cm->match_protocol != protocol) || - (cm->match_dev != dev)) { + (cm->match_protocol != protocol)) { continue; } @@ -1129,7 +1129,7 @@ /* * Allocate the various connection tracking objects. */ - c = (struct sfe_ipv6_connection *)kmalloc(sizeof(struct sfe_ipv6_connection), GFP_ATOMIC); + c = (struct sfe_ipv6_connection *)kzalloc(sizeof(struct sfe_ipv6_connection), GFP_ATOMIC); if (unlikely(!c)) { DEBUG_WARN("%px: memory allocation of connection entry failed\n", msg); this_cpu_inc(si->stats_pcpu->connection_create_failures64); @@ -1138,7 +1138,7 @@ return -ENOMEM; } - original_cm = (struct sfe_ipv6_connection_match *)kmalloc(sizeof(struct sfe_ipv6_connection_match), GFP_ATOMIC); + original_cm = (struct sfe_ipv6_connection_match *)kzalloc(sizeof(struct sfe_ipv6_connection_match), GFP_ATOMIC); if (unlikely(!original_cm)) { this_cpu_inc(si->stats_pcpu->connection_create_failures64); DEBUG_WARN("%px: memory allocation of connection match entry failed\n", msg); @@ -1148,7 +1148,7 @@ return -ENOMEM; } - reply_cm = (struct sfe_ipv6_connection_match *)kmalloc(sizeof(struct sfe_ipv6_connection_match), GFP_ATOMIC); + reply_cm = (struct sfe_ipv6_connection_match *)kzalloc(sizeof(struct sfe_ipv6_connection_match), GFP_ATOMIC); if (unlikely(!reply_cm)) { this_cpu_inc(si->stats_pcpu->connection_create_failures64); DEBUG_WARN("%px: memory allocation of connection match entry failed\n", msg); @@ -1217,18 +1217,12 @@ original_cm->xlate_dest_ip[0] = *(struct sfe_ipv6_addr *)tuple->return_ip; original_cm->xlate_dest_port = tuple->return_ident; - atomic_set(&original_cm->rx_packet_count, 0); - original_cm->rx_packet_count64 = 0; - atomic_set(&original_cm->rx_byte_count, 0); - original_cm->rx_byte_count64 = 0; original_cm->xmit_dev = dest_dev; original_cm->xmit_dev_mtu = msg->conn_rule.return_mtu; original_cm->connection = c; original_cm->counter_match = reply_cm; - original_cm->l2_hdr_size = 0; - original_cm->flags = 0; /* * Valid in decap direction only @@ -1307,9 +1301,6 @@ } } - reply_cm->l2_hdr_size = 0; - reply_cm->flags = 0; - /* * Adding PPPoE parameters to original and reply entries based on the direction where * PPPoE header is valid in ECM rule. @@ -1344,10 +1335,14 @@ ether_addr_copy(reply_cm->pppoe_remote_mac, msg->pppoe_rule.return_pppoe_remote_mac); } - if (msg->rule_flags & SFE_RULE_CREATE_FLAG_SRC_INTERFACE_CHECK) { + if (msg->rule_flags & SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK) { original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK; } + if (msg->rule_flags & SFE_RULE_CREATE_FLAG_FLOW_SRC_INTERFACE_CHECK_NO_FLUSH) { + original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH; + } + /* * For the non-arp interface, we don't write L2 HDR. * Excluding PPPoE from this, since we are now supporting PPPoE encap/decap. @@ -1406,10 +1401,6 @@ reply_cm->match_src_port = tuple->return_ident; } - atomic_set(&original_cm->rx_byte_count, 0); - reply_cm->rx_packet_count64 = 0; - atomic_set(&reply_cm->rx_byte_count, 0); - reply_cm->rx_byte_count64 = 0; reply_cm->xmit_dev = src_dev; reply_cm->xmit_dev_mtu = msg->conn_rule.flow_mtu; @@ -1606,10 +1597,14 @@ } } - if (msg->rule_flags & SFE_RULE_CREATE_FLAG_SRC_INTERFACE_CHECK) { + if (msg->rule_flags & SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK) { reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK; } + if (msg->rule_flags & SFE_RULE_CREATE_FLAG_RETURN_SRC_INTERFACE_CHECK_NO_FLUSH) { + reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH; + } + /* * For the non-arp interface, we don't write L2 HDR. * Excluding PPPoE from this, since we are now supporting PPPoE encap/decap. @@ -2489,7 +2484,72 @@ static const struct device_attribute sfe_ipv6_cpu_attr = __ATTR(stat_work_cpu, S_IWUSR | S_IRUGO, sfe_ipv6_get_cpu, sfe_ipv6_set_cpu); - /* +/* + * DSCP rewrite table + */ +static ssize_t +sfe_ipv6_get_dscp_rewrite_mark_to_match(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sfe_ipv6 *si = &__si6; + return snprintf(buf, (ssize_t)PAGE_SIZE, "0x%x\n", + si->dscp_rewrite_mark_to_match); +} + +static ssize_t +sfe_ipv6_set_dscp_rewrite_mark_to_match(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct sfe_ipv6 *si = &__si6; + int ret; + u32 mark_to_match; + + ret = kstrtou32(buf, 0, &mark_to_match); + if (ret) + return ret; + si->dscp_rewrite_mark_to_match = mark_to_match; + return size; +} + +static const struct device_attribute sfe_ipv6_dscp_rewrite_mark_to_match_attr = + __ATTR(dscp_rewrite_mark_to_match, S_IWUSR | S_IRUGO, + sfe_ipv6_get_dscp_rewrite_mark_to_match, + sfe_ipv6_set_dscp_rewrite_mark_to_match); + +static ssize_t +sfe_ipv6_get_dscp_rewrite_dscp_to_set(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sfe_ipv6 *si = &__si6; + return snprintf(buf, (ssize_t)PAGE_SIZE, "0x%x\n", + si->dscp_rewrite_dscp_to_set >> SFE_IPV6_DSCP_SHIFT); +} + +static ssize_t +sfe_ipv6_set_dscp_rewrite_dscp_to_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct sfe_ipv6 *si = &__si6; + int ret; + u32 dscp_to_set; + + ret = kstrtou32(buf, 0, &dscp_to_set); + if (ret) + return ret; + si->dscp_rewrite_dscp_to_set = dscp_to_set << SFE_IPV6_DSCP_SHIFT; + return size; +} + +static const struct device_attribute sfe_ipv6_dscp_rewrite_dscp_to_set_attr = + __ATTR(dscp_rewrite_dscp_to_set, S_IWUSR | S_IRUGO, + sfe_ipv6_get_dscp_rewrite_dscp_to_set, + sfe_ipv6_set_dscp_rewrite_dscp_to_set); + +/* * sfe_ipv6_hash_init() * Initialize conn match hash lists */ @@ -2583,11 +2643,26 @@ goto exit3; } + result = sysfs_create_file(si->sys_ipv6, + &sfe_ipv6_dscp_rewrite_mark_to_match_attr.attr); + if (result) { + DEBUG_ERROR("failed to register DSCP rewrite mark_to_match file: %d\n", + result); + goto exit4; + } + result = sysfs_create_file(si->sys_ipv6, + &sfe_ipv6_dscp_rewrite_dscp_to_set_attr.attr); + if (result) { + DEBUG_ERROR("failed to register DSCP rewrite dscp_to_set file: %d\n", + result); + goto exit5; + } + #ifdef CONFIG_NF_FLOW_COOKIE result = sysfs_create_file(si->sys_ipv6, &sfe_ipv6_flow_cookie_attr.attr); if (result) { DEBUG_ERROR("failed to register flow cookie enable file: %d\n", result); - goto exit4; + goto exit6; } #endif /* CONFIG_NF_FLOW_COOKIE */ @@ -2597,13 +2672,13 @@ #else result = nf_register_net_hooks(&init_net, sfe_ipv6_ops_local_out, ARRAY_SIZE(sfe_ipv6_ops_local_out)); #endif -#endif if (result < 0) { DEBUG_ERROR("can't register nf local out hook: %d\n", result); - goto exit5; + goto exit7; } else { DEBUG_ERROR("Register nf local out hook success: %d\n", result); } +#endif /* * Register our debug char device. @@ -2611,7 +2686,7 @@ result = register_chrdev(0, "sfe_ipv6", &sfe_ipv6_debug_dev_fops); if (result < 0) { DEBUG_ERROR("Failed to register chrdev: %d\n", result); - goto exit6; + goto exit8; } si->debug_dev = result; @@ -2626,7 +2701,7 @@ return 0; -exit6: +exit8: #ifdef SFE_PROCESS_LOCAL_OUT #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)) DEBUG_TRACE("sfe: Unregister local out hook\n"); @@ -2635,16 +2710,21 @@ DEBUG_TRACE("sfe: Unregister local out hook\n"); nf_unregister_net_hooks(&init_net, sfe_ipv6_ops_local_out, ARRAY_SIZE(sfe_ipv6_ops_local_out)); #endif +exit7: #endif -exit5: #ifdef CONFIG_NF_FLOW_COOKIE sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_flow_cookie_attr.attr); -exit4: +exit6: #endif /* CONFIG_NF_FLOW_COOKIE */ + sysfs_remove_file(si->sys_ipv6, + &sfe_ipv6_dscp_rewrite_dscp_to_set_attr.attr); +exit5: + sysfs_remove_file(si->sys_ipv6, + &sfe_ipv6_dscp_rewrite_mark_to_match_attr.attr); +exit4: sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_cpu_attr.attr); - exit3: sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_debug_dev_attr.attr); @@ -2691,7 +2771,10 @@ #ifdef CONFIG_NF_FLOW_COOKIE sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_flow_cookie_attr.attr); #endif /* CONFIG_NF_FLOW_COOKIE */ - + sysfs_remove_file(si->sys_ipv6, + &sfe_ipv6_dscp_rewrite_dscp_to_set_attr.attr); + sysfs_remove_file(si->sys_ipv6, + &sfe_ipv6_dscp_rewrite_mark_to_match_attr.attr); sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_cpu_attr.attr); sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_debug_dev_attr.attr);
diff --git a/qca-nss-sfe/sfe_ipv6.h b/qca-nss-sfe/sfe_ipv6.h index 2aa9f41..9c78f1c 100644 --- a/qca-nss-sfe/sfe_ipv6.h +++ b/qca-nss-sfe/sfe_ipv6.h
@@ -82,7 +82,7 @@ #define SFE_IPV6_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG (1<<12) /* Insert VLAN tag */ #define SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK (1<<13) - /* Source interface check.*/ + /* Source interface check */ #define SFE_IPV6_CONNECTION_MATCH_FLAG_PASSTHROUGH (1<<14) /* passthrough flow: encap/decap to be skipped for this flow */ #define SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT (1<<15) @@ -91,6 +91,8 @@ /* fast xmit checked or not*/ #define SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT_DEV_ADMISSION (1<<17) /* Fast xmit may be possible for this flow, if SFE check passes */ +#define SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH (1<<18) + /* Source interface check but do not flush the connection */ /* * IPv6 connection matching structure. @@ -381,6 +383,16 @@ struct kobject *sys_ipv6; /* sysfs linkage */ int debug_dev; /* Major number of the debug char device */ u32 debug_read_seq; /* sequence number for debug dump */ + + /* + * DSCP rewrite table + * When `mark_to_match` is set non-zero then any packet with the + * specified skb->mark will override flow DSCP policy with + * `dscp_to_set` value. i.e. basically equivalent to `ip6tables -m mark + * --mark <mark_to_match> -j DSCP --set-dscp <dscp_to_set>` + */ + u32 dscp_rewrite_mark_to_match; + u32 dscp_rewrite_dscp_to_set; }; /*
diff --git a/qca-nss-sfe/sfe_ipv6_gre.c b/qca-nss-sfe/sfe_ipv6_gre.c index 361c23a..8a48b3f 100644 --- a/qca-nss-sfe/sfe_ipv6_gre.c +++ b/qca-nss-sfe/sfe_ipv6_gre.c
@@ -99,18 +99,21 @@ * Source interface validate. */ if (unlikely((cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK) && (cm->match_dev != dev))) { - struct sfe_ipv6_connection *c = cm->connection; - int ret; - spin_lock_bh(&si->lock); - ret = sfe_ipv6_remove_connection(si, c); - spin_unlock_bh(&si->lock); + if (!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH)) { + struct sfe_ipv6_connection *c = cm->connection; + int ret; + DEBUG_TRACE("flush on source interface check failure\n"); + spin_lock_bh(&si->lock); + ret = sfe_ipv6_remove_connection(si, c); + spin_unlock_bh(&si->lock); - if (ret) { - sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + if (ret) { + sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + } } rcu_read_unlock(); sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INVALID_SRC_IFACE); - DEBUG_TRACE("flush on wrong source interface check failure\n"); + DEBUG_TRACE("exception the packet on source interface check failure\n"); return 0; }
diff --git a/qca-nss-sfe/sfe_ipv6_tcp.c b/qca-nss-sfe/sfe_ipv6_tcp.c index 6ccc8c7..6ba30b3 100644 --- a/qca-nss-sfe/sfe_ipv6_tcp.c +++ b/qca-nss-sfe/sfe_ipv6_tcp.c
@@ -196,17 +196,20 @@ * Source interface validate. */ if (unlikely((cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK) && (cm->match_dev != dev))) { - struct sfe_ipv6_connection *c = cm->connection; - spin_lock_bh(&si->lock); - ret = sfe_ipv6_remove_connection(si, c); - spin_unlock_bh(&si->lock); + if (!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH)) { + struct sfe_ipv6_connection *c = cm->connection; + DEBUG_TRACE("flush on source interface check failure\n"); + spin_lock_bh(&si->lock); + ret = sfe_ipv6_remove_connection(si, c); + spin_unlock_bh(&si->lock); - if (ret) { - sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + if (ret) { + sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + } } rcu_read_unlock(); sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INVALID_SRC_IFACE); - DEBUG_TRACE("flush on wrong source interface check failure\n"); + DEBUG_TRACE("exception the packet on source interface check failure\n"); return 0; } @@ -575,9 +578,25 @@ } /* - * Update DSCP + * Apply packet Mark. + * If Mark was set by the Ingress Qdisc that takes precedence over + * flow policy. */ - if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) { + if (likely(skb->mark == 0)) { + if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_MARK)) { + skb->mark = cm->mark; + } + } + + /* + * Update DSCP + * DSCP rewrite table takes precedence over flow policy. + */ + if (unlikely(si->dscp_rewrite_mark_to_match != 0 && + si->dscp_rewrite_mark_to_match == skb->mark)) { + sfe_ipv6_change_dsfield(iph, si->dscp_rewrite_dscp_to_set); + } else if (unlikely(cm->flags & + SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) { sfe_ipv6_change_dsfield(iph, cm->dscp); } @@ -691,13 +710,6 @@ } /* - * Mark outgoing packet - */ - if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_MARK)) { - skb->mark = cm->mark; - } - - /* * For the first packets, check if it could got fast xmit. */ if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT_FLOW_CHECKED)
diff --git a/qca-nss-sfe/sfe_ipv6_udp.c b/qca-nss-sfe/sfe_ipv6_udp.c index f34c6ee..445b43f 100644 --- a/qca-nss-sfe/sfe_ipv6_udp.c +++ b/qca-nss-sfe/sfe_ipv6_udp.c
@@ -206,17 +206,20 @@ * Source interface validate. */ if (unlikely((cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK) && (cm->match_dev != dev))) { - struct sfe_ipv6_connection *c = cm->connection; - spin_lock_bh(&si->lock); - ret = sfe_ipv6_remove_connection(si, c); - spin_unlock_bh(&si->lock); + if (!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH)) { + struct sfe_ipv6_connection *c = cm->connection; + DEBUG_TRACE("flush on source interface check failure\n"); + spin_lock_bh(&si->lock); + ret = sfe_ipv6_remove_connection(si, c); + spin_unlock_bh(&si->lock); - if (ret) { - sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + if (ret) { + sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH); + } } rcu_read_unlock(); sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INVALID_SRC_IFACE); - DEBUG_TRACE("flush on wrong source interface check failure\n"); + DEBUG_TRACE("exception the packet on source interface check failure\n"); return 0; } @@ -400,9 +403,25 @@ } /* - * Update DSCP + * Apply packet Mark. + * If Mark was set by the Ingress Qdisc that takes precedence over + * flow policy. */ - if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) { + if (likely(skb->mark == 0)) { + if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_MARK)) { + skb->mark = cm->mark; + } + } + + /* + * Update DSCP + * DSCP rewrite table takes precedence over flow policy. + */ + if (unlikely(si->dscp_rewrite_mark_to_match != 0 && + si->dscp_rewrite_mark_to_match == skb->mark)) { + sfe_ipv6_change_dsfield(iph, si->dscp_rewrite_dscp_to_set); + } else if (unlikely(cm->flags & + SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) { sfe_ipv6_change_dsfield(iph, cm->dscp); } @@ -523,13 +542,6 @@ } /* - * Mark outgoing packet. - */ - if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_MARK)) { - skb->mark = cm->mark; - } - - /* * For the first packets, check if it could got fast xmit. */ if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT_FLOW_CHECKED)