Merge 571783a2a14ea85c38edadc9a9bd8663eab33281 on remote branch

Change-Id: I5d0c22b7fe23ccdfb21508f2f6ae2fcfcf1616bb
diff --git a/ap.c b/ap.c
index 8195013..0bfa56b 100644
--- a/ap.c
+++ b/ap.c
@@ -9721,6 +9721,9 @@
 	int ret = 0;
 	int ifindex;
 
+	if (!dut->main_ifname)
+		return -1;
+
 	ifindex = if_nametoindex(dut->main_ifname);
 	if (ifindex == 0) {
 		sigma_dut_print(dut, DUT_MSG_DEBUG,
@@ -9753,6 +9756,20 @@
 	return ret;
 }
 
+
+void get_wiphy_capabilities(struct sigma_dut *dut)
+{
+	memset(&dut->hw_modes, 0, sizeof(struct dut_hw_modes));
+	if (get_driver_type(dut) == DRIVER_MAC80211 ||
+	    get_driver_type(dut) == DRIVER_LINUX_WCN) {
+		if (mac80211_get_wiphy(dut))
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"Failed to get wiphy data from the driver");
+		else
+			dut->hw_modes.valid = true;
+	}
+}
+
 #endif /* NL80211_SUPPORT */
 
 
@@ -10139,17 +10156,6 @@
 			dut->ap_dfs_mode = AP_DFS_MODE_ENABLED;
 	}
 
-	memset(&dut->hw_modes, 0, sizeof(struct dut_hw_modes));
-#ifdef NL80211_SUPPORT
-	if (get_driver_type(dut) == DRIVER_MAC80211 ||
-	    get_driver_type(dut) == DRIVER_LINUX_WCN) {
-		if (mac80211_get_wiphy(dut))
-			sigma_dut_print(dut, DUT_MSG_DEBUG,
-					"Failed to get wiphy data from the driver");
-		else
-			dut->hw_modes.valid = true;
-	}
-#endif /* NL80211_SUPPORT */
 
 	dut->ap_oper_chn = 0;
 
diff --git a/sigma_dut.c b/sigma_dut.c
index 1864ea1..c4d13aa 100644
--- a/sigma_dut.c
+++ b/sigma_dut.c
@@ -857,6 +857,9 @@
 	dut->user_config_ap_ocvc = -1;
 	dut->ap_sae_commit_status = -1;
 	dut->sta_async_twt_supp = -1;
+#ifdef ANDROID
+	dut->dscp_use_iptables = 1;
+#endif /* ANDROID */
 }
 
 
@@ -1319,6 +1322,7 @@
 
 #ifdef NL80211_SUPPORT
 	sigma_dut.nl_ctx = nl80211_init(&sigma_dut);
+	get_wiphy_capabilities(&sigma_dut);
 #endif /* NL80211_SUPPORT */
 	sigma_dut_register_cmds();
 
diff --git a/sigma_dut.h b/sigma_dut.h
index ddb5d4d..60ce86c 100644
--- a/sigma_dut.h
+++ b/sigma_dut.h
@@ -393,6 +393,7 @@
 	int end_port;
 	enum ip_protocol protocol;
 	int dscp;
+	int granularity_score;
 	struct dscp_policy_data *next;
 };
 
@@ -1039,6 +1040,7 @@
 	unsigned int num_dscp_status;
 	unsigned int prev_disable_scs_support;
 	unsigned int prev_disable_mscs_support;
+	int dscp_use_iptables;
 };
 
 
@@ -1153,6 +1155,7 @@
 int ap_wps_registration(struct sigma_dut *dut, struct sigma_conn *conn,
 			struct sigma_cmd *cmd);
 const char * get_hostapd_ifname(struct sigma_dut *dut);
+void get_wiphy_capabilities(struct sigma_dut *dut);
 
 /* sta.c */
 void sta_register_cmds(void);
diff --git a/sta.c b/sta.c
index 008f102..2940757 100644
--- a/sta.c
+++ b/sta.c
@@ -3594,6 +3594,21 @@
 }
 
 
+static char * protocol_to_str(int proto)
+{
+	switch (proto) {
+	case 6:
+		return "tcp";
+	case 17:
+		return "udp";
+	case 50:
+		return "esp";
+	default:
+		return "unknown";
+	}
+}
+
+
 static int delete_nft_table(struct sigma_dut *dut, const char *table,
 			   const char *ip_type)
 {
@@ -3640,6 +3655,140 @@
 }
 
 
+static int remove_iptable_rule(struct sigma_dut *dut,
+			       struct dscp_policy_data *dscp_policy)
+{
+	char ip_cmd[1000];
+	char *pos;
+	int ret, len;
+	enum ip_version ip_ver = dscp_policy->ip_version;
+
+	pos = ip_cmd;
+	len = sizeof(ip_cmd);
+
+	ret = snprintf(pos, len,
+		       "%s -t mangle -D OUTPUT -o %s",
+#ifdef ANDROID
+		       ip_ver == IPV6 ? "/system/bin/ip6tables" : "/system/bin/iptables",
+#else /* ANDROID */
+		       ip_ver == IPV6 ? "ip6tables" : "iptables",
+#endif /* ANDROID */
+		       dut->station_ifname);
+	if (snprintf_error(len, ret)) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Failed to create delete iptables command %s",
+				ip_cmd);
+		return -1;
+	}
+
+	pos += ret;
+	len -= ret;
+
+	if (strlen(dscp_policy->src_ip)) {
+		ret = snprintf(pos, len, " -s %s", dscp_policy->src_ip);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding src_ip %s in delete command",
+					dscp_policy->src_ip);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (strlen(dscp_policy->dst_ip)) {
+		ret = snprintf(pos, len, " -d %s",
+			       dscp_policy->dst_ip);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding dst_ip %s in delete cmd",
+					dscp_policy->dst_ip);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (dscp_policy->src_port || dscp_policy->dst_port ||
+	    (dscp_policy->start_port && dscp_policy->end_port)) {
+		ret = snprintf(pos, len, " -p %s",
+			       protocol_to_str(dscp_policy->protocol));
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding protocol %d in delete command",
+					dscp_policy->protocol);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (dscp_policy->src_port) {
+		ret = snprintf(pos, len, " --sport %d",
+			       dscp_policy->src_port);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding src_port %d in delete command",
+					 dscp_policy->src_port);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (dscp_policy->dst_port) {
+		ret = snprintf(pos, len, " --dport %d",
+			       dscp_policy->dst_port);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding dst_port %d in delete command",
+					 dscp_policy->dst_port);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (dscp_policy->start_port && dscp_policy->end_port) {
+		ret = snprintf(pos, len, " --match multiport --dports %d:%d",
+			       dscp_policy->start_port,
+			       dscp_policy->end_port);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding start:end port %d:%d in delete command",
+					 dscp_policy->start_port,
+					 dscp_policy->end_port);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	ret = snprintf(pos, len, " -j DSCP --set-dscp 0x%0x",
+		       dscp_policy->dscp);
+	if (snprintf_error(len, ret)) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Error in adding dscp %0x in delete command",
+				dscp_policy->dscp);
+		return -1;
+	}
+	ret = system(ip_cmd);
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "iptables rule: %s err: %d",
+			ip_cmd, ret);
+
+	return ret;
+}
+
+
+static int remove_dscp_policy_rule(struct sigma_dut *dut,
+				   struct dscp_policy_data *dscp_policy)
+{
+	return dut->dscp_use_iptables ? remove_iptable_rule(dut, dscp_policy) :
+		remove_nft_rule(dut, dscp_policy->policy_id,
+				dscp_policy->ip_version);
+}
+
+
 static int create_nft_table(struct sigma_dut *dut, int policy_id,
 			    const char *table_name, enum ip_version ip_ver)
 {
@@ -3679,21 +3828,6 @@
 }
 
 
-static char * protocol_to_str(int proto)
-{
-	switch (proto) {
-	case 6:
-		return "tcp";
-	case 17:
-		return "udp";
-	case 50:
-		return "esp";
-	default:
-		return "unknown";
-	}
-}
-
-
 static int remove_dscp_policy(struct sigma_dut *dut, u8 policy_id)
 {
 	struct dscp_policy_data *dscp_policy = dut->dscp_policy_table;
@@ -3715,7 +3849,7 @@
 		return 0;
 
 	if (strlen(dscp_policy->domain_name) == 0 &&
-	    remove_nft_rule(dut, policy_id, dscp_policy->ip_version))
+	    remove_dscp_policy_rule(dut, dscp_policy))
 		return -1;
 
 	if (prev)
@@ -3835,13 +3969,184 @@
 }
 
 
+static int add_iptable_rule(struct sigma_dut *dut,
+			    struct dscp_policy_data *dscp_policy)
+{
+	char ip_cmd[1000];
+	char *pos;
+	int ret, len;
+	enum ip_version ip_ver = dscp_policy->ip_version;
+	struct dscp_policy_data *active_policy = dut->dscp_policy_table;
+	int ipv4_rule_num = 1, ipv6_rule_num = 1;
+
+	pos = ip_cmd;
+	len = sizeof(ip_cmd);
+
+	/*
+	 * DSCP target in the mangle table doesn't stop processing of rules
+	 * so to make sure the most granular rule is applied last, add the new
+	 * rules in granularity increasing order.
+	 */
+	while (active_policy) {
+		/*
+		 * Domain name rules are managed in sigma_dut thus don't count
+		 * them while counting the number of active rules.
+		 */
+		if (strlen(active_policy->domain_name)) {
+			active_policy = active_policy->next;
+			continue;
+		}
+
+		if (active_policy->granularity_score >
+		    dscp_policy->granularity_score)
+			break;
+
+		if (active_policy->ip_version == IPV6)
+			ipv6_rule_num++;
+		else
+			ipv4_rule_num++;
+
+		active_policy = active_policy->next;
+	}
+
+	ret = snprintf(pos, len,
+		       "%s -t mangle -I OUTPUT %d -o %s",
+#ifdef ANDROID
+		       ip_ver == IPV6 ? "/system/bin/ip6tables" : "/system/bin/iptables",
+#else /* ANDROID */
+		       ip_ver == IPV6 ? "ip6tables" : "iptables",
+#endif /* ANDROID */
+		       ip_ver == IPV6 ? ipv6_rule_num : ipv4_rule_num,
+		       dut->station_ifname);
+	if (snprintf_error(len, ret)) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Failed to create iptables command %s", ip_cmd);
+		return -1;
+	}
+
+	pos += ret;
+	len -= ret;
+
+	if (strlen(dscp_policy->src_ip)) {
+		ret = snprintf(pos, len, " -s %s", dscp_policy->src_ip);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding src_ip %s",
+					dscp_policy->src_ip);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (strlen(dscp_policy->dst_ip)) {
+		ret = snprintf(pos, len, " -d %s", dscp_policy->dst_ip);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding dst_ip %s",
+					dscp_policy->dst_ip);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (dscp_policy->src_port || dscp_policy->dst_port ||
+	    (dscp_policy->start_port && dscp_policy->end_port)) {
+		ret = snprintf(pos, len, " -p %s",
+			       protocol_to_str(dscp_policy->protocol));
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding protocol %d in add command",
+					 dscp_policy->protocol);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (dscp_policy->src_port) {
+		ret = snprintf(pos, len, " --sport %d", dscp_policy->src_port);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding src_port %d",
+					 dscp_policy->src_port);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (dscp_policy->dst_port) {
+		ret = snprintf(pos, len, " --dport %d", dscp_policy->dst_port);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding dst_port %d",
+					dscp_policy->dst_port);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	if (dscp_policy->start_port && dscp_policy->end_port) {
+		ret = snprintf(pos, len, " --match multiport --dports %d:%d",
+			       dscp_policy->start_port, dscp_policy->end_port);
+		if (snprintf_error(len, ret)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Error in adding start:end port %d:%d",
+					dscp_policy->start_port,
+					dscp_policy->end_port);
+			return -1;
+		}
+		pos += ret;
+		len -= ret;
+	}
+
+	ret = snprintf(pos, len, " -j DSCP --set-dscp 0x%0x",
+		       dscp_policy->dscp);
+	if (snprintf_error(len, ret)) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Error in adding dscp %0x", dscp_policy->dscp);
+		return -1;
+	}
+	ret = system(ip_cmd);
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "iptables rule: %s err: %d",
+			ip_cmd, ret);
+
+	return ret;
+}
+
+
+static int add_dscp_policy_rule(struct sigma_dut *dut,
+				struct dscp_policy_data *dscp_policy)
+{
+	return dut->dscp_use_iptables ? add_iptable_rule(dut, dscp_policy) :
+		add_nft_rule(dut, dscp_policy);
+}
+
+
 static void clear_all_dscp_policies(struct sigma_dut *dut)
 {
 	free_dscp_policy_table(dut);
 
-	if (system("nft flush ruleset") != 0)
-		sigma_dut_print(dut, DUT_MSG_ERROR,
-				"Failed to flush DSCP policy");
+	if (dut->dscp_use_iptables) {
+#ifdef ANDROID
+		if (system("/system/bin/iptables -t mangle -F && /system/bin/iptables -t mangle -X") != 0 ||
+		    system("/system/bin/ip6tables -t mangle -F && /system/bin/ip6tables -t mangle -X") != 0)
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"iptables: Failed to flush DSCP policy");
+#else /* ANDROID */
+		if (system("iptables -t mangle -F && iptables -t mangle -X") != 0 ||
+		    system("ip6tables -t mangle -F && ip6tables -t mangle -X") != 0)
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"iptables: Failed to flush DSCP policy");
+#endif /* ANDROID */
+	} else {
+		if (system("nft flush ruleset") != 0)
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"nftables: Failed to flush DSCP policy");
+	}
 }
 
 
@@ -3911,7 +4216,7 @@
 	int ret, policy_id;
 	struct wpa_ctrl *ctrl;
 	char buf[4096], *pos, *end;
-	struct dscp_policy_data *policy = NULL, *policy_table;
+	struct dscp_policy_data *policy = NULL, *current_policy, *prev_policy;
 	struct dscp_policy_status status_list[10];
 	int num_status = 0;
 	const char *events[] = {
@@ -4038,6 +4343,8 @@
 			goto reject;
 		}
 		policy->ip_version = atoi(pos + 11);
+		if (policy->ip_version)
+			policy->granularity_score++;
 
 		pos = strstr(buf, "domain_name=");
 		if (pos) {
@@ -4051,6 +4358,7 @@
 
 			memcpy(policy->domain_name, pos, end - pos);
 			policy->domain_name[end - pos] = '\0';
+			policy->granularity_score++;
 		}
 
 		pos = strstr(buf, "start_port=");
@@ -4065,6 +4373,9 @@
 			policy->end_port = atoi(pos);
 		}
 
+		if (policy->start_port && policy->end_port)
+			policy->granularity_score++;
+
 		pos = strstr(buf, "src_ip=");
 		if (pos) {
 			pos += 7;
@@ -4077,6 +4388,7 @@
 
 			memcpy(policy->src_ip, pos, end - pos);
 			policy->src_ip[end - pos] = '\0';
+			policy->granularity_score++;
 		}
 
 		pos = strstr(buf, "dst_ip=");
@@ -4091,42 +4403,61 @@
 
 			memcpy(policy->dst_ip, pos, end - pos);
 			policy->dst_ip[end - pos] = '\0';
+			policy->granularity_score++;
 		}
 
 		pos = strstr(buf, "src_port=");
 		if (pos) {
 			pos += 9;
 			policy->src_port = atoi(pos);
+			policy->granularity_score++;
 		}
 
 		pos = strstr(buf, "dst_port=");
 		if (pos) {
 			pos += 9;
 			policy->dst_port = atoi(pos);
+			policy->granularity_score++;
 		}
 
 		pos = strstr(buf, "protocol=");
 		if (pos) {
 			pos += 9;
 			policy->protocol = atoi(pos);
+			policy->granularity_score++;
 		}
 
 		/*
 		 * Skip adding nft rules for doman name policies.
 		 * Domain name rules are applied in sigma_dut itself.
 		 */
-		if (!strlen(policy->domain_name) && add_nft_rule(dut, policy))
+		if (!strlen(policy->domain_name) &&
+		    add_dscp_policy_rule(dut, policy))
 			goto reject;
 
-		if (dut->dscp_policy_table) {
-			policy_table = dut->dscp_policy_table;
-			while (policy_table->next != NULL)
-				policy_table = policy_table->next;
+		/*
+		 * Add the new policy in policy table in granularity increasing
+		 * order.
+		 */
+		current_policy = dut->dscp_policy_table;
+		prev_policy = NULL;
 
-			policy_table->next = policy;
-		} else
+		while (current_policy) {
+			if (current_policy->granularity_score >
+			    policy->granularity_score)
+				break;
+
+			prev_policy = current_policy;
+			current_policy = current_policy->next;
+		}
+
+		if (prev_policy)
+			prev_policy->next = policy;
+		else
 			dut->dscp_policy_table = policy;
 
+		policy->next = current_policy;
+
 success:
 		status_list[num_status].status = DSCP_POLICY_SUCCESS;
 		num_status++;
@@ -14283,6 +14614,9 @@
 	val = get_param(cmd, "TWT_Setup");
 	if (val) {
 		if (strcasecmp(val, "Request") == 0) {
+			if (set_power_save_wcn(dut, intf, 1) < 0)
+				sigma_dut_print(dut, DUT_MSG_ERROR,
+						"Failed to enable power save");
 			if (sta_twt_request(dut, conn, cmd)) {
 				send_resp(dut, conn, SIGMA_ERROR,
 					  "ErrorCode,TWT setup failed");
diff --git a/utils.c b/utils.c
index a5c645c..cb69a99 100644
--- a/utils.c
+++ b/utils.c
@@ -418,6 +418,8 @@
 void * nl80211_cmd(struct sigma_dut *dut, struct nl80211_ctx *ctx,
 		   struct nl_msg *msg, int flags, uint8_t cmd)
 {
+	if (!ctx)
+		return NULL;
 	return genlmsg_put(msg, 0, 0, ctx->netlink_familyid,
 			   0, flags, cmd, 0);
 }