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