From cc2e4246400cb06b1cadce37aacd4c1618bdadda Mon Sep 17 00:00:00 2001 From: quarkysg Date: Tue, 14 Dec 2021 16:36:44 +0800 Subject: [PATCH] qca-nss-ecm: applied bug-fixes from QSDK repo --- .../patches/101-use-kernel-api.patch | 85 +++ ...102-fix-multicast-bridge-route-issue.patch | 504 ++++++++++++++++++ .../103-allow-egress-bridge-hairpin.patch | 106 ++++ ...do-not-process-bridged-packets-twice.patch | 58 ++ .../105-fix-tun6rd-peer-update-issue.patch | 19 + .../patches/200-resolve-high-load.patch | 8 +- .../202-fix-null-pointer-exception.patch | 4 +- 7 files changed, 778 insertions(+), 6 deletions(-) create mode 100644 package/qca/qca-nss-ecm/patches/101-use-kernel-api.patch create mode 100644 package/qca/qca-nss-ecm/patches/102-fix-multicast-bridge-route-issue.patch create mode 100644 package/qca/qca-nss-ecm/patches/103-allow-egress-bridge-hairpin.patch create mode 100644 package/qca/qca-nss-ecm/patches/104-do-not-process-bridged-packets-twice.patch create mode 100644 package/qca/qca-nss-ecm/patches/105-fix-tun6rd-peer-update-issue.patch diff --git a/package/qca/qca-nss-ecm/patches/101-use-kernel-api.patch b/package/qca/qca-nss-ecm/patches/101-use-kernel-api.patch new file mode 100644 index 00000000000000..889bf6bd8e5f92 --- /dev/null +++ b/package/qca/qca-nss-ecm/patches/101-use-kernel-api.patch @@ -0,0 +1,85 @@ +--- a/frontends/nss/ecm_nss_ported_ipv4.c ++++ b/frontends/nss/ecm_nss_ported_ipv4.c +@@ -2155,7 +2155,7 @@ unsigned int ecm_nss_ported_ipv4_process + } + + #ifdef ECM_INTERFACE_VXLAN_ENABLE +- if ((is_vxlan_dev(in_dev) || is_vxlan_dev(out_dev)) && is_routed) { ++ if ((netif_is_vxlan(in_dev) || netif_is_vxlan(out_dev)) && is_routed) { + DEBUG_TRACE("VxLAN outer connection, make src and dest idents same\n"); + src_port = src_port_nat = dest_port; + } +--- a/frontends/nss/ecm_nss_common.h ++++ b/frontends/nss/ecm_nss_common.h +@@ -99,7 +99,7 @@ static inline int32_t ecm_nss_common_get + /* + * Find VxLAN dev type based on type, 0 for outer & 1 for inner. + */ +- if (is_vxlan_dev(dev)) { ++ if (netif_is_vxlan(dev)) { + if (!type) { + return NSS_VXLAN_INTERFACE; + } +--- a/frontends/nss/ecm_nss_multicast_ipv4.c ++++ b/frontends/nss/ecm_nss_multicast_ipv4.c +@@ -2789,7 +2789,7 @@ unsigned int ecm_nss_multicast_ipv4_conn + if ((in_dev->type == ECM_ARPHRD_IPSEC_TUNNEL_TYPE) || + (in_dev->type == ARPHRD_SIT) || (in_dev->type == ARPHRD_PPP) || + (in_dev->type == ARPHRD_TUNNEL6) || +- (is_vxlan_dev(in_dev)) || (is_vxlan_dev(out_dev))) { ++ (netif_is_vxlan(in_dev)) || (netif_is_vxlan(out_dev))) { + DEBUG_TRACE("in_dev: %p, in_type: %d, out_dev: %p, out_type: %d", + in_dev, in_dev->type, out_dev, out_dev->type); + return NF_ACCEPT; +--- a/frontends/nss/ecm_nss_ipv6.c ++++ b/frontends/nss/ecm_nss_ipv6.c +@@ -1086,10 +1086,10 @@ static unsigned int ecm_nss_ipv6_ip_proc + * Eth1 ---> Bridge ---> VxLAN0(Bridge Port) ---> Eth0(WAN) + * The packets from VxLAN0 to Eth0 will be routed. + * +- * is_vxlan_dev API is used to identify the VxLAN device & ++ * netif_is_vxlan API is used to identify the VxLAN device & + * is_routed flag is used to identify the outer flow. + */ +- if (is_routed && is_vxlan_dev(in_dev)) { ++ if (is_routed && netif_is_vxlan(in_dev)) { + DEBUG_TRACE("%p: Untracked CT for VxLAN\n", skb); + ECM_IP_ADDR_TO_NIN6_ADDR(orig_tuple.src.u3.in6, ip_hdr.src_addr); + ECM_IP_ADDR_TO_NIN6_ADDR(orig_tuple.dst.u3.in6, ip_hdr.dest_addr); +--- a/frontends/nss/ecm_nss_ported_ipv6.c ++++ b/frontends/nss/ecm_nss_ported_ipv6.c +@@ -2037,7 +2037,7 @@ unsigned int ecm_nss_ported_ipv6_process + } + + #ifdef ECM_INTERFACE_VXLAN_ENABLE +- if ((is_vxlan_dev(in_dev) || is_vxlan_dev(out_dev)) && is_routed) { ++ if ((netif_is_vxlan(in_dev) || netif_is_vxlan(out_dev)) && is_routed) { + DEBUG_TRACE("VxLAN outer connection, make src and dest idents same.\n"); + src_port = dest_port; + } +--- a/frontends/nss/ecm_nss_ipv4.c ++++ b/frontends/nss/ecm_nss_ipv4.c +@@ -1096,10 +1096,10 @@ static unsigned int ecm_nss_ipv4_ip_proc + * Eth1 ---> Bridge ---> VxLAN0(Bridge Port) ---> Eth0(WAN) + * The packets from VxLAN0 to Eth0 will be routed. + * +- * is_vxlan_dev API is used to identify the VxLAN device & ++ * netif_is_vxlan API is used to identify the VxLAN device & + * is_routed flag is used to identify the outer flow. + */ +- if (is_routed && is_vxlan_dev(in_dev)) { ++ if (is_routed && netif_is_vxlan(in_dev)) { + DEBUG_TRACE("%p: Untracked CT for VxLAN\n", skb); + ECM_IP_ADDR_TO_NIN4_ADDR(orig_tuple.src.u3.ip, ip_hdr.src_addr); + ECM_IP_ADDR_TO_NIN4_ADDR(orig_tuple.dst.u3.ip, ip_hdr.dest_addr); +--- a/frontends/nss/ecm_nss_multicast_ipv6.c ++++ b/frontends/nss/ecm_nss_multicast_ipv6.c +@@ -2684,7 +2684,7 @@ unsigned int ecm_nss_multicast_ipv6_conn + if ((in_dev->type == ECM_ARPHRD_IPSEC_TUNNEL_TYPE) || + (in_dev->type == ARPHRD_SIT) || (in_dev->type == ARPHRD_PPP) || + (in_dev->type == ARPHRD_TUNNEL6) || +- (is_vxlan_dev(in_dev)) || (is_vxlan_dev(out_dev))) { ++ (netif_is_vxlan(in_dev)) || (netif_is_vxlan(out_dev))) { + DEBUG_TRACE("in_dev: %p, in_type: %d, out_dev: %p, out_type: %d", + in_dev, in_dev->type, out_dev, out_dev->type); + return NF_ACCEPT; diff --git a/package/qca/qca-nss-ecm/patches/102-fix-multicast-bridge-route-issue.patch b/package/qca/qca-nss-ecm/patches/102-fix-multicast-bridge-route-issue.patch new file mode 100644 index 00000000000000..160360547d98a1 --- /dev/null +++ b/package/qca/qca-nss-ecm/patches/102-fix-multicast-bridge-route-issue.patch @@ -0,0 +1,504 @@ +[qca-nss-ecm] Fix multicast "bridge + route" issues +1. Fix for upstream interface not receiving traffic. +2. Apply the fix for both Linux and OVS bridge. + +Signed-off-by: Suman Ghosh +Change-Id: Id90d48fc2e1fa190e753fa36f363e77d346dea24 + +diff --git a/ecm_interface.c b/ecm_interface.c +index a4b625c..9a69259 100644 +--- a/ecm_interface.c ++++ b/ecm_interface.c +@@ -3892,9 +3892,17 @@ int32_t ecm_interface_multicast_heirarchy_construct_routed(struct ecm_front_end_ + + /* + * Check if the source net_dev is a bridge slave. ++ * ++ * TODO: We are already considering ingress bridge device and ++ * adding it to dst_dev in ecm_nss_multicast_ipv4_connection_process(). ++ * Check if this can be removed. + */ + if (in_dev && !mfc_update) { +- if (ecm_front_end_is_bridge_port(in_dev)) { ++ if (ecm_front_end_is_bridge_port(in_dev) ++#ifdef ECM_INTERFACE_OVS_BRIDGE_ENABLE ++ || ecm_interface_is_ovs_bridge_port(in_dev) ++#endif ++ ) { + br_dev_src = ecm_interface_get_and_hold_dev_master(in_dev); + DEBUG_ASSERT(br_dev_src, "Expected a master\n"); + +@@ -3914,7 +3922,6 @@ int32_t ecm_interface_multicast_heirarchy_construct_routed(struct ecm_front_end_ + */ + max_if++; + } +- + } + } + +@@ -6740,8 +6747,11 @@ static bool ecm_interface_multicast_find_outdated_iface_instances(struct ecm_db_ + * If the update was received from MFC, do not consider entries in the + * interface list that are part of a bridge/ovs_bridge. The bridge/ovs_bridge entries will be + * taken care by the Bridge Snooper Callback ++ * ++ * TODO: Check if an assert is needed for the flag ++ * ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG to be set, if is_br_snooper is false. + */ +- if (!is_br_snooper && !(flags & ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG)) { ++ if (!is_br_snooper && (flags & ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG)) { + continue; + } + +diff --git a/frontends/nss/ecm_nss_multicast_ipv4.c b/frontends/nss/ecm_nss_multicast_ipv4.c +index 43d622e..d34746d 100644 +--- a/frontends/nss/ecm_nss_multicast_ipv4.c ++++ b/frontends/nss/ecm_nss_multicast_ipv4.c +@@ -2847,50 +2847,87 @@ unsigned int ecm_nss_multicast_ipv4_connection_process(struct net_device *out_de + DEBUG_WARN("Not found a valid vif count %d\n", if_cnt); + return NF_ACCEPT; + } ++ } ++ ++ /* ++ * For "bridge + route" scenario, we can receive packet from both bridge and route hook. ++ * If MFC returns valid interface count (if_cnt), then we need to check for "bridge + route" ++ * scenario. ++ */ ++ if (if_cnt > 0) { ++ is_routed = true; + + /* + * Check for the presence of a bridge device in the destination + * interface list given to us by MFC + */ + br_dev_found_in_mfc = ecm_interface_multicast_check_for_br_dev(dst_dev, if_cnt); +- } else { +- if (if_cnt > 0) { +- /* +- * In case of Bridge + Route there is chance that Bridge post routing hook called first and +- * is_route flag is false. To make sure this is a routed flow, query the MFC and if MFC if_cnt +- * is not Zero than this is a routed flow. +- */ +- is_routed = true; +- br_dev_found_in_mfc = ecm_interface_multicast_check_for_br_dev(dst_dev, if_cnt); +- } else { +- out_dev_master = ecm_interface_get_and_hold_dev_master(out_dev); +- DEBUG_ASSERT(out_dev_master, "Expected a master\n"); ++ ++ /* ++ * We are processing a routed multicast flow. ++ * Check if the source interface is a bridge device. If this is the case, ++ * this flow could be a "bridge + route" flow. ++ * So, we query the bridge device as well for possible joinees ++ */ ++ if (ecm_front_end_is_bridge_device(in_dev) ++#ifdef ECM_INTERFACE_OVS_BRIDGE_ENABLE ++ || ecm_front_end_is_ovs_bridge_device(in_dev) ++#endif ++ ) { ++ int32_t if_cnt_bridge; ++ uint32_t dst_dev_bridge[ECM_DB_MULTICAST_IF_MAX]; ++ ++ memset(dst_dev_bridge, 0, sizeof(dst_dev_bridge)); ++ if_cnt_bridge = mc_bridge_ipv4_get_if(in_dev, ip_src, ip_grp, ECM_DB_MULTICAST_IF_MAX, dst_dev_bridge); ++ if (if_cnt_bridge <= 0) { ++ DEBUG_WARN("No bridge ports have joined multicast group\n"); ++ goto process_packet; ++ } + + /* +- * Packet flow is pure bridge. Try to query the snooper for the destination +- * interface list ++ * Check for max interface limit. + */ +- if_cnt = mc_bridge_ipv4_get_if(out_dev_master, ip_src, ip_grp, ECM_DB_MULTICAST_IF_MAX, dst_dev); +- if (if_cnt <= 0) { +- DEBUG_WARN("Not found a valid MCS if count %d\n", if_cnt); ++ if (if_cnt == ECM_DB_MULTICAST_IF_MAX) { ++ DEBUG_WARN("Interface count reached max limit: %d. Could not handle the connection", if_cnt); + goto done; + } + + /* +- * The source interface could have joined the group as well. +- * In such cases, the destination interface list returned by +- * the snooper would include the source interface as well. +- * We need to filter the source interface from the list in such cases. ++ * Update the dst_dev with in_dev as it is a bridge + route case + */ +- if_cnt = ecm_interface_multicast_check_for_src_ifindex(dst_dev, if_cnt, in_dev->ifindex); +- if (if_cnt <= 0) { +- DEBUG_WARN("Not found a valid MCS if count %d\n", if_cnt); +- goto done; +- } ++ dst_dev[if_cnt++] = in_dev->ifindex; ++ br_dev_found_in_mfc = true; + } ++ ++ goto process_packet; ++ } ++ ++ /* ++ * Packet flow is pure bridge. Try to query the snooper for the destination ++ * interface list ++ */ ++ out_dev_master = ecm_interface_get_and_hold_dev_master(out_dev); ++ DEBUG_ASSERT(out_dev_master, "Expected a master\n"); ++ if_cnt = mc_bridge_ipv4_get_if(out_dev_master, ip_src, ip_grp, ECM_DB_MULTICAST_IF_MAX, dst_dev); ++ if (if_cnt <= 0) { ++ DEBUG_WARN("Not found a valid MCS if count %d\n", if_cnt); ++ goto done; + } + + /* ++ * The source interface could have joined the group as well. ++ * In such cases, the destination interface list returned by ++ * the snooper would include the source interface as well. ++ * We need to filter the source interface from the list in such cases. ++ */ ++ if_cnt = ecm_interface_multicast_check_for_src_ifindex(dst_dev, if_cnt, in_dev->ifindex); ++ if (if_cnt <= 0) { ++ DEBUG_WARN("Not found a valid MCS if count %d\n", if_cnt); ++ goto done; ++ } ++ ++process_packet: ++ /* + * In pure bridge flow, do not process further if TTL is less than two. + */ + if (!is_routed) { +@@ -3307,10 +3344,6 @@ unsigned int ecm_nss_multicast_ipv4_connection_process(struct net_device *out_de + * Add the tuple instance and attach it with connection instance + */ + ecm_db_multicast_tuple_instance_add(tuple_instance, nci); +- if (br_dev_found_in_mfc) { +- ecm_db_multicast_tuple_instance_flags_set(tuple_instance, ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG); +- } +- + spin_unlock_bh(&ecm_nss_ipv4_lock); + + ecm_db_multicast_tuple_instance_deref(tuple_instance); +@@ -3332,15 +3365,29 @@ unsigned int ecm_nss_multicast_ipv4_connection_process(struct net_device *out_de + kfree(to_list); + kfree(to_list_first); + } else { +- bool is_dest_interface_list_empty; ++ bool is_dest_interface_list_empty, routed; ++ ++ is_dest_interface_list_empty = ecm_db_multicast_connection_to_interfaces_set_check(ci); ++ routed = ecm_db_connection_is_routed_get(ci); ++ ++ /* ++ * Check if there was an update to the 'routed' status for an existing flow. ++ * This can happen if the flow is a bridge+route flow, and the MFC rule was not added ++ * at the time the flow was originally created when the packet was processed by ++ * the bridge hook. In this case, we defunct the flow to re-create it again ++ */ ++ if (routed != is_routed) { ++ ecm_db_connection_make_defunct(ci); ++ ecm_db_connection_deref(ci); ++ goto done; ++ } + + /* +- * At this pont the feci->accel_mode is DEACEL because the MC connection has expired ++ * At this point the feci->accel_mode is DECEL because the MC connection has expired + * and we had received a callback from MFC which had freed the multicast destination +- * interface heirarchy. In this case, we reconstruct the multicastdestination interface ++ * interface heirarchy. In this case, we reconstruct the multicast destination interface + * heirarchy and re-accelerate the connection. + */ +- is_dest_interface_list_empty = ecm_db_multicast_connection_to_interfaces_set_check(ci); + if (!is_dest_interface_list_empty) { + struct ecm_db_iface_instance *to_list; + struct ecm_db_iface_instance *to_list_temp[ECM_DB_IFACE_HEIRARCHY_MAX]; +@@ -3374,8 +3421,8 @@ unsigned int ecm_nss_multicast_ipv4_connection_process(struct net_device *out_de + + feci = ecm_db_connection_front_end_get_and_ref(ci); + interface_idx_cnt = ecm_nss_multicast_connection_to_interface_heirarchy_construct(feci, to_list, ip_src_addr, ip_dest_addr, in_dev, +- out_dev_master, if_cnt, dst_dev, to_list_first, +- src_node_addr, is_routed, (__be16 *)&udp_hdr, skb); ++ out_dev_master, if_cnt, dst_dev, to_list_first, ++ src_node_addr, is_routed, (__be16 *)&udp_hdr, skb); + feci->deref(feci); + if (interface_idx_cnt == 0) { + DEBUG_WARN("Failed to reconstruct 'to mc' heirarchy list\n"); +@@ -3397,23 +3444,6 @@ unsigned int ecm_nss_multicast_ipv4_connection_process(struct net_device *out_de + ecm_db_connection_interfaces_deref(to_list_temp, *to_first); + } + +- /* +- * if a bridge dev is present in the MFC destination then set the +- * ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG in tuple_instance +- */ +- if (br_dev_found_in_mfc) { +- struct ecm_db_multicast_tuple_instance *tuple_instance; +- tuple_instance = ecm_db_multicast_connection_find_and_ref(ip_src_addr, ip_dest_addr); +- if (!tuple_instance) { +- ecm_db_connection_deref(ci); +- kfree(to_list); +- kfree(to_list_first); +- goto done; +- } +- +- ecm_db_multicast_tuple_instance_flags_set(tuple_instance, ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG); +- ecm_db_multicast_connection_deref(tuple_instance); +- } + kfree(to_list); + kfree(to_list_first); + +@@ -3428,6 +3458,22 @@ unsigned int ecm_nss_multicast_ipv4_connection_process(struct net_device *out_de + } + } + ++ /* ++ * if a bridge dev is present in the MFC destination then set the ++ * ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG in tuple_instance ++ */ ++ if (br_dev_found_in_mfc) { ++ struct ecm_db_multicast_tuple_instance *tuple_instance; ++ tuple_instance = ecm_db_multicast_connection_find_and_ref(ip_src_addr, ip_dest_addr); ++ if (!tuple_instance) { ++ ecm_db_connection_deref(ci); ++ goto done; ++ } ++ ++ ecm_db_multicast_tuple_instance_flags_set(tuple_instance, ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG); ++ ecm_db_multicast_connection_deref(tuple_instance); ++ } ++ + #ifdef CONFIG_NET_CLS_ACT + /* + * Check if IGS feature is enabled or not. +diff --git a/frontends/nss/ecm_nss_multicast_ipv6.c b/frontends/nss/ecm_nss_multicast_ipv6.c +index c34ca21..93009d4 100644 +--- a/frontends/nss/ecm_nss_multicast_ipv6.c ++++ b/frontends/nss/ecm_nss_multicast_ipv6.c +@@ -2737,52 +2737,87 @@ unsigned int ecm_nss_multicast_ipv6_connection_process(struct net_device *out_de + DEBUG_WARN("Not found a valid vif count %d\n", mc_if_cnt); + return NF_ACCEPT; + } ++ } ++ ++ /* ++ * For "bridge + route" scenario, we can receive packet from both bridge and route hook. ++ * If MFC returns valid interface count (mc_if_cnt), then we need to check for "bridge + route" ++ * scenario. ++ */ ++ if (mc_if_cnt > 0) { ++ is_routed = true; + + /* + * Check for the presence of a bridge device in the destination + * interface list given to us by MFC + */ +- + br_dev_found_in_mfc = ecm_interface_multicast_check_for_br_dev(mc_dest_if, mc_if_cnt); +- } else { +- if (mc_if_cnt > 0) { + +- /* +- * In case of Bridge + Route there is chance that Bridge post routing hook called first and +- * is_route flag is false. To make sure this is a routed flow, query the MFC and if MFC if_cnt +- * is not Zero than this is a routed flow. +- */ +- is_routed = true; +- br_dev_found_in_mfc = ecm_interface_multicast_check_for_br_dev(mc_dest_if, mc_if_cnt); +- } else { +- out_dev_master = ecm_interface_get_and_hold_dev_master(out_dev); +- DEBUG_ASSERT(out_dev_master, "Expected a master\n"); ++ /* ++ * We are processing a routed multicast flow. ++ * Check if the source interface is a bridge device. If this is the case, ++ * this flow could be a "bridge + route" flow. ++ * So, we query the bridge device as well for possible joinees ++ */ ++ if (ecm_front_end_is_bridge_device(in_dev) ++#ifdef ECM_INTERFACE_OVS_BRIDGE_ENABLE ++ || ecm_front_end_is_ovs_bridge_device(in_dev) ++#endif ++ ) { ++ int32_t mc_if_cnt_bridge; ++ uint32_t dst_dev_bridge[ECM_DB_MULTICAST_IF_MAX]; ++ ++ memset(dst_dev_bridge, 0, sizeof(dst_dev_bridge)); ++ mc_if_cnt_bridge = mc_bridge_ipv6_get_if(in_dev, &origin6, &group6, ECM_DB_MULTICAST_IF_MAX, dst_dev_bridge); ++ if (mc_if_cnt_bridge <= 0) { ++ DEBUG_WARN("%p: No bridge ports have joined multicast group\n", ci); ++ goto process_packet; ++ } + + /* +- * Packet flow is pure bridge. Try to query the snooper for the destination +- * interface list ++ * Check for max interface limit. + */ +- mc_if_cnt = mc_bridge_ipv6_get_if(out_dev_master, &origin6, &group6, ECM_DB_MULTICAST_IF_MAX, mc_dest_if); +- if (mc_if_cnt <= 0) { +- DEBUG_WARN("Not found a valid MCS if count %d\n", mc_if_cnt); ++ if (mc_if_cnt == ECM_DB_MULTICAST_IF_MAX) { ++ DEBUG_WARN("Interface count reached max limit: %d. Could not handle the connection", mc_if_cnt); + goto done; + } + + /* +- * The source interface could have joined the group as well. +- * In such cases, the destination interface list returned by +- * the snooper would include the source interface as well. +- * We need to filter the source interface from the list in such cases. ++ * Update the dst_dev with in_dev as it is a bridge + route case + */ +- mc_if_cnt = ecm_interface_multicast_check_for_src_ifindex(mc_dest_if, mc_if_cnt, in_dev->ifindex); +- if (mc_if_cnt <= 0) { +- DEBUG_WARN("Not found a valid MCS if count %d\n", mc_if_cnt); +- goto done; +- } ++ mc_dest_if[mc_if_cnt++] = in_dev->ifindex; ++ br_dev_found_in_mfc = true; + } ++ ++ goto process_packet; ++ } ++ ++ /* ++ * Packet flow is pure bridge. Try to query the snooper for the destination ++ * interface list ++ */ ++ out_dev_master = ecm_interface_get_and_hold_dev_master(out_dev); ++ DEBUG_ASSERT(out_dev_master, "Expected a master\n"); ++ mc_if_cnt = mc_bridge_ipv6_get_if(out_dev_master, &origin6, &group6, ECM_DB_MULTICAST_IF_MAX, mc_dest_if); ++ if (mc_if_cnt <= 0) { ++ DEBUG_WARN("Not found a valid MCS if count %d\n", mc_if_cnt); ++ goto done; + } + + /* ++ * The source interface could have joined the group as well. ++ * In such cases, the destination interface list returned by ++ * the snooper would include the source interface as well. ++ * We need to filter the source interface from the list in such cases. ++ */ ++ mc_if_cnt = ecm_interface_multicast_check_for_src_ifindex(mc_dest_if, mc_if_cnt, in_dev->ifindex); ++ if (mc_if_cnt <= 0) { ++ DEBUG_WARN("Not found a valid MCS if count %d\n", mc_if_cnt); ++ goto done; ++ } ++ ++process_packet: ++ /* + * In pure bridge flow, do not process further if Hop Limit is less than two. + */ + if (!is_routed) { +@@ -2947,7 +2982,7 @@ unsigned int ecm_nss_multicast_ipv6_connection_process(struct net_device *out_de + } + + interface_idx_cnt = ecm_nss_multicast_ipv6_interface_heirarchy_construct(feci, to_list, in_dev, out_dev_master, ip_src_addr, +- ip_dest_addr, mc_if_cnt, mc_dest_if, to_list_first, src_node_addr, is_routed, skb); ++ ip_dest_addr, mc_if_cnt, mc_dest_if, to_list_first, src_node_addr, is_routed, skb); + if (interface_idx_cnt == 0) { + DEBUG_WARN("Failed to obtain 'to' heirarchy list\n"); + ecm_db_mapping_deref(mi[ECM_DB_OBJ_DIR_FROM]); +@@ -3111,10 +3146,6 @@ unsigned int ecm_nss_multicast_ipv6_connection_process(struct net_device *out_de + * Add the tuple instance and attach it with connection instance + */ + ecm_db_multicast_tuple_instance_add(tuple_instance, nci); +- if (br_dev_found_in_mfc) { +- ecm_db_multicast_tuple_instance_flags_set(tuple_instance, ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG); +- } +- + spin_unlock_bh(&ecm_nss_ipv6_lock); + + ecm_db_multicast_tuple_instance_deref(tuple_instance); +@@ -3135,15 +3166,29 @@ unsigned int ecm_nss_multicast_ipv6_connection_process(struct net_device *out_de + kfree(to_list_first); + + } else { +- bool is_dest_interface_list_empty; ++ bool is_dest_interface_list_empty, routed; ++ ++ is_dest_interface_list_empty = ecm_db_multicast_connection_to_interfaces_set_check(ci); ++ routed = ecm_db_connection_is_routed_get(ci); ++ ++ /* ++ * Check if there was an update to the 'routed' status for an existing flow. ++ * This can happen if the flow is a bridge+route flow, and the MFC rule was not added ++ * at the time the flow was originally created when the packet was processed by the ++ * bridge hook. In this case, we defunct the flow to re-create it again. ++ */ ++ if (routed != is_routed) { ++ ecm_db_connection_make_defunct(ci); ++ ecm_db_connection_deref(ci); ++ goto done; ++ } + + /* +- * At this pont the feci->accel_mode is ECM_FRONT_END_ACCELERATION_MODE_DEACCEL because the ++ * At this point the feci->accel_mode is DECEL because the + * MC connection has expired and we had received a callback from MFC which had freed the + * multicast destination interface heirarchy. In this case, we reconstruct the multicast + * destination interface heirarchy and re-accelerate the connection. + */ +- is_dest_interface_list_empty = ecm_db_multicast_connection_to_interfaces_set_check(ci); + if (!is_dest_interface_list_empty) { + struct ecm_db_iface_instance *to_list; + struct ecm_db_iface_instance *to_list_temp[ECM_DB_IFACE_HEIRARCHY_MAX]; +@@ -3174,9 +3219,9 @@ unsigned int ecm_nss_multicast_ipv6_connection_process(struct net_device *out_de + + feci = ecm_db_connection_front_end_get_and_ref(ci); + interface_idx_cnt = ecm_nss_multicast_ipv6_interface_heirarchy_construct(feci, to_list, in_dev, out_dev_master,\ +- ip_src_addr, ip_dest_addr, mc_if_cnt,\ +- mc_dest_if, to_list_first, src_node_addr, +- is_routed, skb); ++ ip_src_addr, ip_dest_addr, mc_if_cnt,\ ++ mc_dest_if, to_list_first, src_node_addr, ++ is_routed, skb); + feci->deref(feci); + if (interface_idx_cnt == 0) { + DEBUG_WARN("Failed to reconstruct 'to mc' heirarchy list\n"); +@@ -3198,23 +3243,6 @@ unsigned int ecm_nss_multicast_ipv6_connection_process(struct net_device *out_de + ecm_db_connection_interfaces_deref(to_list_temp, *to_first); + } + +- /* +- * if a bridge dev is present in the MFC destination then set the +- * ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG in tuple_instance +- */ +- if (br_dev_found_in_mfc) { +- struct ecm_db_multicast_tuple_instance *tuple_instance; +- tuple_instance = ecm_db_multicast_connection_find_and_ref(ip_src_addr, ip_dest_addr); +- if (!tuple_instance) { +- ecm_db_connection_deref(ci); +- kfree(to_list); +- kfree(to_list_first); +- goto done; +- } +- +- ecm_db_multicast_tuple_instance_flags_set(tuple_instance, ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG); +- ecm_db_multicast_connection_deref(tuple_instance); +- } + kfree(to_list); + kfree(to_list_first); + +@@ -3229,6 +3257,22 @@ unsigned int ecm_nss_multicast_ipv6_connection_process(struct net_device *out_de + } + } + ++ /* ++ * if a bridge dev is present in the MFC destination then set the ++ * ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG in tuple_instance ++ */ ++ if (br_dev_found_in_mfc) { ++ struct ecm_db_multicast_tuple_instance *tuple_instance; ++ tuple_instance = ecm_db_multicast_connection_find_and_ref(ip_src_addr, ip_dest_addr); ++ if (!tuple_instance) { ++ ecm_db_connection_deref(ci); ++ goto done; ++ } ++ ++ ecm_db_multicast_tuple_instance_flags_set(tuple_instance, ECM_DB_MULTICAST_CONNECTION_BRIDGE_DEV_SET_FLAG); ++ ecm_db_multicast_connection_deref(tuple_instance); ++ } ++ + #ifdef CONFIG_NET_CLS_ACT + /* + * Check if IGS feature is enabled or not. diff --git a/package/qca/qca-nss-ecm/patches/103-allow-egress-bridge-hairpin.patch b/package/qca/qca-nss-ecm/patches/103-allow-egress-bridge-hairpin.patch new file mode 100644 index 00000000000000..23c2aecb3be3a7 --- /dev/null +++ b/package/qca/qca-nss-ecm/patches/103-allow-egress-bridge-hairpin.patch @@ -0,0 +1,106 @@ +[qca-nss-ecm] Allow egress on same port when bridge hairpin is enabled. +When bridge hairpin is enabled, allow egress on same port. Wi-Fi intrabss +frames are getting exceptioned to stack. Bridge gets to make the decision +whether these frames need to be forwarded or dropped. + +Signed-off-by: Varsha Mishra +Change-Id: Ibdd72264d8887330ba0297ed12cbcfc390065bff + +diff --git a/frontends/nss/ecm_nss_ipv4.c b/frontends/nss/ecm_nss_ipv4.c +index 60f799b..51c9ebf 100644 +--- a/frontends/nss/ecm_nss_ipv4.c ++++ b/frontends/nss/ecm_nss_ipv4.c +@@ -1791,7 +1791,9 @@ static unsigned int ecm_nss_ipv4_bridge_post_routing_hook(void *priv, + * Case 2: + * For routed packets the skb will have the src mac matching the bridge mac. + * Case 3: +- * If the packet was not local (case 1) or routed (case 2) then we process. ++ * If the packet was not local (case 1) or routed (case 2) then ++ * we process. There is an exception to case 2: when hairpin mode ++ * is enabled, we process. + */ + + /* +@@ -1803,14 +1805,28 @@ static unsigned int ecm_nss_ipv4_bridge_post_routing_hook(void *priv, + dev_put(bridge); + return NF_ACCEPT; + } ++ ++ /* ++ * This flag needs to be checked in slave port(eth0/ath0) ++ * and not on master interface(br-lan). Hairpin flag can be ++ * enabled/disabled for ports individually. ++ */ + if (in == out) { +- DEBUG_TRACE("skb: %p, bridge: %p (%s), port bounce on %p (%s)\n", skb, bridge, bridge->name, out, out->name); +- goto skip_ipv4_bridge_flow; ++ if (!br_is_hairpin_enabled(in)) { ++ DEBUG_TRACE("skb: %p, bridge: %p (%s), ignoring" ++ "the packet, hairpin not enabled" ++ "on port %p (%s)\n", skb, bridge, ++ bridge->name, out, out->name); ++ goto skip_ipv4_bridge_flow; ++ } ++ DEBUG_TRACE("skb: %p, bridge: %p (%s), hairpin enabled on port" ++ "%p (%s)\n", skb, bridge, bridge->name, out, out->name); + } ++ ++ /* ++ * Case 2: Routed trafffic would be handled by the INET post routing. ++ */ + if (!ecm_mac_addr_equal(skb_eth_hdr->h_source, bridge->dev_addr)) { +- /* +- * Case 2: Routed trafffic would be handled by the INET post routing. +- */ + DEBUG_TRACE("skb: %p, Ignoring routed packet to bridge: %p (%s)\n", skb, bridge, bridge->name); + goto skip_ipv4_bridge_flow; + } +diff --git a/frontends/nss/ecm_nss_ipv6.c b/frontends/nss/ecm_nss_ipv6.c +index 6ad425e..160c94c 100644 +--- a/frontends/nss/ecm_nss_ipv6.c ++++ b/frontends/nss/ecm_nss_ipv6.c +@@ -1533,7 +1533,9 @@ static unsigned int ecm_nss_ipv6_bridge_post_routing_hook(void *priv, + * Case 2: + * For routed packets the skb will have the src mac matching the bridge mac. + * Case 3: +- * If the packet was not local (case 1) or routed (case 2) then we process. ++ * If the packet was not local (case 1) or routed (case 2) then ++ * we process. There is an exception to case 2: when hairpin mode ++ * is enabled, we process. + */ + + /* +@@ -1545,14 +1547,28 @@ static unsigned int ecm_nss_ipv6_bridge_post_routing_hook(void *priv, + dev_put(bridge); + return NF_ACCEPT; + } ++ ++ /* ++ * This flag needs to be checked in slave port(eth0/ath0) ++ * and not on master interface(br-lan). Hairpin flag can be ++ * enabled/disabled for ports individually. ++ */ + if (in == out) { +- DEBUG_TRACE("skb: %p, bridge: %p (%s), port bounce on %p (%s)\n", skb, bridge, bridge->name, out, out->name); +- goto skip_ipv6_bridge_flow; ++ if (!br_is_hairpin_enabled(in)) { ++ DEBUG_TRACE("skb: %p, bridge: %p (%s), ignoring" ++ "the packet, hairpin not enabled" ++ "on port %p (%s)\n", skb, bridge, ++ bridge->name, out, out->name); ++ goto skip_ipv6_bridge_flow; ++ } ++ DEBUG_TRACE("skb: %p, bridge: %p (%s), hairpin enabled on port" ++ "%p (%s)\n", skb, bridge, bridge->name, out, out->name); + } ++ ++ /* ++ * Case 2: Routed trafffic would be handled by the INET post routing. ++ */ + if (!ecm_mac_addr_equal(skb_eth_hdr->h_source, bridge->dev_addr)) { +- /* +- * Case 2: Routed trafffic would be handled by the INET post routing. +- */ + DEBUG_TRACE("skb: %p, Ignoring routed packet to bridge: %p (%s)\n", skb, bridge, bridge->name); + goto skip_ipv6_bridge_flow; + } diff --git a/package/qca/qca-nss-ecm/patches/104-do-not-process-bridged-packets-twice.patch b/package/qca/qca-nss-ecm/patches/104-do-not-process-bridged-packets-twice.patch new file mode 100644 index 00000000000000..39cc30efc3822e --- /dev/null +++ b/package/qca/qca-nss-ecm/patches/104-do-not-process-bridged-packets-twice.patch @@ -0,0 +1,58 @@ +[qca-nss-ecm] Do not process bridged packets twice +Bridged packets go through both bridge and IP post +routing hook. We should detect the packet in the IP +post routing hook and skip this process. + +Signed-off-by: Murat Sezgin +Change-Id: Icb179e72d0ac572fe3a8c0a1a0fe1b6f553792c3 + +diff --git a/frontends/nss/ecm_nss_ported_ipv4.c b/frontends/nss/ecm_nss_ported_ipv4.c +index e94da79..23b3608 100644 +--- a/frontends/nss/ecm_nss_ported_ipv4.c ++++ b/frontends/nss/ecm_nss_ported_ipv4.c +@@ -2483,6 +2483,20 @@ done: + ; + } + ++ /* ++ * Bridged traffic goes through the IP post routing hook as well after it ++ * finishes the bridge post routing hook. In that case, ecm_dir will become ++ * as Non-Nat since the is_routed flag is true. But the is_routed flag of the connection ++ * was set as false while the packet was going throught the bridge post routing ++ * hook. So, we need to check if the is_routed flag and ecm_dir matches the routed ++ * flow condition. If it doesn't, do not process the flow. ++ */ ++ if (!ecm_db_connection_is_routed_get(ci) && (ecm_dir == ECM_DB_DIRECTION_NON_NAT)) { ++ DEBUG_TRACE("%px: ignore route hook path for bridged packet\n", ci); ++ ecm_db_connection_deref(ci); ++ return NF_ACCEPT; ++ } ++ + #ifdef CONFIG_NET_CLS_ACT + /* + * Check if IGS feature is enabled or not. +diff --git a/frontends/nss/ecm_nss_ported_ipv6.c b/frontends/nss/ecm_nss_ported_ipv6.c +index aede82f..95ea7c5 100644 +--- a/frontends/nss/ecm_nss_ported_ipv6.c ++++ b/frontends/nss/ecm_nss_ported_ipv6.c +@@ -2299,6 +2299,20 @@ done: + ; + } + ++ /* ++ * Bridged traffic goes through the IP post routing hook as well after it ++ * finishes the bridge post routing hook. In that case, ecm_dir will become ++ * as Non-Nat since the is_routed flag is true. But the is_routed flag of the connection ++ * was set as false while the packet was going throught the bridge post routing ++ * hook. So, we need to check if the is_routed flag and ecm_dir matches the routed ++ * flow condition. If it doesn't, do not process the flow. ++ */ ++ if (!ecm_db_connection_is_routed_get(ci) && (ecm_dir == ECM_DB_DIRECTION_NON_NAT)) { ++ DEBUG_TRACE("%px: ignore route hook path for bridged packet\n", ci); ++ ecm_db_connection_deref(ci); ++ return NF_ACCEPT; ++ } ++ + #ifdef CONFIG_NET_CLS_ACT + /* + * Check if IGS feature is enabled or not. diff --git a/package/qca/qca-nss-ecm/patches/105-fix-tun6rd-peer-update-issue.patch b/package/qca/qca-nss-ecm/patches/105-fix-tun6rd-peer-update-issue.patch new file mode 100644 index 00000000000000..c6e8ef6800a4d4 --- /dev/null +++ b/package/qca/qca-nss-ecm/patches/105-fix-tun6rd-peer-update-issue.patch @@ -0,0 +1,19 @@ +[qca-nss-ecm] Fix for TUN6RD peer update issue. +Updated the endianness of the peer IPv4 address. + +Change-Id: I0403ef1572d8e089d59823dfaf51615be2654711 +Signed-off-by: Apoorv Gupta + +diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.c b/frontends/nss/ecm_nss_non_ported_ipv4.c +index 0c81243..8fc13a6 100644 +--- a/frontends/nss/ecm_nss_non_ported_ipv4.c ++++ b/frontends/nss/ecm_nss_non_ported_ipv4.c +@@ -175,7 +175,7 @@ static void ecm_nss_non_ported_ipv4_sit_set_peer(struct ecm_nss_non_ported_ipv4_ + sizeof(struct nss_tun6rd_set_peer_msg), NULL, NULL); + + tun6rdpeer = &tun6rdmsg.msg.peer; +- ECM_IP_ADDR_TO_NIN4_ADDR(tun6rdpeer->dest, addr); ++ ECM_IP_ADDR_TO_HIN4_ADDR(tun6rdpeer->dest, addr); + iph6 = (struct ipv6hdr *)skb_inner_network_header(skb); + memcpy(tun6rdpeer->ipv6_address, &iph6->daddr, sizeof(struct in6_addr)); + diff --git a/package/qca/qca-nss-ecm/patches/200-resolve-high-load.patch b/package/qca/qca-nss-ecm/patches/200-resolve-high-load.patch index 2f39d2770e637d..b3cb092e34655c 100644 --- a/package/qca/qca-nss-ecm/patches/200-resolve-high-load.patch +++ b/package/qca/qca-nss-ecm/patches/200-resolve-high-load.patch @@ -4,7 +4,7 @@ task should be interruptible. --- a/frontends/nss/ecm_nss_ipv4.c +++ b.frontends/nss/ecm_nss_ipv4.c -@@ -2411,7 +2411,7 @@ static void ecm_nss_ipv4_stats_sync_req_ +@@ -2427,7 +2427,7 @@ static void ecm_nss_ipv4_stats_sync_req_ } spin_unlock_bh(&ecm_nss_ipv4_lock); @@ -13,7 +13,7 @@ task should be interruptible. /* * If index is 0, we are starting a new round, but if we still have time remain -@@ -2425,7 +2425,7 @@ static void ecm_nss_ipv4_stats_sync_req_ +@@ -2441,7 +2441,7 @@ static void ecm_nss_ipv4_stats_sync_req_ } if (ecm_nss_ipv4_next_req_time > current_jiffies) { @@ -24,7 +24,7 @@ task should be interruptible. ecm_nss_ipv4_next_req_time = ecm_nss_ipv4_roll_check_jiffies + ECM_NSS_IPV4_STATS_SYNC_PERIOD; --- a/frontends/nss/ecm_nss_ipv6.c +++ b.frontends/nss/ecm_nss_ipv6.c -@@ -2128,7 +2128,7 @@ static void ecm_nss_ipv6_stats_sync_req_ +@@ -2144,7 +2144,7 @@ static void ecm_nss_ipv6_stats_sync_req_ } spin_unlock_bh(&ecm_nss_ipv6_lock); @@ -33,7 +33,7 @@ task should be interruptible. /* * If index is 0, we are starting a new round, but if we still have time remain -@@ -2142,7 +2142,7 @@ static void ecm_nss_ipv6_stats_sync_req_ +@@ -2158,7 +2158,7 @@ static void ecm_nss_ipv6_stats_sync_req_ } if (ecm_nss_ipv6_next_req_time > current_jiffies) { diff --git a/package/qca/qca-nss-ecm/patches/202-fix-null-pointer-exception.patch b/package/qca/qca-nss-ecm/patches/202-fix-null-pointer-exception.patch index 85230c2950beb5..2dfcf67f426b85 100644 --- a/package/qca/qca-nss-ecm/patches/202-fix-null-pointer-exception.patch +++ b/package/qca/qca-nss-ecm/patches/202-fix-null-pointer-exception.patch @@ -1,6 +1,6 @@ --- a/frontends/nss/ecm_nss_ipv4.c +++ b/frontends/nss/ecm_nss_ipv4.c -@@ -2324,6 +2324,8 @@ sync_conntrack: +@@ -2340,6 +2340,8 @@ sync_conntrack: set_bit(IPS_ASSURED_BIT, &ct->status); timeouts = nf_ct_timeout_lookup(ct); @@ -11,7 +11,7 @@ ct->timeout = jiffies + timeouts[UDP_CT_REPLIED]; --- a/frontends/nss/ecm_nss_ipv6.c +++ b/frontends/nss/ecm_nss_ipv6.c -@@ -2042,6 +2042,8 @@ sync_conntrack: +@@ -2058,6 +2058,8 @@ sync_conntrack: set_bit(IPS_ASSURED_BIT, &ct->status); timeouts = nf_ct_timeout_lookup(ct);