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