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_socket.h"
17 #include "nstackx_log.h"
18 #include "nstackx_error.h"
19 #include "nstackx_util.h"
20 #include "nstackx_dev.h"
21 #include "securec.h"
22
23 #define DEFAULT_UDP_MSS 1472
24 #define DEFAULT_MAX_BUF 4096
25 #define IOV_CNT 2
26
27 #define TAG "nStackXSocket"
28
29 static int32_t g_gsoSupport = 0;
30
SupportGSO(void)31 int32_t SupportGSO(void)
32 {
33 return g_gsoSupport;
34 }
35
SocketModuleClean(void)36 void SocketModuleClean(void)
37 {
38 }
39
SocketModuleInit(void)40 int32_t SocketModuleInit(void)
41 {
42 return NSTACKX_EOK;
43 }
44
SetSocketNonBlock(SocketDesc fd)45 int32_t SetSocketNonBlock(SocketDesc fd)
46 {
47 int32_t flag;
48
49 flag = fcntl(fd, F_GETFL, 0);
50 if (flag < 0) {
51 LOGE(TAG, "fcntl GETFL error");
52 return NSTACKX_EFAILED;
53 }
54
55 if (fcntl(fd, F_SETFL, (unsigned int)flag | O_NONBLOCK) < 0) {
56 LOGE(TAG, "fcntl SETFL error");
57 return NSTACKX_EFAILED;
58 }
59 return NSTACKX_EOK;
60 }
61
62 #ifndef UDP_SEGMENT
63 #define UDP_SEGMENT 103
64 #endif
65
SetupCmsg(struct cmsghdr * cm,uint16_t mss)66 static inline void SetupCmsg(struct cmsghdr *cm, uint16_t mss)
67 {
68 cm->cmsg_level = SOL_UDP;
69 cm->cmsg_type = UDP_SEGMENT;
70 cm->cmsg_len = CMSG_LEN(sizeof(mss));
71 *(uint16_t *)(void *)CMSG_DATA(cm) = mss;
72 }
73
IsSocketValid(const Socket * s)74 static inline int32_t IsSocketValid(const Socket *s)
75 {
76 return !(s == NULL || s->protocol != NSTACKX_PROTOCOL_UDP);
77 }
78
SocketSendEx(const Socket * s,uint16_t mss,const struct iovec * iov,uint32_t cnt)79 int32_t SocketSendEx(const Socket *s, uint16_t mss, const struct iovec *iov, uint32_t cnt)
80 {
81 int32_t ret = NSTACKX_EFAILED;
82 char ctrl[CMSG_SPACE(sizeof(uint16_t))] = {0};
83 struct msghdr mh;
84
85 if (!IsSocketValid(s)) {
86 LOGE(TAG, "invalid socket input\n");
87 return ret;
88 }
89
90 mh.msg_name = (struct sockaddr *)&s->dstAddr;
91 mh.msg_namelen = sizeof(struct sockaddr_in);
92 mh.msg_iov = (struct iovec *)iov;
93 mh.msg_iovlen = (size_t)cnt;
94 mh.msg_control = ctrl;
95 mh.msg_controllen = sizeof(ctrl);
96 mh.msg_flags = 0;
97
98 SetupCmsg(CMSG_FIRSTHDR(&mh), mss);
99
100 ret = (int32_t)sendmsg(s->sockfd, &mh, 0);
101 if (ret <= 0) {
102 ret = CheckSocketError();
103 }
104
105 return ret;
106 }
107 #ifndef NSTACKX_WITH_HMOS_LINUX
SendUdpSegment(struct sockaddr_in * sa)108 static int32_t SendUdpSegment(struct sockaddr_in *sa)
109 {
110 int32_t err;
111 char ctrl[CMSG_SPACE(sizeof(uint16_t))] = {0};
112 struct msghdr mh;
113 char buf[DEFAULT_UDP_MSS];
114 struct iovec iov[IOV_CNT] = {
115 {
116 .iov_base = buf,
117 .iov_len = sizeof(buf),
118 },
119 {
120 .iov_base = buf,
121 .iov_len = sizeof(buf),
122 }
123 };
124 int32_t fd = socket(AF_INET, SOCK_DGRAM, 0);
125 if (fd < 0) {
126 return NSTACKX_EFAILED;
127 }
128
129 mh.msg_name = (struct sockaddr *)sa;
130 mh.msg_namelen = sizeof(struct sockaddr_in);
131 mh.msg_iov = iov;
132 mh.msg_iovlen = IOV_CNT;
133 mh.msg_control = ctrl;
134 mh.msg_controllen = sizeof(ctrl);
135 mh.msg_flags = 0;
136
137 SetupCmsg(CMSG_FIRSTHDR(&mh), DEFAULT_UDP_MSS);
138
139 err = (int32_t)sendmsg(fd, &mh, 0);
140 if (close(fd) < 0) {
141 return NSTACKX_EFAILED;
142 }
143 return (err == (IOV_CNT * DEFAULT_UDP_MSS)) ? NSTACKX_EOK : NSTACKX_EFAILED;
144 }
145
RecvUdpSegment(int32_t fd)146 static void RecvUdpSegment(int32_t fd)
147 {
148 ssize_t err;
149 char buf[DEFAULT_MAX_BUF];
150
151 err = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
152 if (err == DEFAULT_UDP_MSS) {
153 err = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
154 if (err == DEFAULT_UDP_MSS) {
155 g_gsoSupport = 1;
156 LOGI(TAG, "kernel support UDP GSO");
157 } else {
158 LOGI(TAG, "kernel does not support UDP GSO");
159 }
160 } else {
161 LOGI(TAG, "kernel does not support UDP GSO");
162 }
163 }
164
LocalAddrBindAndGet(int32_t fd,struct sockaddr_in * sa)165 static int32_t LocalAddrBindAndGet(int32_t fd, struct sockaddr_in *sa)
166 {
167 int32_t err;
168 socklen_t len = sizeof(*sa);
169
170 sa->sin_family = AF_INET;
171 sa->sin_port = 0;
172 sa->sin_addr.s_addr = inet_addr("127.0.0.1");
173 err = bind(fd, (struct sockaddr *)sa, len);
174 if (err) {
175 return NSTACKX_EFAILED;
176 }
177
178 err = getsockname(fd, (struct sockaddr *)sa, &len);
179 if (err) {
180 return NSTACKX_EFAILED;
181 }
182 return NSTACKX_EOK;
183 }
184 #endif
185
CheckGSOSupport(void)186 void CheckGSOSupport(void)
187 {
188 #ifndef NSTACKX_WITH_HMOS_LINUX
189 int32_t fd;
190 struct sockaddr_in sa = {0};
191
192 fd = socket(AF_INET, SOCK_DGRAM, 0);
193 if (fd < 0) {
194 return;
195 }
196 if (LocalAddrBindAndGet(fd, &sa) != NSTACKX_EOK) {
197 goto L_OUT;
198 }
199
200 if (SendUdpSegment(&sa) != NSTACKX_EOK) {
201 goto L_OUT;
202 }
203
204 RecvUdpSegment(fd);
205
206 L_OUT:
207 CloseSocketInner(fd);
208 #endif
209 }
210
SocketOpInProgress(void)211 int32_t SocketOpInProgress(void)
212 {
213 return errno == EINPROGRESS;
214 }
215
SocketOpWouldBlock(void)216 int32_t SocketOpWouldBlock(void)
217 {
218 return errno == EAGAIN || errno == EWOULDBLOCK;
219 }
220