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_qdisc.h"
17
18 #include <errno.h>
19 #include <pthread.h>
20
21 #include "nstackx_congestion.h"
22 #include "nstackx_error.h"
23 #include "nstackx_log.h"
24 #include "nstackx_nlmsg.h"
25 #include "nstackx_timer.h"
26
27
28 #define TAG "nStackXCongestion"
29 #define SEND_NETLINK_REQUEST_COUNT 2
30
ProcessQdiscInfoInner(struct rtattr * tb[],int32_t parent)31 static int32_t ProcessQdiscInfoInner(struct rtattr *tb[], int32_t parent)
32 {
33 struct rtattr *tbs[TCA_STATS_MAX + 1] = {0};
34 (void)parent;
35
36 struct rtattr *rta = RTA_DATA(tb[TCA_STATS2]); // tb is trusted
37 int32_t len = (int32_t)RTA_PAYLOAD(tb[TCA_STATS2]);
38
39 RecvNetlinkParseAttr(rta, len, tbs, TCA_STATS_MAX);
40
41 if (tbs[TCA_STATS_QUEUE] != NULL) {
42 struct gnet_stats_queue q = {0};
43 len = NlMin((int32_t)RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), (int32_t)sizeof(q));
44 if (memcpy_s(&q, len, RTA_DATA(tbs[TCA_STATS_QUEUE]), len) == NSTACKX_EOK) {
45 return q.qlen;
46 } else {
47 LOGE(TAG, "memcpy_s failed");
48 }
49 }
50
51 return 0;
52 }
53
CheckTcMsgRecv(struct tcmsg * tcMsgRecv,int32_t ifIndex,int32_t protocol)54 static int32_t CheckTcMsgRecv(struct tcmsg *tcMsgRecv, int32_t ifIndex, int32_t protocol)
55 {
56 if (tcMsgRecv->tcm_ifindex != ifIndex || tcMsgRecv->tcm_parent != (uint32_t)protocol) {
57 return NSTACKX_EINVAL;
58 }
59 return NSTACKX_EOK;
60 }
61
ProcessQdiscInfo(struct nlmsghdr * h,void * arg,void * value)62 static void ProcessQdiscInfo(struct nlmsghdr *h, void *arg, void *value)
63 {
64 QdiscArg *qdiscArg = (QdiscArg *)arg;
65 QdiscValue *qdiscValue = (QdiscValue *)value;
66
67 int32_t ifIndex = qdiscArg->ifIndex;
68 int32_t protocol = qdiscArg->protocol;
69 if (h->nlmsg_type != RTM_NEWQDISC && h->nlmsg_type != RTM_DELQDISC) {
70 LOGE(TAG, "Not a qdisc\n");
71 return;
72 }
73 struct tcmsg *tcMsgRecv = NLMSG_DATA(h); // h is trusted
74 struct rtattr *tb[TCA_MAX + 1] = {0};
75 int32_t len = (int32_t)(h->nlmsg_len);
76 len -= NLMSG_LENGTH(sizeof(*tcMsgRecv));
77 if (len < 0) {
78 LOGE(TAG, "Wrong len %d", len);
79 return;
80 }
81
82 if (CheckTcMsgRecv(tcMsgRecv, ifIndex, protocol) != NSTACKX_EOK) {
83 return;
84 }
85
86 struct rtattr *rta = TCA_RTA(tcMsgRecv);
87 RecvNetlinkParseAttr(rta, len, tb, TCA_MAX);
88
89 if (tb[TCA_KIND] == NULL) {
90 LOGE(TAG, "NULL KIND!");
91 return;
92 }
93
94 qdiscValue->qlen = ProcessQdiscInfoInner(tb, tcMsgRecv->tcm_parent);
95
96 return;
97 }
98
GetQdiscUsedLength(const char * devName,int32_t protocol,int32_t * len)99 static int32_t GetQdiscUsedLength(const char *devName, int32_t protocol, int32_t *len)
100 {
101 int32_t sockFd;
102 int32_t ret;
103 int32_t sendNetlinkRequestCount = SEND_NETLINK_REQUEST_COUNT;
104 struct NlmsgCallback nlcb;
105 QdiscArg qdiscArg;
106 QdiscValue qdiscValue = {0};
107 qdiscArg.ifIndex = (int32_t)if_nametoindex(devName);
108 qdiscArg.protocol = protocol;
109
110 nlcb.nlcb = ProcessQdiscInfo;
111 nlcb.arg = &qdiscArg;
112 nlcb.value = &qdiscValue;
113
114 sockFd = NetlinkSocketInit();
115 if (sockFd < 0) {
116 return NSTACKX_EFAILED;
117 }
118
119 while (sendNetlinkRequestCount > 0) {
120 ret = SendNetlinkRequest(sockFd, qdiscArg.ifIndex, RTM_GETQDISC);
121 if (ret == NSTACKX_EOK) {
122 ret = RecvNetlinkResponse(sockFd, &nlcb);
123 if (ret == NSTACKX_EOK) {
124 break;
125 }
126 }
127 sendNetlinkRequestCount--;
128 }
129 if (ret == NSTACKX_EOK) {
130 *len = qdiscValue.qlen;
131 }
132 if (close(sockFd) < 0) {
133 LOGE(TAG, "close failed.");
134 return NSTACKX_EFAILED;
135 }
136
137 return ret;
138 }
139
CheckQdiscAllLen(int32_t qdiscAllLen)140 static inline int32_t CheckQdiscAllLen(int32_t qdiscAllLen)
141 {
142 if (qdiscAllLen < QDISC_MIN_LENGTH || qdiscAllLen > QDISC_MAX_LENGTH) {
143 return NSTACKX_EFAILED;
144 }
145 return NSTACKX_EOK;
146 }
147
GetQdiscAllLengthFromFile(const char * devName)148 static int32_t GetQdiscAllLengthFromFile(const char *devName)
149 {
150 char qdiscFileName[QDISC_FILE_NAME_NAX_LENGTH] = {0};
151 int32_t ret = sprintf_s(
152 qdiscFileName, QDISC_FILE_NAME_NAX_LENGTH, "/sys/devices/virtual/net/%s/tx_queue_len", devName);
153 if (ret <= 0) {
154 LOGE(TAG, "sprintf_s failed, ret %d errno %d", ret, errno);
155 return ret;
156 }
157
158 char *fileName = qdiscFileName;
159 char absolutePath[PATH_MAX + 1] = {0}; // +1 is avoiding array out of bound
160 if (realpath(qdiscFileName, absolutePath) == NULL) {
161 LOGE(TAG, "realpath failed");
162 } else {
163 fileName = absolutePath;
164 }
165
166 if (strstr(fileName, "..") != NULL) {
167 LOGE(TAG, "file name is not canonical form");
168 return NSTACKX_EFAILED;
169 }
170
171 FILE *fd = fopen(fileName, "r");
172 if (fd == NULL) {
173 LOGE(TAG, "file open failed, errno %d", errno);
174 return NSTACKX_EFAILED;
175 }
176
177 int32_t qdiscAllLen;
178 ret = fscanf_s(fd, "%d", &qdiscAllLen);
179 if (ret != 1 || CheckQdiscAllLen(qdiscAllLen) != NSTACKX_EOK) {
180 LOGE(TAG, "fscanf_s error ret %d qdiscAllLen %d.", ret, qdiscAllLen);
181 if (fclose(fd) < 0) {
182 LOGE(TAG, "close failed.");
183 }
184 return NSTACKX_EFAILED;
185 }
186 LOGI(TAG, "qdiscAllLen is %d.", qdiscAllLen);
187 if (fclose(fd) < 0) {
188 LOGE(TAG, "close failed.");
189 return NSTACKX_EFAILED;
190 }
191
192 return qdiscAllLen;
193 }
194
GetQdiscAllLength(const char * devName)195 static int32_t GetQdiscAllLength(const char *devName)
196 {
197 static int32_t searchFlag = 0;
198 static int32_t qdiscAllLength = QDISC_DEFAULT_LENGTH;
199
200 if (searchFlag == 0) {
201 int32_t tmpQdiscAllLength = GetQdiscAllLengthFromFile(devName);
202 if (tmpQdiscAllLength >= QDISC_MIN_LENGTH && tmpQdiscAllLength <= QDISC_MAX_LENGTH) {
203 qdiscAllLength = tmpQdiscAllLength;
204 }
205 searchFlag = 1;
206 }
207 return qdiscAllLength;
208 }
209
GetQdiscLeftLengthPolicy(int32_t qdiscAllLength,int32_t qdiscUsedLen)210 static uint32_t GetQdiscLeftLengthPolicy(int32_t qdiscAllLength, int32_t qdiscUsedLen)
211 {
212 uint32_t len;
213
214 if (qdiscUsedLen >= qdiscAllLength - FIRST_QDISC_LEN) {
215 len = 1;
216 } else if (qdiscUsedLen >= qdiscAllLength - SECOND_QDISC_LEN) {
217 len = 2; /* only send 2 packets */
218 } else {
219 len = qdiscAllLength - SECOND_QDISC_LEN - qdiscUsedLen + 1;
220 }
221 return len;
222 }
223
GetQdiscLeftLength(const char * devName,int32_t protocol,uint32_t * len)224 int32_t GetQdiscLeftLength(const char *devName, int32_t protocol, uint32_t *len)
225 {
226 int32_t qdiscAllLength = GetQdiscAllLength(devName);
227 int32_t qdiscUsedLen;
228 int32_t ret = GetQdiscUsedLength(devName, protocol, &qdiscUsedLen);
229 if (ret == NSTACKX_EOK) {
230 *len = GetQdiscLeftLengthPolicy(qdiscAllLength, qdiscUsedLen);
231 }
232 return ret;
233 }
234