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