diff --git a/dp/inc/cdp_txrx_ops.h b/dp/inc/cdp_txrx_ops.h index 0574155582..c659887d50 100644 --- a/dp/inc/cdp_txrx_ops.h +++ b/dp/inc/cdp_txrx_ops.h @@ -571,6 +571,9 @@ struct cdp_wds_ops { void (*txrx_set_wds_rx_policy)(struct cdp_vdev *vdev, u_int32_t val); + void + (*txrx_wds_peer_tx_policy_update)(struct cdp_peer *peer, + int wds_tx_ucast, int wds_tx_mcast); int (*vdev_set_wds)(void *vdev, uint32_t val); }; diff --git a/dp/inc/cdp_txrx_wds.h b/dp/inc/cdp_txrx_wds.h index 1b4a144f41..8e1a9c5dcb 100644 --- a/dp/inc/cdp_txrx_wds.h +++ b/dp/inc/cdp_txrx_wds.h @@ -58,6 +58,33 @@ cdp_set_wds_rx_policy(ol_txrx_soc_handle soc, return; } +/** + * @brief set the wds rx filter policy of the device + * @details + * This flag sets the wds rx policy on the vdev. Rx frames not compliant + * with the policy will be dropped. + * + * @param vdev - the data virtual device object + * @param val - the wds rx policy bitmask + * @return - void + */ +static inline void +cdp_set_wds_tx_policy_update(ol_txrx_soc_handle soc, + struct cdp_peer *peer, + int wds_tx_ucast, int wds_tx_mcast) +{ + if (!soc || !soc->ops || !soc->ops->wds_ops) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_FATAL, + "%s invalid instance", __func__); + return; + } + + if (soc->ops->wds_ops->txrx_wds_peer_tx_policy_update) + return soc->ops->wds_ops->txrx_wds_peer_tx_policy_update( + peer, wds_tx_ucast, wds_tx_mcast); + return; +} + /** * cdp_vdev_set_wds() - Set/unset wds_enable flag in vdev * @soc - data path soc handle diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index a26b0f6533..385a384a42 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -5328,8 +5328,81 @@ QDF_STATUS dp_update_config_parameters(struct cdp_soc *psoc, return QDF_STATUS_SUCCESS; } +/** + * dp_txrx_set_wds_rx_policy() - API to store datapath + * config parameters + * @vdev_handle - datapath vdev handle + * @cfg: ini parameter handle + * + * Return: status + */ +#ifdef WDS_VENDOR_EXTENSION +void +dp_txrx_set_wds_rx_policy( + struct cdp_vdev *vdev_handle, + u_int32_t val) +{ + struct dp_vdev *vdev = (struct dp_vdev *)vdev_handle; + struct dp_peer *peer; + if (vdev->opmode == wlan_op_mode_ap) { + /* for ap, set it on bss_peer */ + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + if (peer->bss_peer) { + peer->wds_ecm.wds_rx_filter = 1; + peer->wds_ecm.wds_rx_ucast_4addr = (val & WDS_POLICY_RX_UCAST_4ADDR) ? 1:0; + peer->wds_ecm.wds_rx_mcast_4addr = (val & WDS_POLICY_RX_MCAST_4ADDR) ? 1:0; + break; + } + } + } else if (vdev->opmode == wlan_op_mode_sta) { + peer = TAILQ_FIRST(&vdev->peer_list); + peer->wds_ecm.wds_rx_filter = 1; + peer->wds_ecm.wds_rx_ucast_4addr = (val & WDS_POLICY_RX_UCAST_4ADDR) ? 1:0; + peer->wds_ecm.wds_rx_mcast_4addr = (val & WDS_POLICY_RX_MCAST_4ADDR) ? 1:0; + } +} + +/** + * dp_txrx_peer_wds_tx_policy_update() - API to set tx wds policy + * + * @peer_handle - datapath peer handle + * @wds_tx_ucast: policy for unicast transmission + * @wds_tx_mcast: policy for multicast transmission + * + * Return: void + */ +void +dp_txrx_peer_wds_tx_policy_update(struct cdp_peer *peer_handle, + int wds_tx_ucast, int wds_tx_mcast) +{ + struct dp_peer *peer = (struct dp_peer *)peer_handle; + if (wds_tx_ucast || wds_tx_mcast) { + peer->wds_enabled = 1; + peer->wds_ecm.wds_tx_ucast_4addr = wds_tx_ucast; + peer->wds_ecm.wds_tx_mcast_4addr = wds_tx_mcast; + } else { + peer->wds_enabled = 0; + peer->wds_ecm.wds_tx_ucast_4addr = 0; + peer->wds_ecm.wds_tx_mcast_4addr = 0; + } + + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO, + FL("Policy Update set to :\ + peer->wds_enabled %d\ + peer->wds_ecm.wds_tx_ucast_4addr %d\ + peer->wds_ecm.wds_tx_mcast_4addr %d\n"), + peer->wds_enabled, peer->wds_ecm.wds_tx_ucast_4addr, + peer->wds_ecm.wds_tx_mcast_4addr); + return; +} +#endif + static struct cdp_wds_ops dp_ops_wds = { .vdev_set_wds = dp_vdev_set_wds, +#ifdef WDS_VENDOR_EXTENSION + .txrx_set_wds_rx_policy = dp_txrx_set_wds_rx_policy, + .txrx_wds_peer_tx_policy_update = dp_txrx_peer_wds_tx_policy_update, +#endif }; /* diff --git a/dp/wifi3.0/dp_rx.c b/dp/wifi3.0/dp_rx.c index ee2c631085..dd10aeaa97 100644 --- a/dp/wifi3.0/dp_rx.c +++ b/dp/wifi3.0/dp_rx.c @@ -817,7 +817,7 @@ static inline void dp_rx_adjust_nbuf_len(qdf_nbuf_t nbuf, uint16_t *mpdu_len) * */ void dp_rx_sg_create(qdf_nbuf_t nbuf, uint8_t *rx_tlv_hdr, - uint16_t *mpdu_len, bool *is_first_frag, + uint16_t *mpdu_len, bool *is_first_frag, uint16_t *frag_list_len, qdf_nbuf_t *head_frag_nbuf, qdf_nbuf_t *frag_list_head, qdf_nbuf_t *frag_list_tail) { @@ -888,6 +888,98 @@ static inline void dp_rx_deliver_to_stack(struct dp_vdev *vdev, } +#ifdef WDS_VENDOR_EXTENSION +int dp_wds_rx_policy_check( + uint8_t *rx_tlv_hdr, + struct dp_vdev *vdev, + struct dp_peer *peer, + int rx_mcast + ) +{ + struct dp_peer *bss_peer; + int fr_ds, to_ds, rx_3addr, rx_4addr; + int rx_policy_ucast, rx_policy_mcast; + + if (vdev->opmode == wlan_op_mode_ap) { + TAILQ_FOREACH(bss_peer, &vdev->peer_list, peer_list_elem) { + if (bss_peer->bss_peer) { + /* if wds policy check is not enabled on this vdev, accept all frames */ + if (!bss_peer->wds_ecm.wds_rx_filter) { + return 1; + } + break; + } + } + rx_policy_ucast = bss_peer->wds_ecm.wds_rx_ucast_4addr; + rx_policy_mcast = bss_peer->wds_ecm.wds_rx_mcast_4addr; + } else { /* sta mode */ + if (!peer->wds_ecm.wds_rx_filter) { + return 1; + } + rx_policy_ucast = peer->wds_ecm.wds_rx_ucast_4addr; + rx_policy_mcast = peer->wds_ecm.wds_rx_mcast_4addr; + } + + /* ------------------------------------------------ + * self + * peer- rx rx- + * wds ucast mcast dir policy accept note + * ------------------------------------------------ + * 1 1 0 11 x1 1 AP configured to accept ds-to-ds Rx ucast from wds peers, constraint met; so, accept + * 1 1 0 01 x1 0 AP configured to accept ds-to-ds Rx ucast from wds peers, constraint not met; so, drop + * 1 1 0 10 x1 0 AP configured to accept ds-to-ds Rx ucast from wds peers, constraint not met; so, drop + * 1 1 0 00 x1 0 bad frame, won't see it + * 1 0 1 11 1x 1 AP configured to accept ds-to-ds Rx mcast from wds peers, constraint met; so, accept + * 1 0 1 01 1x 0 AP configured to accept ds-to-ds Rx mcast from wds peers, constraint not met; so, drop + * 1 0 1 10 1x 0 AP configured to accept ds-to-ds Rx mcast from wds peers, constraint not met; so, drop + * 1 0 1 00 1x 0 bad frame, won't see it + * 1 1 0 11 x0 0 AP configured to accept from-ds Rx ucast from wds peers, constraint not met; so, drop + * 1 1 0 01 x0 0 AP configured to accept from-ds Rx ucast from wds peers, constraint not met; so, drop + * 1 1 0 10 x0 1 AP configured to accept from-ds Rx ucast from wds peers, constraint met; so, accept + * 1 1 0 00 x0 0 bad frame, won't see it + * 1 0 1 11 0x 0 AP configured to accept from-ds Rx mcast from wds peers, constraint not met; so, drop + * 1 0 1 01 0x 0 AP configured to accept from-ds Rx mcast from wds peers, constraint not met; so, drop + * 1 0 1 10 0x 1 AP configured to accept from-ds Rx mcast from wds peers, constraint met; so, accept + * 1 0 1 00 0x 0 bad frame, won't see it + * + * 0 x x 11 xx 0 we only accept td-ds Rx frames from non-wds peers in mode. + * 0 x x 01 xx 1 + * 0 x x 10 xx 0 + * 0 x x 00 xx 0 bad frame, won't see it + * ------------------------------------------------ + */ + + fr_ds = hal_rx_mpdu_get_fr_ds(rx_tlv_hdr); + to_ds = hal_rx_mpdu_get_to_ds(rx_tlv_hdr); + rx_3addr = fr_ds ^ to_ds; + rx_4addr = fr_ds & to_ds; + + if (vdev->opmode == wlan_op_mode_ap) { + if ((!peer->wds_enabled && rx_3addr && to_ds) || + (peer->wds_enabled && !rx_mcast && (rx_4addr == rx_policy_ucast)) || + (peer->wds_enabled && rx_mcast && (rx_4addr == rx_policy_mcast))) { + return 1; + } + } else { /* sta mode */ + if ((!rx_mcast && (rx_4addr == rx_policy_ucast)) || + (rx_mcast && (rx_4addr == rx_policy_mcast))) { + return 1; + } + } + return 0; +} +#else +int dp_wds_rx_policy_check( + uint8_t *rx_tlv_hdr, + struct dp_vdev *vdev, + struct dp_peer *peer, + int rx_mcast + ) +{ + return 1; +} +#endif + /** * dp_rx_process() - Brain of the Rx processing functionality * Called from the bottom half (tasklet/NET_RX_SOFTIRQ) @@ -1182,6 +1274,18 @@ done: */ dp_rx_peer_validity_check(peer); + if (!dp_wds_rx_policy_check(rx_tlv_hdr, vdev, peer, + hal_rx_msdu_end_da_is_mcbc_get(rx_tlv_hdr))) { + QDF_TRACE(QDF_MODULE_ID_DP, + QDF_TRACE_LEVEL_ERROR, + FL("Policy Check Drop pkt")); + /* Drop & free packet */ + qdf_nbuf_free(nbuf); + /* Statistics */ + nbuf = next; + continue; + } + if (qdf_unlikely(peer && peer->bss_peer)) { QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, diff --git a/dp/wifi3.0/dp_rx.h b/dp/wifi3.0/dp_rx.h index 62f51c870d..0848e13d84 100644 --- a/dp/wifi3.0/dp_rx.h +++ b/dp/wifi3.0/dp_rx.h @@ -659,4 +659,7 @@ void dp_rx_fill_mesh_stats(struct dp_vdev *vdev, qdf_nbuf_t nbuf, QDF_STATUS dp_rx_filter_mesh_packets(struct dp_vdev *vdev, qdf_nbuf_t nbuf, uint8_t *rx_tlv_hdr); +int dp_wds_rx_policy_check(uint8_t *rx_tlv_hdr, struct dp_vdev *vdev, + struct dp_peer *peer, int rx_mcast); + #endif /* _DP_RX_H */ diff --git a/dp/wifi3.0/dp_rx_err.c b/dp/wifi3.0/dp_rx_err.c index 886979e757..997aabe8fc 100644 --- a/dp/wifi3.0/dp_rx_err.c +++ b/dp/wifi3.0/dp_rx_err.c @@ -494,6 +494,16 @@ skip_mec_check: goto fail; } + if (!dp_wds_rx_policy_check(rx_desc->rx_buf_start, vdev, peer, + hal_rx_msdu_end_da_is_mcbc_get(rx_desc->rx_buf_start))) { + QDF_TRACE(QDF_MODULE_ID_DP, + QDF_TRACE_LEVEL_ERROR, + FL("mcast Policy Check Drop pkt")); + /* Drop & free packet */ + qdf_nbuf_free(nbuf); + goto fail; + } + /* WDS Source Port Learning */ if (qdf_likely(vdev->rx_decap_type == htt_cmn_pkt_type_ethernet)) dp_rx_wds_srcport_learn(soc, rx_desc->rx_buf_start, peer, nbuf); diff --git a/dp/wifi3.0/dp_tx.c b/dp/wifi3.0/dp_tx.c index dfabe432b8..0f876b8704 100644 --- a/dp/wifi3.0/dp_tx.c +++ b/dp/wifi3.0/dp_tx.c @@ -1746,6 +1746,12 @@ void dp_tx_reinject_handler(struct dp_tx_desc_s *tx_desc, uint8_t *status) struct dp_ast_entry *ast_entry = NULL; struct dp_soc *soc = NULL; struct ether_header *eh = (struct ether_header *)qdf_nbuf_data(nbuf); +#ifdef WDS_VENDOR_EXTENSION + int is_mcast = 0, is_ucast = 0; + int num_peers_3addr = 0; + struct ether_header *eth_hdr = (struct ether_header *)(qdf_nbuf_data(nbuf)); + struct ieee80211_frame_addr4 *wh = (struct ieee80211_frame_addr4 *)(qdf_nbuf_data(nbuf)); +#endif vdev = tx_desc->vdev; soc = vdev->pdev->soc; @@ -1762,7 +1768,6 @@ void dp_tx_reinject_handler(struct dp_tx_desc_s *tx_desc, uint8_t *status) DP_STATS_INC_PKT(vdev, tx_i.reinject_pkts, 1, qdf_nbuf_len(tx_desc->nbuf)); - qdf_spin_lock_bh(&(soc->ast_lock)); ast_entry = dp_peer_ast_hash_find(soc, (uint8_t *)(eh->ether_shost), 0); @@ -1771,16 +1776,53 @@ void dp_tx_reinject_handler(struct dp_tx_desc_s *tx_desc, uint8_t *status) qdf_spin_unlock_bh(&(soc->ast_lock)); +#ifdef WDS_VENDOR_EXTENSION + if (qdf_unlikely(vdev->tx_encap_type != htt_cmn_pkt_type_raw)) { + is_mcast = (IS_MULTICAST(wh->i_addr1)) ? 1 : 0; + } else { + is_mcast = (IS_MULTICAST(eth_hdr->ether_dhost)) ? 1 : 0; + } + is_ucast = !is_mcast; + + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + if (peer->bss_peer) + continue; + + /* Detect wds peers that use 3-addr framing for mcast. + * if there are any, the bss_peer is used to send the + * the mcast frame using 3-addr format. all wds enabled + * peers that use 4-addr framing for mcast frames will + * be duplicated and sent as 4-addr frames below. + */ + if (!peer->wds_enabled || !peer->wds_ecm.wds_tx_mcast_4addr) { + num_peers_3addr = 1; + break; + } + } +#endif + if (qdf_unlikely(vdev->mesh_vdev)) { DP_TX_FREE_SINGLE_BUF(vdev->pdev->soc, tx_desc->nbuf); } else { TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { if ((peer->peer_ids[0] != HTT_INVALID_PEER) && - ((peer->bss_peer && - !(vdev->osif_proxy_arp( - vdev->osif_vdev, - nbuf))) || - peer->nawds_enabled)) { +#ifdef WDS_VENDOR_EXTENSION + /* + * . if 3-addr STA, then send on BSS Peer + * . if Peer WDS enabled and accept 4-addr mcast, + * send mcast on that peer only + * . if Peer WDS enabled and accept 4-addr ucast, + * send ucast on that peer only + */ + ((peer->bss_peer && num_peers_3addr && is_mcast) || + (peer->wds_enabled && + ((is_mcast && peer->wds_ecm.wds_tx_mcast_4addr) || + (is_ucast && peer->wds_ecm.wds_tx_ucast_4addr))))) { +#else + ((peer->bss_peer && + !(vdev->osif_proxy_arp(vdev->osif_vdev, nbuf))) || + peer->nawds_enabled)) { +#endif peer_id = DP_INVALID_PEER; if (peer->nawds_enabled) { diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index e86222a80d..ed1f404cbd 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -1217,6 +1217,17 @@ enum { dp_sec_ucast }; +#ifdef WDS_VENDOR_EXTENSION +typedef struct { + uint8_t wds_tx_mcast_4addr:1, + wds_tx_ucast_4addr:1, + wds_rx_filter:1, /* enforce rx filter */ + wds_rx_ucast_4addr:1, /* when set, accept 4addr unicast frames */ + wds_rx_mcast_4addr:1; /* when set, accept 4addr multicast frames */ + +} dp_ecm_policy; +#endif + /* Peer structure for data path state */ struct dp_peer { /* VDEV to which this peer is associated */ @@ -1265,7 +1276,8 @@ struct dp_peer { /* NAWDS Flag and Bss Peer bit */ uint8_t nawds_enabled:1, bss_peer:1, - wapi:1; + wapi:1, + wds_enabled:1; /* MCL specific peer local id */ uint16_t local_id; @@ -1280,6 +1292,10 @@ struct dp_peer { TAILQ_HEAD(, dp_ast_entry) ast_entry_list; /* TBD */ + +#ifdef WDS_VENDOR_EXTENSION + dp_ecm_policy wds_ecm; +#endif }; #ifdef CONFIG_WIN