Browse Source

qcacmn: Add WDS Vendor Extension ECM Framework

Add WDS tx/rx policy checks in Tx and Rx datapaths.
In Rx path, check packets against rx policy configured
In Tx Reinject path, checks are to process or drop 4-addr/3-addr packets
to peers decisively

Change-Id: I0a6c01b7555fa5d369ab2c9baf454d49808857fc
Tallapragada Kalyan 7 years ago
parent
commit
2a5fc625d2
8 changed files with 286 additions and 8 deletions
  1. 3 0
      dp/inc/cdp_txrx_ops.h
  2. 27 0
      dp/inc/cdp_txrx_wds.h
  3. 73 0
      dp/wifi3.0/dp_main.c
  4. 105 1
      dp/wifi3.0/dp_rx.c
  5. 3 0
      dp/wifi3.0/dp_rx.h
  6. 10 0
      dp/wifi3.0/dp_rx_err.c
  7. 48 6
      dp/wifi3.0/dp_tx.c
  8. 17 1
      dp/wifi3.0/dp_types.h

+ 3 - 0
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);
 };
 

+ 27 - 0
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

+ 73 - 0
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
 };
 
 /*

+ 105 - 1
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,

+ 3 - 0
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 */

+ 10 - 0
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);

+ 48 - 6
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) {

+ 17 - 1
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