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