1 /*
2 * Copyright (c) 2016, 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 * This file implements DHCPv6 Server.
32 */
33
34 #include "dhcp6_server.hpp"
35
36 #if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE
37
38 #include "common/array.hpp"
39 #include "common/as_core_type.hpp"
40 #include "common/code_utils.hpp"
41 #include "common/encoding.hpp"
42 #include "common/instance.hpp"
43 #include "common/locator_getters.hpp"
44 #include "common/log.hpp"
45 #include "thread/mle.hpp"
46 #include "thread/thread_netif.hpp"
47
48 namespace ot {
49 namespace Dhcp6 {
50
51 RegisterLogModule("Dhcp6Server");
52
Server(Instance & aInstance)53 Server::Server(Instance &aInstance)
54 : InstanceLocator(aInstance)
55 , mSocket(aInstance)
56 , mPrefixAgentsCount(0)
57 , mPrefixAgentsMask(0)
58 {
59 memset(mPrefixAgents, 0, sizeof(mPrefixAgents));
60 }
61
UpdateService(void)62 Error Server::UpdateService(void)
63 {
64 Error error = kErrorNone;
65 uint16_t rloc16 = Get<Mle::MleRouter>().GetRloc16();
66 NetworkData::Iterator iterator;
67 NetworkData::OnMeshPrefixConfig config;
68 Lowpan::Context lowpanContext;
69
70 // remove dhcp agent aloc and prefix delegation
71 for (PrefixAgent &prefixAgent : mPrefixAgents)
72 {
73 bool found = false;
74
75 if (!prefixAgent.IsValid())
76 {
77 continue;
78 }
79
80 iterator = NetworkData::kIteratorInit;
81
82 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, rloc16, config) == kErrorNone)
83 {
84 if (!(config.mDhcp || config.mConfigure))
85 {
86 continue;
87 }
88
89 error = Get<NetworkData::Leader>().GetContext(prefixAgent.GetPrefixAsAddress(), lowpanContext);
90
91 if ((error == kErrorNone) && (prefixAgent.GetContextId() == lowpanContext.mContextId))
92 {
93 // still in network data
94 found = true;
95 break;
96 }
97 }
98
99 if (!found)
100 {
101 Get<ThreadNetif>().RemoveUnicastAddress(prefixAgent.GetAloc());
102 prefixAgent.Clear();
103 mPrefixAgentsCount--;
104 }
105 }
106
107 // add dhcp agent aloc and prefix delegation
108 iterator = NetworkData::kIteratorInit;
109
110 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, rloc16, config) == kErrorNone)
111 {
112 if (!(config.mDhcp || config.mConfigure))
113 {
114 continue;
115 }
116
117 error = Get<NetworkData::Leader>().GetContext(AsCoreType(&config.mPrefix.mPrefix), lowpanContext);
118
119 if (error == kErrorNone)
120 {
121 AddPrefixAgent(config.GetPrefix(), lowpanContext);
122 }
123 }
124
125 if (mPrefixAgentsCount > 0)
126 {
127 Start();
128 }
129 else
130 {
131 Stop();
132 }
133
134 return error;
135 }
136
Start(void)137 void Server::Start(void)
138 {
139 VerifyOrExit(!mSocket.IsOpen());
140
141 IgnoreError(mSocket.Open(&Server::HandleUdpReceive, this));
142 IgnoreError(mSocket.Bind(kDhcpServerPort));
143
144 exit:
145 return;
146 }
147
Stop(void)148 void Server::Stop(void)
149 {
150 IgnoreError(mSocket.Close());
151 }
152
AddPrefixAgent(const Ip6::Prefix & aIp6Prefix,const Lowpan::Context & aContext)153 void Server::AddPrefixAgent(const Ip6::Prefix &aIp6Prefix, const Lowpan::Context &aContext)
154 {
155 Error error = kErrorNone;
156 PrefixAgent *newEntry = nullptr;
157
158 for (PrefixAgent &prefixAgent : mPrefixAgents)
159 {
160 if (!prefixAgent.IsValid())
161 {
162 newEntry = &prefixAgent;
163 }
164 else if (prefixAgent.GetPrefix() == aIp6Prefix)
165 {
166 // already added
167 ExitNow();
168 }
169 }
170
171 VerifyOrExit(newEntry != nullptr, error = kErrorNoBufs);
172
173 newEntry->Set(aIp6Prefix, Get<Mle::MleRouter>().GetMeshLocalPrefix(), aContext.mContextId);
174 Get<ThreadNetif>().AddUnicastAddress(newEntry->GetAloc());
175 mPrefixAgentsCount++;
176
177 exit:
178
179 if (error != kErrorNone)
180 {
181 LogNote("Failed to add DHCPv6 prefix agent: %s", ErrorToString(error));
182 }
183 }
184
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)185 void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
186 {
187 static_cast<Server *>(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo));
188 }
189
HandleUdpReceive(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)190 void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
191 {
192 Header header;
193
194 SuccessOrExit(aMessage.Read(aMessage.GetOffset(), header));
195 aMessage.MoveOffset(sizeof(header));
196
197 // discard if not solicit type
198 VerifyOrExit((header.GetType() == kTypeSolicit));
199
200 ProcessSolicit(aMessage, aMessageInfo.GetPeerAddr(), header.GetTransactionId());
201
202 exit:
203 return;
204 }
205
ProcessSolicit(Message & aMessage,const Ip6::Address & aDst,const TransactionId & aTransactionId)206 void Server::ProcessSolicit(Message &aMessage, const Ip6::Address &aDst, const TransactionId &aTransactionId)
207 {
208 IaNa iana;
209 ClientIdentifier clientIdentifier;
210 uint16_t optionOffset;
211 uint16_t offset = aMessage.GetOffset();
212 uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
213
214 // Client Identifier (discard if not present)
215 VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, kOptionClientIdentifier)) > 0);
216 SuccessOrExit(ProcessClientIdentifier(aMessage, optionOffset, clientIdentifier));
217
218 // Server Identifier (assuming Rapid Commit, discard if present)
219 VerifyOrExit(FindOption(aMessage, offset, length, kOptionServerIdentifier) == 0);
220
221 // Rapid Commit (assuming Rapid Commit, discard if not present)
222 VerifyOrExit(FindOption(aMessage, offset, length, kOptionRapidCommit) > 0);
223
224 // Elapsed Time if present
225 if ((optionOffset = FindOption(aMessage, offset, length, kOptionElapsedTime)) > 0)
226 {
227 SuccessOrExit(ProcessElapsedTime(aMessage, optionOffset));
228 }
229
230 // IA_NA (discard if not present)
231 VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, kOptionIaNa)) > 0);
232 SuccessOrExit(ProcessIaNa(aMessage, optionOffset, iana));
233
234 SuccessOrExit(SendReply(aDst, aTransactionId, clientIdentifier, iana));
235
236 exit:
237 return;
238 }
239
FindOption(Message & aMessage,uint16_t aOffset,uint16_t aLength,Code aCode)240 uint16_t Server::FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Code aCode)
241 {
242 uint16_t end = aOffset + aLength;
243 uint16_t rval = 0;
244
245 while (aOffset <= end)
246 {
247 Option option;
248
249 SuccessOrExit(aMessage.Read(aOffset, option));
250
251 if (option.GetCode() == aCode)
252 {
253 ExitNow(rval = aOffset);
254 }
255
256 aOffset += sizeof(option) + option.GetLength();
257 }
258
259 exit:
260 return rval;
261 }
ProcessClientIdentifier(Message & aMessage,uint16_t aOffset,ClientIdentifier & aClientId)262 Error Server::ProcessClientIdentifier(Message &aMessage, uint16_t aOffset, ClientIdentifier &aClientId)
263 {
264 Error error = kErrorNone;
265
266 SuccessOrExit(error = aMessage.Read(aOffset, aClientId));
267 VerifyOrExit((aClientId.GetLength() == sizeof(aClientId) - sizeof(Option)) &&
268 (aClientId.GetDuidType() == kDuidLinkLayerAddress) &&
269 (aClientId.GetDuidHardwareType() == kHardwareTypeEui64),
270 error = kErrorParse);
271 exit:
272 return error;
273 }
274
ProcessElapsedTime(Message & aMessage,uint16_t aOffset)275 Error Server::ProcessElapsedTime(Message &aMessage, uint16_t aOffset)
276 {
277 Error error = kErrorNone;
278 ElapsedTime option;
279
280 SuccessOrExit(error = aMessage.Read(aOffset, option));
281 VerifyOrExit(option.GetLength() == sizeof(option) - sizeof(Option), error = kErrorParse);
282 exit:
283 return error;
284 }
285
ProcessIaNa(Message & aMessage,uint16_t aOffset,IaNa & aIaNa)286 Error Server::ProcessIaNa(Message &aMessage, uint16_t aOffset, IaNa &aIaNa)
287 {
288 Error error = kErrorNone;
289 uint16_t optionOffset;
290 uint16_t length;
291
292 SuccessOrExit(error = aMessage.Read(aOffset, aIaNa));
293
294 aOffset += sizeof(aIaNa);
295 length = aIaNa.GetLength() + sizeof(Option) - sizeof(IaNa);
296
297 VerifyOrExit(length <= aMessage.GetLength() - aOffset, error = kErrorParse);
298
299 mPrefixAgentsMask = 0;
300
301 while (length > 0)
302 {
303 VerifyOrExit((optionOffset = FindOption(aMessage, aOffset, length, kOptionIaAddress)) > 0);
304 SuccessOrExit(error = ProcessIaAddress(aMessage, optionOffset));
305
306 length -= ((optionOffset - aOffset) + sizeof(IaAddress));
307 aOffset = optionOffset + sizeof(IaAddress);
308 }
309
310 exit:
311 return error;
312 }
313
ProcessIaAddress(Message & aMessage,uint16_t aOffset)314 Error Server::ProcessIaAddress(Message &aMessage, uint16_t aOffset)
315 {
316 Error error = kErrorNone;
317 IaAddress option;
318
319 SuccessOrExit(error = aMessage.Read(aOffset, option));
320 VerifyOrExit(option.GetLength() == sizeof(option) - sizeof(Option), error = kErrorParse);
321
322 // mask matching prefix
323 for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++)
324 {
325 if (mPrefixAgents[i].IsValid() && mPrefixAgents[i].IsPrefixMatch(option.GetAddress()))
326 {
327 mPrefixAgentsMask |= (1 << i);
328 break;
329 }
330 }
331
332 exit:
333 return error;
334 }
335
SendReply(const Ip6::Address & aDst,const TransactionId & aTransactionId,ClientIdentifier & aClientId,IaNa & aIaNa)336 Error Server::SendReply(const Ip6::Address & aDst,
337 const TransactionId &aTransactionId,
338 ClientIdentifier & aClientId,
339 IaNa & aIaNa)
340 {
341 Error error = kErrorNone;
342 Ip6::MessageInfo messageInfo;
343 Message * message;
344
345 VerifyOrExit((message = mSocket.NewMessage(0)) != nullptr, error = kErrorNoBufs);
346 SuccessOrExit(error = AppendHeader(*message, aTransactionId));
347 SuccessOrExit(error = AppendServerIdentifier(*message));
348 SuccessOrExit(error = AppendClientIdentifier(*message, aClientId));
349 SuccessOrExit(error = AppendIaNa(*message, aIaNa));
350 SuccessOrExit(error = AppendStatusCode(*message, kStatusSuccess));
351 SuccessOrExit(error = AppendIaAddress(*message, aClientId));
352 SuccessOrExit(error = AppendRapidCommit(*message));
353
354 messageInfo.SetPeerAddr(aDst);
355 messageInfo.SetPeerPort(kDhcpClientPort);
356 SuccessOrExit(error = mSocket.SendTo(*message, messageInfo));
357
358 exit:
359 FreeMessageOnError(message, error);
360 return error;
361 }
362
AppendHeader(Message & aMessage,const TransactionId & aTransactionId)363 Error Server::AppendHeader(Message &aMessage, const TransactionId &aTransactionId)
364 {
365 Header header;
366
367 header.Clear();
368 header.SetType(kTypeReply);
369 header.SetTransactionId(aTransactionId);
370 return aMessage.Append(header);
371 }
372
AppendClientIdentifier(Message & aMessage,ClientIdentifier & aClientId)373 Error Server::AppendClientIdentifier(Message &aMessage, ClientIdentifier &aClientId)
374 {
375 return aMessage.Append(aClientId);
376 }
377
AppendServerIdentifier(Message & aMessage)378 Error Server::AppendServerIdentifier(Message &aMessage)
379 {
380 Error error = kErrorNone;
381 ServerIdentifier option;
382 Mac::ExtAddress eui64;
383
384 Get<Radio>().GetIeeeEui64(eui64);
385
386 option.Init();
387 option.SetDuidType(kDuidLinkLayerAddress);
388 option.SetDuidHardwareType(kHardwareTypeEui64);
389 option.SetDuidLinkLayerAddress(eui64);
390 SuccessOrExit(error = aMessage.Append(option));
391
392 exit:
393 return error;
394 }
395
AppendIaNa(Message & aMessage,IaNa & aIaNa)396 Error Server::AppendIaNa(Message &aMessage, IaNa &aIaNa)
397 {
398 Error error = kErrorNone;
399 uint16_t length = 0;
400
401 if (mPrefixAgentsMask)
402 {
403 for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++)
404 {
405 if (mPrefixAgentsMask & (1 << i))
406 {
407 length += sizeof(IaAddress);
408 }
409 }
410 }
411 else
412 {
413 length += sizeof(IaAddress) * mPrefixAgentsCount;
414 }
415
416 length += sizeof(IaNa) + sizeof(StatusCode) - sizeof(Option);
417
418 aIaNa.SetLength(length);
419 aIaNa.SetT1(IaNa::kDefaultT1);
420 aIaNa.SetT2(IaNa::kDefaultT2);
421 SuccessOrExit(error = aMessage.Append(aIaNa));
422
423 exit:
424 return error;
425 }
426
AppendStatusCode(Message & aMessage,Status aStatusCode)427 Error Server::AppendStatusCode(Message &aMessage, Status aStatusCode)
428 {
429 StatusCode option;
430
431 option.Init();
432 option.SetStatusCode(aStatusCode);
433 return aMessage.Append(option);
434 }
435
AppendIaAddress(Message & aMessage,ClientIdentifier & aClientId)436 Error Server::AppendIaAddress(Message &aMessage, ClientIdentifier &aClientId)
437 {
438 Error error = kErrorNone;
439
440 if (mPrefixAgentsMask)
441 {
442 // if specified, only apply specified prefixes
443 for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++)
444 {
445 if (mPrefixAgentsMask & (1 << i))
446 {
447 SuccessOrExit(error = AddIaAddress(aMessage, mPrefixAgents[i].GetPrefixAsAddress(), aClientId));
448 }
449 }
450 }
451 else
452 {
453 // if not specified, apply all configured prefixes
454 for (const PrefixAgent &prefixAgent : mPrefixAgents)
455 {
456 if (prefixAgent.IsValid())
457 {
458 SuccessOrExit(error = AddIaAddress(aMessage, prefixAgent.GetPrefixAsAddress(), aClientId));
459 }
460 }
461 }
462
463 exit:
464 return error;
465 }
466
AddIaAddress(Message & aMessage,const Ip6::Address & aPrefix,ClientIdentifier & aClientId)467 Error Server::AddIaAddress(Message &aMessage, const Ip6::Address &aPrefix, ClientIdentifier &aClientId)
468 {
469 Error error = kErrorNone;
470 IaAddress option;
471
472 option.Init();
473 option.GetAddress().SetPrefix(aPrefix.mFields.m8, OT_IP6_PREFIX_BITSIZE);
474 option.GetAddress().GetIid().SetFromExtAddress(aClientId.GetDuidLinkLayerAddress());
475 option.SetPreferredLifetime(IaAddress::kDefaultPreferredLifetime);
476 option.SetValidLifetime(IaAddress::kDefaultValidLiftetime);
477 SuccessOrExit(error = aMessage.Append(option));
478
479 exit:
480 return error;
481 }
482
AppendRapidCommit(Message & aMessage)483 Error Server::AppendRapidCommit(Message &aMessage)
484 {
485 RapidCommit option;
486
487 option.Init();
488 return aMessage.Append(option);
489 }
490
ApplyMeshLocalPrefix(void)491 void Server::ApplyMeshLocalPrefix(void)
492 {
493 for (PrefixAgent &prefixAgent : mPrefixAgents)
494 {
495 if (prefixAgent.IsValid())
496 {
497 PrefixAgent *entry = &prefixAgent;
498
499 Get<ThreadNetif>().RemoveUnicastAddress(entry->GetAloc());
500 entry->GetAloc().GetAddress().SetPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix());
501 Get<ThreadNetif>().AddUnicastAddress(entry->GetAloc());
502 }
503 }
504 }
505
506 } // namespace Dhcp6
507 } // namespace ot
508
509 #endif // OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE
510