1 /*
2 * Copyright (c) 2018, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * @brief
32 * This file implements the posix simulation.
33 */
34
35 #include "openthread-posix-config.h"
36 #include "platform-posix.h"
37
38 #include <arpa/inet.h>
39 #include <stddef.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <sys/select.h>
43 #include <unistd.h>
44
45 #if OPENTHREAD_POSIX_VIRTUAL_TIME
46
47 static const int kMaxNetworkSize = 33; ///< Well-known ID used by a simulated radio supporting promiscuous mode.
48 static const int kBasePort = 18000; ///< This base port for posix app simulation.
49 static const int kUsPerSecond = 1000000; ///< Number of microseconds per second.
50
51 static uint64_t sNow = 0; ///< Time of simulation.
52 static int sSockFd = -1; ///< Socket used to communicating with simulator.
53 static uint16_t sPortOffset = 0; ///< Port offset for simulation.
54
virtualTimeInit(uint16_t aNodeId)55 void virtualTimeInit(uint16_t aNodeId)
56 {
57 struct sockaddr_in sockaddr;
58 char * offset;
59
60 memset(&sockaddr, 0, sizeof(sockaddr));
61 sockaddr.sin_family = AF_INET;
62
63 offset = getenv("PORT_OFFSET");
64
65 if (offset)
66 {
67 char *endptr;
68
69 sPortOffset = (uint16_t)strtol(offset, &endptr, 0);
70
71 if (*endptr != '\0')
72 {
73 const uint8_t kMsgSize = 40;
74 char msg[kMsgSize];
75
76 snprintf(msg, sizeof(msg), "Invalid PORT_OFFSET: %s", offset);
77 DieNowWithMessage(msg, OT_EXIT_INVALID_ARGUMENTS);
78 }
79
80 sPortOffset *= (kMaxNetworkSize + 1);
81 }
82
83 sockaddr.sin_port = htons(kBasePort + sPortOffset + aNodeId);
84 sockaddr.sin_addr.s_addr = INADDR_ANY;
85
86 sSockFd = SocketWithCloseExec(AF_INET, SOCK_DGRAM, IPPROTO_UDP, kSocketBlock);
87
88 if (sSockFd == -1)
89 {
90 DieNowWithMessage("socket", OT_EXIT_ERROR_ERRNO);
91 }
92
93 if (bind(sSockFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == -1)
94 {
95 DieNowWithMessage("bind", OT_EXIT_ERROR_ERRNO);
96 }
97 }
98
virtualTimeDeinit(void)99 void virtualTimeDeinit(void)
100 {
101 if (sSockFd != -1)
102 {
103 close(sSockFd);
104 sSockFd = -1;
105 }
106 }
107
virtualTimeSendEvent(struct VirtualTimeEvent * aEvent,size_t aLength)108 static void virtualTimeSendEvent(struct VirtualTimeEvent *aEvent, size_t aLength)
109 {
110 ssize_t rval;
111 struct sockaddr_in sockaddr;
112
113 memset(&sockaddr, 0, sizeof(sockaddr));
114 sockaddr.sin_family = AF_INET;
115 inet_pton(AF_INET, "127.0.0.1", &sockaddr.sin_addr);
116 sockaddr.sin_port = htons(9000 + sPortOffset);
117
118 rval = sendto(sSockFd, aEvent, aLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
119
120 if (rval < 0)
121 {
122 DieNowWithMessage("sendto", OT_EXIT_ERROR_ERRNO);
123 }
124 }
125
virtualTimeReceiveEvent(struct VirtualTimeEvent * aEvent)126 void virtualTimeReceiveEvent(struct VirtualTimeEvent *aEvent)
127 {
128 ssize_t rval = recvfrom(sSockFd, aEvent, sizeof(*aEvent), 0, nullptr, nullptr);
129
130 if (rval < 0 || (uint16_t)rval < offsetof(struct VirtualTimeEvent, mData))
131 {
132 DieNowWithMessage("recvfrom", (rval < 0) ? OT_EXIT_ERROR_ERRNO : OT_EXIT_FAILURE);
133 }
134
135 sNow += aEvent->mDelay;
136 }
137
virtualTimeSendSleepEvent(const struct timeval * aTimeout)138 void virtualTimeSendSleepEvent(const struct timeval *aTimeout)
139 {
140 struct VirtualTimeEvent event;
141
142 event.mDelay = (uint64_t)aTimeout->tv_sec * kUsPerSecond + (uint64_t)aTimeout->tv_usec;
143 event.mEvent = OT_SIM_EVENT_ALARM_FIRED;
144 event.mDataLength = 0;
145
146 virtualTimeSendEvent(&event, offsetof(struct VirtualTimeEvent, mData));
147 }
148
virtualTimeSendRadioSpinelWriteEvent(const uint8_t * aData,uint16_t aLength)149 void virtualTimeSendRadioSpinelWriteEvent(const uint8_t *aData, uint16_t aLength)
150 {
151 struct VirtualTimeEvent event;
152
153 event.mDelay = 0;
154 event.mEvent = OT_SIM_EVENT_RADIO_SPINEL_WRITE;
155 event.mDataLength = aLength;
156
157 memcpy(event.mData, aData, aLength);
158
159 virtualTimeSendEvent(&event, offsetof(struct VirtualTimeEvent, mData) + event.mDataLength);
160 }
161
virtualTimeUpdateFdSet(fd_set * aReadFdSet,fd_set * aWriteFdSet,fd_set * aErrorFdSet,int * aMaxFd,struct timeval * aTimeout)162 void virtualTimeUpdateFdSet(fd_set * aReadFdSet,
163 fd_set * aWriteFdSet,
164 fd_set * aErrorFdSet,
165 int * aMaxFd,
166 struct timeval *aTimeout)
167 {
168 OT_UNUSED_VARIABLE(aWriteFdSet);
169 OT_UNUSED_VARIABLE(aErrorFdSet);
170 OT_UNUSED_VARIABLE(aTimeout);
171
172 FD_SET(sSockFd, aReadFdSet);
173 if (*aMaxFd < sSockFd)
174 {
175 *aMaxFd = sSockFd;
176 }
177 }
178
virtualTimeProcess(otInstance * aInstance,const fd_set * aReadFdSet,const fd_set * aWriteFdSet,const fd_set * aErrorFdSet)179 void virtualTimeProcess(otInstance * aInstance,
180 const fd_set *aReadFdSet,
181 const fd_set *aWriteFdSet,
182 const fd_set *aErrorFdSet)
183 {
184 struct VirtualTimeEvent event;
185
186 memset(&event, 0, sizeof(event));
187
188 OT_UNUSED_VARIABLE(aInstance);
189 OT_UNUSED_VARIABLE(aWriteFdSet);
190 OT_UNUSED_VARIABLE(aErrorFdSet);
191
192 if (FD_ISSET(sSockFd, aReadFdSet))
193 {
194 virtualTimeReceiveEvent(&event);
195 }
196
197 virtualTimeRadioSpinelProcess(aInstance, &event);
198 }
199
otPlatTimeGet(void)200 uint64_t otPlatTimeGet(void)
201 {
202 return sNow;
203 }
204
205 #endif // OPENTHREAD_POSIX_VIRTUAL_TIME
206