1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "nstackx_congestion.h"
17 #include "sys_congestion.h"
18 #include "nstackx_error.h"
19 #include "nstackx_log.h"
20 #include "nstackx_util.h"
21 #include "nstackx_dev.h"
22 #include "securec.h"
23 
24 #define TAG "nStackXCongestion"
25 #define WIFI_THETA_ACCURACY 1000
26 #define PACKET_HEAD_BYTES 44
27 #define MS_NUM_PER_SECOND 1000
28 #define DATA_FRAME_SEND_INTERVAL_MS 5
29 
30 #define WIFI_DIFS 34
31 #define WIFI_SIFS 16
32 #define WIFI_ACK 32
33 #define WIFI_RTS 28
34 #define WIFI_CTS 28
35 #define WIFI_SLOT 9
36 #define WIFI_WINDOW 8
37 #define WIFI_PLCP 5
38 #define WIFI_COMPENSATE_TIME 30
39 #define MB_SIZE 1048576
40 
41 #define MAC_HEAD 14
42 #define IP_HEAD 20
43 #define UDP_HEAD 8
44 
45 #define WIFI_ASSEMBLE_NUM 100
46 #define WIFI_ASSEMBLE_TIMES 8
47 #define ENHANCE_P2P_SPEED_NUMRATOR 120
48 #define ENHANCE_P2P_SPEED_DENOMINATOR 100
49 #define NSTACKX_LEAST_SENDRATE 3 // 3 packets per 5ms
50 #define NSTACKX_LEAST_SENDRATE_DMSG 1 // MBps
51 
52 #define NSTACKX_MAX_CONNECTION_NUM 2
53 
54 static WifiStationInfo g_txWifiStationInfo[NSTACKX_MAX_CONNECTION_NUM] = {{0}};
55 
GetGTxWifiStationInfo(uint8_t socketIndex)56 WifiStationInfo GetGTxWifiStationInfo(uint8_t socketIndex)
57 {
58     return g_txWifiStationInfo[socketIndex];
59 }
60 
CheckWlanNegoRateValid(uint32_t rate)61 int32_t CheckWlanNegoRateValid(uint32_t rate)
62 {
63     if (rate == 0 || rate > MAX_WIFI_NEGO_RATE) {
64         return NSTACKX_EFAILED;
65     }
66     return NSTACKX_EOK;
67 }
68 
GetAssembleNumByBitrate(uint32_t speed)69 static inline uint32_t GetAssembleNumByBitrate(uint32_t speed)
70 {
71     uint32_t times = speed / WIFI_ASSEMBLE_NUM;  /* Mbps */
72     return (times > 1) ? (times * WIFI_ASSEMBLE_TIMES) : WIFI_ASSEMBLE_TIMES;
73 }
74 
GetEnhanceP2pSendRate(uint16_t speed)75 static inline uint16_t GetEnhanceP2pSendRate(uint16_t speed)
76 {
77     uint32_t ret = (uint32_t)(speed * ENHANCE_P2P_SPEED_NUMRATOR / ENHANCE_P2P_SPEED_DENOMINATOR);
78 
79     ret = (ret > UINT16_MAX) ? UINT16_MAX : ret;
80     return (uint16_t)ret;
81 }
82 
GetProtocolHead(void)83 static inline uint32_t GetProtocolHead(void)
84 {
85     return MAC_HEAD + IP_HEAD + UDP_HEAD;
86 }
87 
GetWifiNonDataPacketConsumeTime(void)88 static inline uint32_t GetWifiNonDataPacketConsumeTime(void)
89 {
90     const uint32_t time = WIFI_DIFS + WIFI_SLOT * WIFI_WINDOW + WIFI_RTS + WIFI_SIFS + WIFI_CTS + WIFI_SIFS +
91                           WIFI_SIFS + WIFI_ACK + WIFI_PLCP + WIFI_COMPENSATE_TIME;
92     return time;
93 }
94 
GetThetaByLinkSpeed(uint32_t speedByte,uint32_t mtu)95 static inline uint32_t GetThetaByLinkSpeed(uint32_t speedByte, uint32_t mtu)
96 {
97     uint32_t assembleNum = GetAssembleNumByBitrate(speedByte * BITINBYTE);
98     uint32_t effectiveSize = mtu * assembleNum;
99     uint32_t allSize = (GetWifiNonDataPacketConsumeTime() * speedByte + (PACKET_HEAD_BYTES + mtu) * assembleNum);
100     // WIFI_THETA_ACCURACY, ratio accuracy per thousand
101     const uint32_t theta = (WIFI_THETA_ACCURACY * effectiveSize / allSize);
102     return theta;
103 }
104 
GetSpeedRx(const WifiStationInfo * txWifiStationInfo,const WifiStationInfo * rxWifiStationInfo)105 static inline uint32_t GetSpeedRx(const WifiStationInfo *txWifiStationInfo, const WifiStationInfo *rxWifiStationInfo)
106 {
107     uint32_t speedRX;
108     if (rxWifiStationInfo->rxRate == 0) {
109         LOGE(TAG, "rxWifiStationInfo.txRateInfo.rateBitrate == 0");
110         speedRX = txWifiStationInfo->txRate / BITINBYTE;
111     } else {
112         speedRX = rxWifiStationInfo->rxRate / BITINBYTE;
113     }
114     return speedRX;
115 }
116 
GetWlanConngestSendRate(const WifiStationInfo * txWifiStationInfo,const WifiStationInfo * rxWifiStationInfo,uint16_t * sendRateResult,uint32_t mtu)117 static int32_t GetWlanConngestSendRate(const WifiStationInfo *txWifiStationInfo,
118     const WifiStationInfo *rxWifiStationInfo, uint16_t *sendRateResult, uint32_t mtu)
119 {
120     if (CheckWlanNegoRateValid(rxWifiStationInfo->rxRate) != NSTACKX_EOK) {
121         LOGE(TAG, "recv endian tx rate error %u", rxWifiStationInfo->rxRate);
122         return NSTACKX_EFAILED;
123     }
124 
125     uint32_t speedTX = txWifiStationInfo->txRate / BITINBYTE;
126     uint32_t speedRX = GetSpeedRx(txWifiStationInfo, rxWifiStationInfo);
127 
128     uint32_t thetaTx = GetThetaByLinkSpeed(speedTX, mtu);
129     uint32_t thetaRx = GetThetaByLinkSpeed(speedRX, mtu);
130     uint32_t sendRateOri =
131             (speedTX * thetaTx / WIFI_THETA_ACCURACY * speedRX * thetaRx) / (speedTX * thetaTx + speedRX * thetaRx);
132 
133     /* the packet num needed to be sent per 5 ms */
134     *sendRateResult = (uint16_t)(sendRateOri * MB_SIZE / (mtu + GetProtocolHead()) *
135                         DATA_FRAME_SEND_INTERVAL_MS / MS_NUM_PER_SECOND);
136     if (*sendRateResult < NSTACKX_LEAST_SENDRATE) {
137         *sendRateResult = NSTACKX_LEAST_SENDRATE;
138     }
139 
140     return NSTACKX_EOK;
141 }
142 
GetP2pCongestSendRate(const WifiStationInfo * txWifiStationInfo,const WifiStationInfo * rxWifiStationInfo,uint16_t * sendRateResult,uint32_t mtu)143 static int32_t GetP2pCongestSendRate(const WifiStationInfo *txWifiStationInfo,
144     const WifiStationInfo *rxWifiStationInfo, uint16_t *sendRateResult, uint32_t mtu)
145 {
146     const WifiStationInfo *wifiStationInfo = txWifiStationInfo;
147     if (txWifiStationInfo->txRate < rxWifiStationInfo->txRate) {
148         wifiStationInfo = rxWifiStationInfo;
149     }
150 
151     uint32_t speedTX = wifiStationInfo->txRate / BITINBYTE;
152     uint32_t thetaTx = GetThetaByLinkSpeed(speedTX, mtu);
153     uint32_t sendRateOri = speedTX * thetaTx / WIFI_THETA_ACCURACY;
154 
155     /* the packet num needed to be sent per 5 ms */
156     uint16_t realSendRateResult =
157             (uint16_t)(sendRateOri * MB_SIZE / (uint32_t)mtu * DATA_FRAME_SEND_INTERVAL_MS / MS_NUM_PER_SECOND);
158     *sendRateResult = GetEnhanceP2pSendRate(realSendRateResult);
159     if (*sendRateResult < NSTACKX_LEAST_SENDRATE) {
160         *sendRateResult = NSTACKX_LEAST_SENDRATE;
161     }
162 
163     return NSTACKX_EOK;
164 }
165 
CheckMtu(uint32_t mtu)166 static inline int32_t CheckMtu(uint32_t mtu)
167 {
168     if (mtu <= MIN_MTU || mtu > MAX_MTU) {
169         return NSTACKX_EFAILED;
170     }
171     return NSTACKX_EOK;
172 }
173 
174 /* calculate the sendrate for client */
GetConngestSendRate(WifiStationInfo * rxWifiStationInfo,uint16_t connType,uint32_t mtu,uint8_t socketIndex,uint16_t * sendRateResult)175 int32_t GetConngestSendRate(WifiStationInfo *rxWifiStationInfo, uint16_t connType, uint32_t mtu,
176     uint8_t socketIndex, uint16_t *sendRateResult)
177 {
178     int32_t ret;
179     if (CheckMtu(mtu) != NSTACKX_EOK) {
180         return NSTACKX_EFAILED;
181     }
182 
183     if (connType == CONNECT_TYPE_WLAN) {
184         ret = GetWlanConngestSendRate(&g_txWifiStationInfo[socketIndex],
185                                       rxWifiStationInfo, sendRateResult, mtu);
186     } else if (connType == CONNECT_TYPE_P2P) {
187         ret = GetP2pCongestSendRate(&g_txWifiStationInfo[socketIndex],
188                                     rxWifiStationInfo, sendRateResult, mtu);
189     } else {
190         return NSTACKX_EFAILED;
191     }
192 
193     return ret;
194 }
195 
CheckDevNameValid(const char * devName)196 int32_t CheckDevNameValid(const char *devName)
197 {
198     if (devName == NULL || strlen(devName) == 0 || strlen(devName) > IF_NAMESIZE) {
199         return NSTACKX_EFAILED;
200     }
201     return NSTACKX_EOK;
202 }
203 
204 /* get wifi info of server endian to send to client endian */
UpdateWifiStationInfo(const char * devName,WifiStationInfo * txWifiStationInfo,uint8_t socketIndex,int * changeStatus)205 int32_t UpdateWifiStationInfo(const char *devName, WifiStationInfo *txWifiStationInfo, uint8_t socketIndex,
206     int *changeStatus)
207 {
208     if (changeStatus != NULL) {
209         *changeStatus = 0;
210     }
211 
212     if (CheckDevNameValid(devName) != NSTACKX_EOK || txWifiStationInfo == NULL) {
213         return NSTACKX_EFAILED;
214     }
215 
216     (void)memset_s(txWifiStationInfo, sizeof(WifiStationInfo), 0, sizeof(WifiStationInfo));
217     int32_t ret = GetWifiInfo(devName, txWifiStationInfo);
218     if (ret != NSTACKX_EOK) {
219         return NSTACKX_EFAILED;
220     }
221     if (txWifiStationInfo->txRate != g_txWifiStationInfo[socketIndex].txRate) {
222         LOGI(TAG, "new.txRate %u old.txRate %u", txWifiStationInfo->txRate, g_txWifiStationInfo[socketIndex].txRate);
223         if (changeStatus != NULL) {
224             *changeStatus = 1;
225         }
226     }
227 
228     return memcpy_s(&g_txWifiStationInfo[socketIndex], sizeof(g_txWifiStationInfo[socketIndex]),
229                     txWifiStationInfo, sizeof(WifiStationInfo));
230 }
231 
232 /* get wifi info of server endian to send to client endian */
GetServerWifiStationInfo(const char * devName,WifiStationInfo * wifiStationInfo)233 int32_t GetServerWifiStationInfo(const char *devName, WifiStationInfo *wifiStationInfo)
234 {
235     if (CheckDevNameValid(devName) != NSTACKX_EOK || wifiStationInfo == NULL) {
236         return NSTACKX_EFAILED;
237     }
238 
239     return GetWifiInfo(devName, wifiStationInfo);
240 }
241 
GetWifiInfoDMsg(const char * devName,WifiStationInfo * wifiStationInfo)242 int32_t GetWifiInfoDMsg(const char *devName, WifiStationInfo *wifiStationInfo)
243 {
244     if (CheckDevNameValid(devName) != NSTACKX_EOK) {
245         return NSTACKX_EFAILED;
246     }
247 
248     return GetWifiInfo(devName, wifiStationInfo);
249 }
250 
GetWlanConngestSendRateDMsg(uint32_t speedTX,uint32_t speedRX,uint32_t * sendRateResult,uint32_t mtu)251 static int32_t GetWlanConngestSendRateDMsg(uint32_t speedTX, uint32_t speedRX,
252     uint32_t *sendRateResult, uint32_t mtu)
253 {
254     if (CheckWlanNegoRateValid(speedTX) != NSTACKX_EOK || CheckWlanNegoRateValid(speedRX) != NSTACKX_EOK) {
255         LOGD(TAG, "wifi tx rate or rx rate invalid, tx=%u, rx=%u", speedTX, speedRX);
256         return NSTACKX_EFAILED;
257     }
258 
259     speedTX = speedTX / BITINBYTE;
260     speedRX = speedRX / BITINBYTE;
261 
262     uint32_t thetaTx = GetThetaByLinkSpeed(speedTX, mtu);
263     uint32_t thetaRx = GetThetaByLinkSpeed(speedRX, mtu);
264     // MBps
265     *sendRateResult =
266             (speedTX * thetaTx / WIFI_THETA_ACCURACY * speedRX * thetaRx) / (speedTX * thetaTx + speedRX * thetaRx);
267     if (*sendRateResult < NSTACKX_LEAST_SENDRATE_DMSG) {
268         *sendRateResult = NSTACKX_LEAST_SENDRATE_DMSG;
269     }
270 
271     return NSTACKX_EOK;
272 }
273 
GetP2pCongestSendRateDmsg(uint32_t speedTX,uint32_t speedRX,uint32_t * sendRateResult,uint32_t mtu)274 static int32_t GetP2pCongestSendRateDmsg(uint32_t speedTX, uint32_t speedRX,
275     uint32_t *sendRateResult, uint32_t mtu)
276 {
277     if (speedRX > speedTX) {
278         speedTX = speedRX;
279     }
280     speedTX = speedTX / BITINBYTE;
281     uint32_t thetaTx = GetThetaByLinkSpeed(speedTX, mtu);
282     *sendRateResult = speedTX * thetaTx / WIFI_THETA_ACCURACY;
283 
284     if (*sendRateResult < NSTACKX_LEAST_SENDRATE_DMSG) {
285         *sendRateResult = NSTACKX_LEAST_SENDRATE_DMSG;
286     }
287 
288     return NSTACKX_EOK;
289 }
290 
GetConngestSendRateDMsg(const char * devName,uint32_t speedTX,uint32_t speedRX,uint32_t * sendRateResult,uint32_t mtu)291 int32_t GetConngestSendRateDMsg(const char *devName, uint32_t speedTX, uint32_t speedRX,
292     uint32_t *sendRateResult, uint32_t mtu)
293 {
294     if (CheckDevNameValid(devName) != NSTACKX_EOK) {
295         return NSTACKX_EFAILED;
296     }
297 
298     if (strstr(devName, "p2p") != NULL) {
299         return GetP2pCongestSendRateDmsg(speedTX, speedRX, sendRateResult, mtu);
300     } else {
301         return GetWlanConngestSendRateDMsg(speedTX, speedRX, sendRateResult, mtu);
302     }
303 }
304