• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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