1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32
33 #include <osTest.h>
34 #include <sys/socket.h>
35 #include <sys/select.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <net/if.h>
39 #include <sys/ioctl.h>
40
41 #define SERVER_PORT 9999
42 #define INVALID_SOCKET -1
43 #define CLIENT_NUM 1
44
45 static int gFds[FD_SETSIZE];
46 static int gBye;
47
InitFds()48 static void InitFds()
49 {
50 for (int i = 0; i < FD_SETSIZE; ++i) {
51 gFds[i] = INVALID_SOCKET;
52 }
53 }
54
GetReadfds(fd_set * fds,int * nfd)55 static void GetReadfds(fd_set *fds, int *nfd)
56 {
57 for (int i = 0; i < FD_SETSIZE; i++) {
58 if (gFds[i] == INVALID_SOCKET) {
59 continue;
60 }
61 FD_SET(gFds[i], fds);
62 if (*nfd < gFds[i]) {
63 *nfd = gFds[i];
64 }
65 }
66 }
67
AddFd(int fd)68 static int AddFd(int fd)
69 {
70 for (int i = 0; i < FD_SETSIZE; ++i) {
71 if (gFds[i] == INVALID_SOCKET) {
72 gFds[i] = fd;
73 return 0;
74 }
75 }
76 return -1;
77 }
78
DelFd(int fd)79 static void DelFd(int fd)
80 {
81 for (int i = 0; i < FD_SETSIZE; ++i) {
82 if (gFds[i] == fd) {
83 gFds[i] = INVALID_SOCKET;
84 }
85 }
86 (void)close(fd);
87 }
88
CloseAllFd(void)89 static int CloseAllFd(void)
90 {
91 for (int i = 0; i < FD_SETSIZE; ++i) {
92 if (gFds[i] != INVALID_SOCKET) {
93 (void)close(gFds[i]);
94 gFds[i] = INVALID_SOCKET;
95 }
96 }
97 return 0;
98 }
99
HandleRecv(int fd)100 static int HandleRecv(int fd)
101 {
102 char buf[256];
103 int ret = recv(fd, buf, sizeof(buf)-1, 0);
104 if (ret < 0) {
105 LogPrintln("[%d]Error: %s", fd, strerror(errno));
106 DelFd(fd);
107 } else if (ret == 0) {
108 LogPrintln("[%d]Closed", fd);
109 DelFd(fd);
110 } else {
111 buf[ret] = 0;
112 LogPrintln("[%d]Received: %s", fd, buf);
113 if (strstr(buf, "Bye") != NULL) {
114 gBye++;
115 }
116 }
117 return -(ret < 0);
118 }
119
HandleReadfds(fd_set * fds,int lsfd)120 static int HandleReadfds(fd_set *fds, int lsfd)
121 {
122 int ret = 0;
123 for (int i = 0; i < FD_SETSIZE; ++i) {
124 if (gFds[i] == INVALID_SOCKET || !FD_ISSET(gFds[i], fds)) {
125 continue;
126 }
127 ret += HandleRecv(gFds[i]);
128 }
129 return ret;
130 }
131
GetIp(int sfd,const char * ifname)132 static unsigned int GetIp(int sfd, const char *ifname)
133 {
134 struct ifreq ifr;
135 unsigned int ip = 0;
136 int ret = strncpy_s(ifr.ifr_name, sizeof(ifr.ifr_name) - 1, ifname, sizeof(ifr.ifr_name) - 1);
137 if (ret < 0) {
138 return 0;
139 }
140 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
141 ret = ioctl(sfd, SIOCGIFADDR, &ifr);
142 if (ret == 0) {
143 ip = (reinterpret_cast<struct sockaddr_in *>(&(ifr.ifr_addr)))->sin_addr.s_addr;
144 }
145 return ip;
146 }
147
GetNetmask(int sfd,const char * ifname)148 static unsigned int GetNetmask(int sfd, const char *ifname)
149 {
150 struct ifreq ifr;
151 unsigned int msk = 0;
152 int ret = strncpy_s(ifr.ifr_name, sizeof(ifr.ifr_name) - 1, ifname, sizeof(ifr.ifr_name) - 1);
153 if (ret < 0) {
154 return 0;
155 }
156 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
157 ret = ioctl(sfd, SIOCGIFNETMASK, &ifr);
158 if (ret == 0) {
159 msk = (reinterpret_cast<struct sockaddr_in *>(&(ifr.ifr_addr)))->sin_addr.s_addr;
160 }
161 return msk;
162 }
163
MyInetNtoa(unsigned int ip)164 static char *MyInetNtoa(unsigned int ip)
165 {
166 struct in_addr in = {ip};
167 return inet_ntoa(in);
168 }
169
ClientsThread(void * param)170 static void *ClientsThread(void *param)
171 {
172 int fd;
173 int thrNo = (int)(intptr_t)param;
174 int ret;
175 const char *ifname[] = {"eth0", "wlan0", "et1", "wl1", "enp4s0f0"};
176 unsigned int ip, msk, brdcast;
177
178 LogPrintln("<%d>socket client thread started", thrNo);
179 fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
180 if (fd == INVALID_SOCKET) {
181 perror("socket");
182 return NULL;
183 }
184
185 int broadcast = 1;
186 ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));
187 if (ret != 0) {
188 LogPrintln("[%d]<%d> set broadcast option fail", fd, thrNo);
189 close(fd);
190 return NULL;
191 }
192
193 for (int j = 0; j < sizeof(ifname) / sizeof(ifname[0]); ++j) {
194 ip = GetIp(fd, ifname[j]);
195 msk = GetNetmask(fd, ifname[j]);
196 if (ip != 0) {
197 LogPrintln("[%d]<%d>%s: ip %s", fd, thrNo, ifname[j], MyInetNtoa(ip));
198 LogPrintln("[%d]<%d>%s: netmask %s", fd, thrNo, ifname[j], MyInetNtoa(msk));
199 break;
200 }
201 }
202
203 brdcast = ip | ~msk;
204 LogPrintln("[%d]<%d>broadcast address %s", fd, thrNo, MyInetNtoa(brdcast));
205
206 struct sockaddr_in sa;
207 sa.sin_family = AF_INET;
208 sa.sin_addr.s_addr = brdcast;
209 sa.sin_port = htons(SERVER_PORT);
210 if (connect(fd, reinterpret_cast<struct sockaddr *>(&sa), sizeof(sa)) == -1) {
211 perror("connect");
212 return NULL;
213 }
214
215 LogPrintln("[%d]<%d>connected to udp://%s:%d successful", fd, thrNo, inet_ntoa(sa.sin_addr), SERVER_PORT);
216
217 const char *msg[] = {
218 "hello, ",
219 "ohos, ",
220 "my name is net_socket_test_013, ",
221 "see u next time, ",
222 "Bye!"
223 };
224
225 for (int i = 0; i < sizeof(msg) / sizeof(msg[0]); ++i) {
226 if (send(fd, msg[i], strlen(msg[i]), 0) < 0) {
227 LogPrintln("[%d]<%d>send msg [%s] fail: errno %d", fd, thrNo, msg[i], errno);
228 }
229 }
230
231 (void)shutdown(fd, SHUT_RDWR);
232 (void)close(fd);
233 return param;
234 }
235
StartClients(pthread_t * cli,int cliNum)236 static int StartClients(pthread_t *cli, int cliNum)
237 {
238 int ret;
239 pthread_attr_t attr;
240
241 for (int i = 0; i < cliNum; ++i) {
242 ret = pthread_attr_init(&attr);
243 ICUNIT_ASSERT_EQUAL(ret, 0, ret);
244
245 ret = pthread_create(&cli[i], &attr, ClientsThread, (void *)(intptr_t)i);
246 ICUNIT_ASSERT_EQUAL(ret, 0, ret);
247
248 ret = pthread_attr_destroy(&attr);
249 ICUNIT_ASSERT_EQUAL(ret, 0, ret);
250 }
251 return 0;
252 }
253
UdpBrdcastSelectTest(void)254 static int UdpBrdcastSelectTest(void)
255 {
256 struct sockaddr_in sa = {0};
257 int lsfd;
258 int ret;
259
260 lsfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
261 ICUNIT_ASSERT_NOT_EQUAL(lsfd, -1, errno);
262
263 sa.sin_family = AF_INET;
264 sa.sin_addr.s_addr = htonl(INADDR_ANY);
265 sa.sin_port = htons(SERVER_PORT);
266 ret = bind(lsfd, reinterpret_cast<struct sockaddr *>(&sa), sizeof(sa));
267 ICUNIT_ASSERT_NOT_EQUAL(ret, -1, errno + CloseAllFd());
268
269 int broadcast = 1;
270 ret = setsockopt(lsfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));
271 ICUNIT_ASSERT_EQUAL(ret, 0, errno + CloseAllFd());
272
273 int loop = 0;
274 socklen_t len = sizeof(loop);
275 ret = getsockopt(lsfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &len);
276 ICUNIT_ASSERT_EQUAL(ret, 0, errno + CloseAllFd());
277 LogPrintln("IP_MULTICAST_LOOP default: %d", loop);
278
279 loop = 0;
280 ret = setsockopt(lsfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
281 ICUNIT_ASSERT_EQUAL(ret, 0, errno + CloseAllFd());
282
283 ret = getsockopt(lsfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &len);
284 ICUNIT_ASSERT_EQUAL(ret, 0, errno + CloseAllFd());
285 LogPrintln("IP_MULTICAST_LOOP changed to: %d", loop);
286
287 InitFds();
288 AddFd(lsfd);
289 LogPrintln("[%d]Waiting for client to connect on UDP port %d", lsfd, SERVER_PORT);
290
291 pthread_t clients[CLIENT_NUM];
292
293 ret = StartClients(clients, CLIENT_NUM);
294 ICUNIT_ASSERT_EQUAL(ret, 0, ret + CloseAllFd());
295
296 for ( ; ; ) {
297 int nfd;
298 fd_set readfds;
299 struct timeval timeout;
300
301 timeout.tv_sec = 3;
302 timeout.tv_usec = 0;
303
304 nfd = 0;
305 FD_ZERO(&readfds);
306
307 GetReadfds(&readfds, &nfd);
308
309 ret = select(nfd + 1, &readfds, NULL, NULL, &timeout);
310 LogPrintln("select %d", ret);
311 if (ret == -1) {
312 perror("select");
313 break; // error occurred
314 } else if (ret == 0) {
315 break; // timed out
316 }
317
318 if (HandleReadfds(&readfds, lsfd) < 0) {
319 break;
320 }
321 }
322
323 for (int i = 0; i < CLIENT_NUM; ++i) {
324 ret = pthread_join(clients[i], NULL);
325 ICUNIT_ASSERT_EQUAL(ret, 0, ret + CloseAllFd());
326 }
327
328 ICUNIT_ASSERT_EQUAL(gBye, CLIENT_NUM, gBye + CloseAllFd());
329 (void)CloseAllFd();
330 return ICUNIT_SUCCESS;
331 }
332
NetSocketTest013(void)333 void NetSocketTest013(void)
334 {
335 TEST_ADD_CASE(__FUNCTION__, UdpBrdcastSelectTest, TEST_POSIX, TEST_TCP, TEST_LEVEL0, TEST_FUNCTION);
336 }
337