• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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