1 /*
2 * Copyright (c) 2021, 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 * The file implements the OTBR Agent.
32 */
33
34 #define OTBR_LOG_TAG "APP"
35
36 #ifdef HAVE_LIBSYSTEMD
37 #include <systemd/sd-daemon.h>
38 #endif
39
40 #include "agent/application.hpp"
41 #include "common/code_utils.hpp"
42 #include "common/mainloop_manager.hpp"
43 #include "utils/infra_link_selector.hpp"
44
45 namespace otbr {
46
47 std::atomic_bool Application::sShouldTerminate(false);
48 const struct timeval Application::kPollTimeout = {10, 0};
49
Application(const std::string & aInterfaceName,const std::vector<const char * > & aBackboneInterfaceNames,const std::vector<const char * > & aRadioUrls,bool aEnableAutoAttach,const std::string & aRestListenAddress,int aRestListenPort)50 Application::Application(const std::string &aInterfaceName,
51 const std::vector<const char *> &aBackboneInterfaceNames,
52 const std::vector<const char *> &aRadioUrls,
53 bool aEnableAutoAttach,
54 const std::string &aRestListenAddress,
55 int aRestListenPort)
56 : mInterfaceName(aInterfaceName)
57 #if __linux__
58 , mInfraLinkSelector(aBackboneInterfaceNames)
59 , mBackboneInterfaceName(mInfraLinkSelector.Select())
60 #else
61 , mBackboneInterfaceName(aBackboneInterfaceNames.empty() ? "" : aBackboneInterfaceNames.front())
62 #endif
63 , mHost(Ncp::ThreadHost::Create(mInterfaceName.c_str(),
64 aRadioUrls,
65 mBackboneInterfaceName,
66 /* aDryRun */ false,
67 aEnableAutoAttach))
68 #if OTBR_ENABLE_MDNS
69 , mPublisher(Mdns::Publisher::Create([this](Mdns::Publisher::State aState) { this->HandleMdnsState(aState); }))
70 #endif
71 #if OTBR_ENABLE_DBUS_SERVER && OTBR_ENABLE_BORDER_AGENT
72 , mDBusAgent(MakeUnique<DBus::DBusAgent>(*mHost, *mPublisher))
73 #endif
74 {
75 if (mHost->GetCoprocessorType() == OT_COPROCESSOR_RCP)
76 {
77 CreateRcpMode(aRestListenAddress, aRestListenPort);
78 }
79 }
80
Init(void)81 void Application::Init(void)
82 {
83 mHost->Init();
84
85 switch (mHost->GetCoprocessorType())
86 {
87 case OT_COPROCESSOR_RCP:
88 InitRcpMode();
89 break;
90 case OT_COPROCESSOR_NCP:
91 InitNcpMode();
92 break;
93 default:
94 DieNow("Unknown coprocessor type!");
95 break;
96 }
97
98 otbrLogInfo("Co-processor version: %s", mHost->GetCoprocessorVersion());
99 }
100
Deinit(void)101 void Application::Deinit(void)
102 {
103 switch (mHost->GetCoprocessorType())
104 {
105 case OT_COPROCESSOR_RCP:
106 DeinitRcpMode();
107 break;
108 case OT_COPROCESSOR_NCP:
109 DeinitNcpMode();
110 break;
111 default:
112 DieNow("Unknown coprocessor type!");
113 break;
114 }
115
116 mHost->Deinit();
117 }
118
Run(void)119 otbrError Application::Run(void)
120 {
121 otbrError error = OTBR_ERROR_NONE;
122
123 otbrLogInfo("Thread Border Router started on AIL %s.", mBackboneInterfaceName);
124
125 #ifdef HAVE_LIBSYSTEMD
126 if (getenv("SYSTEMD_EXEC_PID") != nullptr)
127 {
128 otbrLogInfo("Notify systemd the service is ready.");
129
130 // Ignored return value as systemd recommends.
131 // See https://www.freedesktop.org/software/systemd/man/sd_notify.html
132 sd_notify(0, "READY=1");
133 }
134 #endif
135
136 #if OTBR_ENABLE_NOTIFY_UPSTART
137 if (getenv("UPSTART_JOB") != nullptr)
138 {
139 otbrLogInfo("Notify Upstart the service is ready.");
140 if (raise(SIGSTOP))
141 {
142 otbrLogWarning("Failed to notify Upstart.");
143 }
144 }
145 #endif
146
147 // allow quitting elegantly
148 signal(SIGTERM, HandleSignal);
149
150 while (!sShouldTerminate)
151 {
152 otbr::MainloopContext mainloop;
153 int rval;
154
155 mainloop.mMaxFd = -1;
156 mainloop.mTimeout = kPollTimeout;
157
158 FD_ZERO(&mainloop.mReadFdSet);
159 FD_ZERO(&mainloop.mWriteFdSet);
160 FD_ZERO(&mainloop.mErrorFdSet);
161
162 MainloopManager::GetInstance().Update(mainloop);
163
164 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
165 &mainloop.mTimeout);
166
167 if (rval >= 0)
168 {
169 MainloopManager::GetInstance().Process(mainloop);
170
171 #if __linux__
172 {
173 const char *newInfraLink = mInfraLinkSelector.Select();
174
175 if (mBackboneInterfaceName != newInfraLink)
176 {
177 error = OTBR_ERROR_INFRA_LINK_CHANGED;
178 break;
179 }
180 }
181 #endif
182 }
183 else if (errno != EINTR)
184 {
185 error = OTBR_ERROR_ERRNO;
186 otbrLogErr("select() failed: %s", strerror(errno));
187 break;
188 }
189 }
190
191 return error;
192 }
193
HandleMdnsState(Mdns::Publisher::State aState)194 void Application::HandleMdnsState(Mdns::Publisher::State aState)
195 {
196 OTBR_UNUSED_VARIABLE(aState);
197
198 #if OTBR_ENABLE_BORDER_AGENT
199 mBorderAgent->HandleMdnsState(aState);
200 #endif
201 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
202 mAdvertisingProxy->HandleMdnsState(aState);
203 #endif
204 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
205 mDiscoveryProxy->HandleMdnsState(aState);
206 #endif
207 #if OTBR_ENABLE_TREL
208 mTrelDnssd->HandleMdnsState(aState);
209 #endif
210 }
211
HandleSignal(int aSignal)212 void Application::HandleSignal(int aSignal)
213 {
214 sShouldTerminate = true;
215 signal(aSignal, SIG_DFL);
216 }
217
CreateRcpMode(const std::string & aRestListenAddress,int aRestListenPort)218 void Application::CreateRcpMode(const std::string &aRestListenAddress, int aRestListenPort)
219 {
220 otbr::Ncp::RcpHost &rcpHost = static_cast<otbr::Ncp::RcpHost &>(*mHost);
221 #if OTBR_ENABLE_BORDER_AGENT
222 mBorderAgent = MakeUnique<BorderAgent>(rcpHost, *mPublisher);
223 #endif
224 #if OTBR_ENABLE_BACKBONE_ROUTER
225 mBackboneAgent = MakeUnique<BackboneRouter::BackboneAgent>(rcpHost, mInterfaceName, mBackboneInterfaceName);
226 #endif
227 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
228 mAdvertisingProxy = MakeUnique<AdvertisingProxy>(rcpHost, *mPublisher);
229 #endif
230 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
231 mDiscoveryProxy = MakeUnique<Dnssd::DiscoveryProxy>(rcpHost, *mPublisher);
232 #endif
233 #if OTBR_ENABLE_TREL
234 mTrelDnssd = MakeUnique<TrelDnssd::TrelDnssd>(rcpHost, *mPublisher);
235 #endif
236 #if OTBR_ENABLE_OPENWRT
237 mUbusAgent = MakeUnique<ubus::UBusAgent>(rcpHost);
238 #endif
239 #if OTBR_ENABLE_REST_SERVER
240 mRestWebServer = MakeUnique<rest::RestWebServer>(rcpHost, aRestListenAddress, aRestListenPort);
241 #endif
242 #if OTBR_ENABLE_VENDOR_SERVER
243 mVendorServer = vendor::VendorServer::newInstance(*this);
244 #endif
245
246 OT_UNUSED_VARIABLE(aRestListenAddress);
247 OT_UNUSED_VARIABLE(aRestListenPort);
248 }
249
InitRcpMode(void)250 void Application::InitRcpMode(void)
251 {
252 #if OTBR_ENABLE_MDNS
253 mPublisher->Start();
254 #endif
255 #if OTBR_ENABLE_BORDER_AGENT
256 // This is for delaying publishing the MeshCoP service until the correct
257 // vendor name and OUI etc. are correctly set by BorderAgent::SetMeshCopServiceValues()
258 #if OTBR_STOP_BORDER_AGENT_ON_INIT
259 mBorderAgent->SetEnabled(false);
260 #else
261 mBorderAgent->SetEnabled(true);
262 #endif
263 #endif
264 #if OTBR_ENABLE_BACKBONE_ROUTER
265 mBackboneAgent->Init();
266 #endif
267 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
268 mAdvertisingProxy->SetEnabled(true);
269 #endif
270 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
271 mDiscoveryProxy->SetEnabled(true);
272 #endif
273 #if OTBR_ENABLE_OPENWRT
274 mUbusAgent->Init();
275 #endif
276 #if OTBR_ENABLE_REST_SERVER
277 mRestWebServer->Init();
278 #endif
279 #if OTBR_ENABLE_DBUS_SERVER
280 mDBusAgent->Init(*mBorderAgent);
281 #endif
282 #if OTBR_ENABLE_VENDOR_SERVER
283 mVendorServer->Init();
284 #endif
285 }
286
DeinitRcpMode(void)287 void Application::DeinitRcpMode(void)
288 {
289 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
290 mAdvertisingProxy->SetEnabled(false);
291 #endif
292 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
293 mDiscoveryProxy->SetEnabled(false);
294 #endif
295 #if OTBR_ENABLE_BORDER_AGENT
296 mBorderAgent->SetEnabled(false);
297 #endif
298 #if OTBR_ENABLE_MDNS
299 mPublisher->Stop();
300 #endif
301 }
302
InitNcpMode(void)303 void Application::InitNcpMode(void)
304 {
305 #if OTBR_ENABLE_DBUS_SERVER
306 mDBusAgent->Init(*mBorderAgent);
307 #endif
308 }
309
DeinitNcpMode(void)310 void Application::DeinitNcpMode(void)
311 {
312 /* empty */
313 }
314
315 } // namespace otbr
316