1 /*
2 * Copyright (c) 2020, 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 #include <assert.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <memory>
35
36 #include <dbus/dbus.h>
37 #include <unistd.h>
38
39 #include "common/code_utils.hpp"
40 #include "dbus/client/thread_api_dbus.hpp"
41 #include "dbus/common/constants.hpp"
42
43 using otbr::DBus::ActiveScanResult;
44 using otbr::DBus::ClientError;
45 using otbr::DBus::DeviceRole;
46 using otbr::DBus::EnergyScanResult;
47 using otbr::DBus::ExternalRoute;
48 using otbr::DBus::Ip6Prefix;
49 using otbr::DBus::LinkModeConfig;
50 using otbr::DBus::OnMeshPrefix;
51 using otbr::DBus::SrpServerInfo;
52 using otbr::DBus::ThreadApiDBus;
53 using otbr::DBus::TxtEntry;
54
55 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
56 using otbr::DBus::DnssdCounters;
57 #endif
58
59 #define TEST_ASSERT(x) \
60 do \
61 { \
62 if (!(x)) \
63 { \
64 printf("Assert failed at %s:%d\n", __FILE__, __LINE__); \
65 exit(EXIT_FAILURE); \
66 } \
67 } while (false)
68
69 struct DBusConnectionDeleter
70 {
operator ()DBusConnectionDeleter71 void operator()(DBusConnection *aConnection) { dbus_connection_unref(aConnection); }
72 };
73
74 using UniqueDBusConnection = std::unique_ptr<DBusConnection, DBusConnectionDeleter>;
75
operator ==(const otbr::DBus::Ip6Prefix & aLhs,const otbr::DBus::Ip6Prefix & aRhs)76 static bool operator==(const otbr::DBus::Ip6Prefix &aLhs, const otbr::DBus::Ip6Prefix &aRhs)
77 {
78 bool prefixDataEquality = (aLhs.mPrefix.size() == aRhs.mPrefix.size()) &&
79 (memcmp(&aLhs.mPrefix[0], &aRhs.mPrefix[0], aLhs.mPrefix.size()) == 0);
80
81 return prefixDataEquality && aLhs.mLength == aRhs.mLength;
82 }
83
CheckExternalRoute(ThreadApiDBus * aApi,const Ip6Prefix & aPrefix)84 static void CheckExternalRoute(ThreadApiDBus *aApi, const Ip6Prefix &aPrefix)
85 {
86 ExternalRoute route = {};
87 std::vector<ExternalRoute> externalRouteTable;
88
89 route.mPrefix = aPrefix;
90 route.mStable = true;
91 route.mPreference = 0;
92
93 TEST_ASSERT(aApi->AddExternalRoute(route) == OTBR_ERROR_NONE);
94 sleep(10);
95 TEST_ASSERT(aApi->GetExternalRoutes(externalRouteTable) == OTBR_ERROR_NONE);
96 TEST_ASSERT(externalRouteTable.size() == 1);
97 TEST_ASSERT(externalRouteTable[0].mPrefix == aPrefix);
98 TEST_ASSERT(externalRouteTable[0].mPreference == 0);
99 TEST_ASSERT(externalRouteTable[0].mStable);
100 TEST_ASSERT(externalRouteTable[0].mNextHopIsThisDevice);
101
102 TEST_ASSERT(aApi->RemoveExternalRoute(aPrefix) == OTBR_ERROR_NONE);
103 sleep(10);
104 TEST_ASSERT(aApi->GetExternalRoutes(externalRouteTable) == OTBR_ERROR_NONE);
105 TEST_ASSERT(externalRouteTable.empty());
106 }
107
CheckOnMeshPrefix(ThreadApiDBus * aApi)108 static void CheckOnMeshPrefix(ThreadApiDBus *aApi)
109 {
110 OnMeshPrefix prefix = {};
111 std::vector<OnMeshPrefix> onMeshPrefixes;
112
113 prefix.mPrefix.mPrefix = {0xfd, 0xee, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
114 prefix.mPrefix.mLength = 64;
115
116 prefix.mPreference = 0;
117 prefix.mStable = true;
118
119 TEST_ASSERT(aApi->AddOnMeshPrefix(prefix) == OTBR_ERROR_NONE);
120 sleep(10);
121 TEST_ASSERT(aApi->GetOnMeshPrefixes(onMeshPrefixes) == OTBR_ERROR_NONE);
122 TEST_ASSERT(onMeshPrefixes.size() == 1);
123 TEST_ASSERT(onMeshPrefixes[0].mPrefix == prefix.mPrefix);
124 TEST_ASSERT(onMeshPrefixes[0].mPreference == 0);
125 TEST_ASSERT(onMeshPrefixes[0].mStable);
126
127 TEST_ASSERT(aApi->RemoveOnMeshPrefix(prefix.mPrefix) == OTBR_ERROR_NONE);
128 sleep(10);
129 TEST_ASSERT(aApi->GetOnMeshPrefixes(onMeshPrefixes) == OTBR_ERROR_NONE);
130 TEST_ASSERT(onMeshPrefixes.empty());
131 }
132
CheckSrpServerInfo(ThreadApiDBus * aApi)133 void CheckSrpServerInfo(ThreadApiDBus *aApi)
134 {
135 SrpServerInfo srpServerInfo;
136
137 TEST_ASSERT(aApi->GetSrpServerInfo(srpServerInfo) == OTBR_ERROR_NONE);
138 TEST_ASSERT(srpServerInfo.mState == otbr::DBus::OTBR_SRP_SERVER_STATE_RUNNING);
139 TEST_ASSERT(srpServerInfo.mPort != 0);
140 TEST_ASSERT(srpServerInfo.mHosts.mFreshCount == 0);
141 TEST_ASSERT(srpServerInfo.mHosts.mDeletedCount == 0);
142 TEST_ASSERT(srpServerInfo.mHosts.mLeaseTimeTotal == 0);
143 TEST_ASSERT(srpServerInfo.mHosts.mKeyLeaseTimeTotal == 0);
144 TEST_ASSERT(srpServerInfo.mHosts.mRemainingLeaseTimeTotal == 0);
145 TEST_ASSERT(srpServerInfo.mHosts.mRemainingKeyLeaseTimeTotal == 0);
146 TEST_ASSERT(srpServerInfo.mServices.mFreshCount == 0);
147 TEST_ASSERT(srpServerInfo.mServices.mDeletedCount == 0);
148 TEST_ASSERT(srpServerInfo.mServices.mLeaseTimeTotal == 0);
149 TEST_ASSERT(srpServerInfo.mServices.mKeyLeaseTimeTotal == 0);
150 TEST_ASSERT(srpServerInfo.mServices.mRemainingLeaseTimeTotal == 0);
151 TEST_ASSERT(srpServerInfo.mServices.mRemainingKeyLeaseTimeTotal == 0);
152 TEST_ASSERT(srpServerInfo.mResponseCounters.mSuccess == 0);
153 TEST_ASSERT(srpServerInfo.mResponseCounters.mServerFailure == 0);
154 TEST_ASSERT(srpServerInfo.mResponseCounters.mFormatError == 0);
155 TEST_ASSERT(srpServerInfo.mResponseCounters.mNameExists == 0);
156 TEST_ASSERT(srpServerInfo.mResponseCounters.mRefused == 0);
157 TEST_ASSERT(srpServerInfo.mResponseCounters.mOther == 0);
158 }
159
CheckDnssdCounters(ThreadApiDBus * aApi)160 void CheckDnssdCounters(ThreadApiDBus *aApi)
161 {
162 OTBR_UNUSED_VARIABLE(aApi);
163 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
164 otbr::DBus::DnssdCounters dnssdCounters;
165
166 TEST_ASSERT(aApi->GetDnssdCounters(dnssdCounters) == OTBR_ERROR_NONE);
167 TEST_ASSERT(dnssdCounters.mSuccessResponse == 0);
168 TEST_ASSERT(dnssdCounters.mServerFailureResponse == 0);
169 TEST_ASSERT(dnssdCounters.mFormatErrorResponse == 0);
170 TEST_ASSERT(dnssdCounters.mNameErrorResponse == 0);
171 TEST_ASSERT(dnssdCounters.mNotImplementedResponse == 0);
172 TEST_ASSERT(dnssdCounters.mOtherResponse == 0);
173 TEST_ASSERT(dnssdCounters.mResolvedBySrp == 0);
174 #endif
175 }
176
CheckMdnsInfo(ThreadApiDBus * aApi)177 void CheckMdnsInfo(ThreadApiDBus *aApi)
178 {
179 otbr::MdnsTelemetryInfo mdnsInfo;
180
181 TEST_ASSERT(aApi->GetMdnsTelemetryInfo(mdnsInfo) == OTBR_ERROR_NONE);
182
183 TEST_ASSERT(mdnsInfo.mServiceRegistrations.mSuccess > 0);
184 TEST_ASSERT(mdnsInfo.mServiceRegistrationEmaLatency > 0);
185 }
186
main()187 int main()
188 {
189 DBusError error;
190 UniqueDBusConnection connection;
191 std::unique_ptr<ThreadApiDBus> api;
192 uint64_t extpanid = 0xdead00beaf00cafe;
193 std::string region;
194 uint32_t scanDuration = 1000; // 1s for each channel
195 bool stepDone = false;
196
197 dbus_error_init(&error);
198 connection = UniqueDBusConnection(dbus_bus_get(DBUS_BUS_SYSTEM, &error));
199
200 VerifyOrExit(connection != nullptr);
201
202 VerifyOrExit(dbus_bus_register(connection.get(), &error) == true);
203
204 api = std::unique_ptr<ThreadApiDBus>(new ThreadApiDBus(connection.get()));
205
206 api->AddDeviceRoleHandler(
207 [](DeviceRole aRole) { printf("Device role changed to %d\n", static_cast<uint8_t>(aRole)); });
208
209 TEST_ASSERT(api->SetRadioRegion("US") == ClientError::ERROR_NONE);
210 TEST_ASSERT(api->GetRadioRegion(region) == ClientError::ERROR_NONE);
211 TEST_ASSERT(region == "US");
212
213 api->EnergyScan(scanDuration, [&stepDone](const std::vector<EnergyScanResult> &aResult) {
214 TEST_ASSERT(!aResult.empty());
215 printf("Energy Scan:\n");
216 for (auto &result : aResult)
217 {
218 printf("channel %d rssi %d\n", result.mChannel, result.mMaxRssi);
219 }
220
221 stepDone = true;
222 });
223
224 while (!stepDone)
225 {
226 dbus_connection_read_write_dispatch(connection.get(), 0);
227 }
228
229 stepDone = false;
230
231 api->Scan([&api, extpanid, &stepDone](const std::vector<ActiveScanResult> &aResult) {
232 LinkModeConfig cfg = {true, false, true};
233 std::vector<uint8_t> networkKey = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
234 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
235 uint16_t channel = 11;
236
237 for (auto &&result : aResult)
238 {
239 printf("channel %d rssi %d\n", result.mChannel, result.mRssi);
240 }
241
242 api->SetLinkMode(cfg);
243 api->GetLinkMode(cfg);
244 printf("LinkMode %d %d %d\n", cfg.mRxOnWhenIdle, cfg.mDeviceType, cfg.mNetworkData);
245
246 cfg.mDeviceType = true;
247 api->SetLinkMode(cfg);
248
249 api->Attach("Test", 0x3456, extpanid, networkKey, {}, 1 << channel,
250 [&api, channel, extpanid, &stepDone](ClientError aError) {
251 printf("Attach result %d\n", static_cast<int>(aError));
252 sleep(20);
253 uint64_t extpanidCheck;
254 std::vector<uint8_t> activeDataset;
255
256 if (aError == OTBR_ERROR_NONE)
257 {
258 std::string name;
259 uint64_t extAddress = 0;
260 uint16_t rloc16 = 0xffff;
261 std::vector<uint8_t> networkData;
262 std::vector<uint8_t> stableNetworkData;
263 int8_t rssi;
264 int8_t txPower;
265 std::vector<otbr::DBus::ChildInfo> childTable;
266 std::vector<otbr::DBus::NeighborInfo> neighborTable;
267 uint32_t partitionId;
268 uint16_t channelResult;
269
270 TEST_ASSERT(api->GetChannel(channelResult) == OTBR_ERROR_NONE);
271 TEST_ASSERT(channelResult == channel);
272 TEST_ASSERT(api->GetNetworkName(name) == OTBR_ERROR_NONE);
273 TEST_ASSERT(api->GetExtPanId(extpanidCheck) == OTBR_ERROR_NONE);
274 TEST_ASSERT(api->GetRloc16(rloc16) == OTBR_ERROR_NONE);
275 TEST_ASSERT(api->GetExtendedAddress(extAddress) == OTBR_ERROR_NONE);
276 TEST_ASSERT(api->GetNetworkData(networkData) == OTBR_ERROR_NONE);
277 TEST_ASSERT(api->GetStableNetworkData(stableNetworkData) == OTBR_ERROR_NONE);
278 TEST_ASSERT(api->GetChildTable(childTable) == OTBR_ERROR_NONE);
279 TEST_ASSERT(api->GetNeighborTable(neighborTable) == OTBR_ERROR_NONE);
280 printf("neighborTable size %zu\n", neighborTable.size());
281 printf("childTable size %zu\n", childTable.size());
282 TEST_ASSERT(neighborTable.size() == 1);
283 TEST_ASSERT(childTable.size() == 1);
284 TEST_ASSERT(api->GetPartitionId(partitionId) == OTBR_ERROR_NONE);
285 TEST_ASSERT(api->GetInstantRssi(rssi) == OTBR_ERROR_NONE);
286 TEST_ASSERT(api->GetRadioTxPower(txPower) == OTBR_ERROR_NONE);
287 TEST_ASSERT(api->GetActiveDatasetTlvs(activeDataset) == OTBR_ERROR_NONE);
288 CheckSrpServerInfo(api.get());
289 CheckMdnsInfo(api.get());
290 CheckDnssdCounters(api.get());
291 api->FactoryReset(nullptr);
292 TEST_ASSERT(api->GetNetworkName(name) == OTBR_ERROR_NONE);
293 TEST_ASSERT(rloc16 != 0xffff);
294 TEST_ASSERT(extAddress != 0);
295 TEST_ASSERT(!networkData.empty());
296 TEST_ASSERT(api->GetNeighborTable(neighborTable) == OTBR_ERROR_NONE);
297 TEST_ASSERT(neighborTable.empty());
298 }
299 if (aError != OTBR_ERROR_NONE || extpanidCheck != extpanid)
300 {
301 exit(-1);
302 }
303 TEST_ASSERT(api->SetActiveDatasetTlvs(activeDataset) == OTBR_ERROR_NONE);
304 api->Attach([&api, channel, extpanid, &stepDone](ClientError aErr) {
305 uint8_t routerId;
306 otbr::DBus::LeaderData leaderData;
307 uint8_t leaderWeight;
308 uint16_t channelResult;
309 uint64_t extpanidCheck;
310 Ip6Prefix prefix;
311 std::vector<TxtEntry> updatedTxtEntries{TxtEntry{"B", {97, 98, 99}}};
312
313 prefix.mPrefix = {0xfd, 0xcd, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
314 prefix.mLength = 64;
315
316 TEST_ASSERT(aErr == ClientError::ERROR_NONE);
317 TEST_ASSERT(api->GetChannel(channelResult) == OTBR_ERROR_NONE);
318 TEST_ASSERT(channelResult == channel);
319 TEST_ASSERT(api->GetExtPanId(extpanidCheck) == OTBR_ERROR_NONE);
320 TEST_ASSERT(extpanidCheck == extpanid);
321
322 TEST_ASSERT(api->GetLocalLeaderWeight(leaderWeight) == OTBR_ERROR_NONE);
323 TEST_ASSERT(api->GetLeaderData(leaderData) == OTBR_ERROR_NONE);
324 TEST_ASSERT(api->GetRouterId(routerId) == OTBR_ERROR_NONE);
325 TEST_ASSERT(routerId == leaderData.mLeaderRouterId);
326
327 TEST_ASSERT(api->UpdateVendorMeshCopTxtEntries(updatedTxtEntries) == OTBR_ERROR_NONE);
328
329 CheckExternalRoute(api.get(), prefix);
330 CheckOnMeshPrefix(api.get());
331
332 api->FactoryReset(nullptr);
333 TEST_ASSERT(api->JoinerStart("ABCDEF", "", "", "", "", "", nullptr) ==
334 ClientError::OT_ERROR_NOT_FOUND);
335 TEST_ASSERT(api->JoinerStart(
336 "ABCDEF", "", "", "", "", "", [&api, &stepDone](ClientError aJoinError) {
337 DeviceRole deviceRole;
338
339 TEST_ASSERT(aJoinError == ClientError::OT_ERROR_NOT_FOUND);
340
341 api->FactoryReset(nullptr);
342 api->GetDeviceRole(deviceRole);
343 TEST_ASSERT(deviceRole == otbr::DBus::OTBR_DEVICE_ROLE_DISABLED);
344
345 stepDone = true;
346 }) == ClientError::ERROR_NONE);
347 });
348 });
349 });
350
351 while (!stepDone)
352 {
353 dbus_connection_read_write_dispatch(connection.get(), 0);
354 }
355
356 exit:
357 dbus_error_free(&error);
358 return 0;
359 };
360