A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
dhcp6-client.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2024 NITK Surathkal
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Kavya Bhat <kavyabhat@gmail.com>
7 *
8 */
9
10#include "dhcp6-client.h"
11
12#include "dhcp6-duid.h"
13
14#include "ns3/address-utils.h"
15#include "ns3/enum.h"
16#include "ns3/icmpv6-l4-protocol.h"
17#include "ns3/ipv6-interface.h"
18#include "ns3/ipv6-l3-protocol.h"
19#include "ns3/ipv6-packet-info-tag.h"
20#include "ns3/ipv6.h"
21#include "ns3/log.h"
22#include "ns3/loopback-net-device.h"
23#include "ns3/mac48-address.h"
24#include "ns3/net-device-container.h"
25#include "ns3/object.h"
26#include "ns3/pointer.h"
27#include "ns3/ptr.h"
28#include "ns3/random-variable-stream.h"
29#include "ns3/simulator.h"
30#include "ns3/socket.h"
31#include "ns3/string.h"
32#include "ns3/trace-source-accessor.h"
33#include "ns3/traced-value.h"
34#include "ns3/trickle-timer.h"
35
36#include <algorithm>
37
38namespace ns3
39{
40
41NS_LOG_COMPONENT_DEFINE("Dhcp6Client");
42
45{
46 static TypeId tid =
47 TypeId("ns3::Dhcp6Client")
49 .AddConstructor<Dhcp6Client>()
50 .SetGroupName("InternetApps")
51 .AddAttribute("Transactions",
52 "A value to be used as the transaction ID.",
53 StringValue("ns3::UniformRandomVariable[Min=0.0|Max=1000000.0]"),
56 .AddAttribute("SolicitJitter",
57 "The jitter in ms that a node waits before sending any solicitation. By "
58 "default, the model will wait for a duration in ms defined by a uniform "
59 "random-variable between 0 and SolicitJitter. This is equivalent to"
60 "SOL_MAX_DELAY (RFC 8415, Section 7.6).",
61 StringValue("ns3::UniformRandomVariable[Min=0.0|Max=1000.0]"),
64 .AddAttribute("IaidValue",
65 "The identifier for a new IA created by a client.",
66 StringValue("ns3::UniformRandomVariable[Min=0.0|Max=1000000.0]"),
69 .AddAttribute("SolicitInterval",
70 "Time after which the client resends the Solicit."
71 "Equivalent to SOL_MAX_RT (RFC 8415, Section 7.6)",
72 TimeValue(Seconds(100)),
75 .AddAttribute("DuidType",
76 "Configure the type of DUID used by the client.",
80 "LLT",
82 "EN",
84 "LL",
86 "UUID"))
87 .AddAttribute("DuidEnIdentifierLength",
88 "Length of the identifier of the DUID-EN.",
89 IntegerValue(8),
92 .AddTraceSource("NewLease",
93 "The client has obtained a lease",
95 "ns3::Ipv6Address::TracedCallback");
96 return tid;
97}
98
103
104void
106{
107 NS_LOG_FUNCTION(this);
108
109 for (auto& itr : m_interfaces)
110 {
111 itr.second->Cleanup();
112 itr.second = nullptr;
113 }
114 m_interfaces.clear();
115 m_iaidMap.clear();
116
118}
119
120int64_t
122{
123 NS_LOG_FUNCTION(this << stream);
124 m_solicitJitter->SetStream(stream);
125 m_transactionId->SetStream(stream + 1);
126 m_iaidStream->SetStream(stream + 2);
127 return 3;
128}
129
130bool
132{
133 Ptr<Packet> packet = Create<Packet>();
134 Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
135 int32_t ifIndex = ipv6->GetInterfaceForDevice(iDev);
136 uint32_t clientTransactId = m_interfaces[ifIndex]->m_transactId;
137 uint32_t receivedTransactId = header.GetTransactId();
138
139 if (clientTransactId != receivedTransactId)
140 {
141 return false;
142 }
143
144 Duid clientDuid = header.GetClientIdentifier().GetDuid();
145 NS_ASSERT_MSG(clientDuid == m_clientDuid, "Client DUID mismatch.");
146
147 m_interfaces[ifIndex]->m_serverDuid = header.GetServerIdentifier().GetDuid();
148 return true;
149}
150
151void
153{
154 NS_LOG_FUNCTION(this << iDev << header << server);
155
156 Ptr<Packet> packet = Create<Packet>();
157 Dhcp6Header requestHeader;
158 requestHeader.ResetOptions();
160
161 // TODO: Use min, max for GetValue
162 Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
163 int32_t ifIndex = ipv6->GetInterfaceForDevice(iDev);
164 const auto& dhcpInterface = m_interfaces[ifIndex];
165 dhcpInterface->m_transactId = static_cast<uint32_t>(m_transactionId->GetValue());
166 requestHeader.SetTransactId(dhcpInterface->m_transactId);
167
168 // Add Client Identifier Option.
169 requestHeader.AddClientIdentifier(m_clientDuid);
170
171 // Add Server Identifier Option, copied from the received header.
172 Duid serverDuid = header.GetServerIdentifier().GetDuid();
173 requestHeader.AddServerIdentifier(serverDuid);
174
175 // Add Elapsed Time Option.
176 uint32_t actualElapsedTime =
177 (Simulator::Now() - dhcpInterface->m_msgStartTime).GetMilliSeconds() / 10;
178 uint16_t elapsed = actualElapsedTime > 65535 ? 65535 : actualElapsedTime;
179 requestHeader.AddElapsedTime(elapsed);
180
181 // Add IA_NA option.
182 // Request all addresses from the Advertise message.
183 std::vector<IaOptions> ianaOptionsList = header.GetIanaOptions();
184
185 for (const auto& iaOpt : ianaOptionsList)
186 {
187 // Iterate through the offered addresses.
188 // Current approach: Try to accept all offers.
189 for (const auto& iaAddrOpt : iaOpt.m_iaAddressOption)
190 {
191 requestHeader.AddIanaOption(iaOpt.GetIaid(), iaOpt.GetT1(), iaOpt.GetT2());
192 requestHeader.AddAddress(iaOpt.GetIaid(),
193 iaAddrOpt.GetIaAddress(),
194 iaAddrOpt.GetPreferredLifetime(),
195 iaAddrOpt.GetValidLifetime());
196
197 NS_LOG_DEBUG("Requesting " << iaAddrOpt.GetIaAddress());
198 }
199 }
200
201 // Add Option Request.
203
204 packet->AddHeader(requestHeader);
205
206 // TODO: Handle server unicast option.
207
208 // Send the request message.
209 dhcpInterface->m_state = State::WAIT_REPLY;
210 if (dhcpInterface->m_socket->SendTo(
211 packet,
212 0,
214 {
215 NS_LOG_INFO("DHCPv6 client: Request sent.");
216 }
217 else
218 {
219 NS_LOG_INFO("DHCPv6 client: Error while sending Request.");
220 }
221}
222
231
233{
234 m_socket = nullptr;
235 m_client = nullptr;
236
237 m_iaids.clear();
238 m_solicitTimer.Stop();
239 m_declinedAddresses.clear();
240
241 m_renewEvent.Cancel();
242 m_rebindEvent.Cancel();
243
244 for (auto& itr : m_releaseEvent)
245 {
246 itr.Cancel();
247 }
248}
249
250void
252{
253 NS_LOG_DEBUG("Accepting address " << offeredAddress);
254
255 // Check that the offered address is from DHCPv6.
256 bool found = false;
257 for (auto& addr : m_offeredAddresses)
258 {
259 if (addr == offeredAddress)
260 {
261 found = true;
262 break;
263 }
264 }
265
266 if (found)
267 {
269
270 // Notify the new lease.
271 m_client->m_newLease(offeredAddress);
272 }
273}
274
275void
277{
278 NS_LOG_DEBUG("Address to be declined " << offeredAddress);
279
280 // Check that the offered address is from DHCPv6.
281 bool found = false;
282 for (auto& addr : m_offeredAddresses)
283 {
284 if (addr == offeredAddress)
285 {
286 found = true;
287 break;
288 }
289 }
290
291 if (found)
292 {
293 m_declinedAddresses.emplace_back(offeredAddress);
295 {
296 DeclineOffer();
297 }
298 }
299}
300
301void
303{
304 if (m_declinedAddresses.empty())
305 {
306 return;
307 }
308
309 // Cancel all scheduled Release, Renew, Rebind events.
310 m_renewEvent.Cancel();
311 m_rebindEvent.Cancel();
312 for (auto itr : m_releaseEvent)
313 {
314 itr.Cancel();
315 }
316
317 Dhcp6Header declineHeader;
318 Ptr<Packet> packet = Create<Packet>();
319
320 // Remove address associations.
321 for (const auto& offer : m_declinedAddresses)
322 {
323 uint32_t iaid = m_client->m_iaidMap[offer];
324
325 // IA_NA option, IA address option
326 declineHeader.AddIanaOption(iaid, m_renewTime.GetSeconds(), m_rebindTime.GetSeconds());
327 declineHeader.AddAddress(iaid,
328 offer,
329 m_prefLifetime.GetSeconds(),
330 m_validLifetime.GetSeconds());
331 NS_LOG_DEBUG("Declining address " << offer);
332 }
333
334 m_transactId = static_cast<uint32_t>(m_client->m_transactionId->GetValue());
335 declineHeader.SetTransactId(m_transactId);
337
338 // Add client identifier option
339 declineHeader.AddClientIdentifier(m_client->m_clientDuid);
340
341 // Add server identifier option
342 declineHeader.AddServerIdentifier(m_serverDuid);
343
345 declineHeader.AddElapsedTime(0);
346
347 packet->AddHeader(declineHeader);
348 if ((m_socket->SendTo(packet,
349 0,
352 {
353 NS_LOG_INFO("DHCPv6 client: Decline sent");
354 }
355 else
356 {
357 NS_LOG_INFO("DHCPv6 client: Error while sending Decline");
358 }
359
361}
362
363void
365{
366 m_solicitTimer.Stop();
367
368 for (auto& releaseEvent : m_releaseEvent)
369 {
370 releaseEvent.Cancel();
371 }
372
373 m_renewEvent.Cancel();
374 m_rebindEvent.Cancel();
375
376 m_socket->SetRecvCallback(MakeNullCallback<void, Ptr<Socket>>());
377 m_socket->Close();
378
379 Ptr<Ipv6> ipv6 = m_client->GetNode()->GetObject<Ipv6>();
380
381 // Remove the offered IPv6 addresses from the interface.
382 for (uint32_t i = 0; i < ipv6->GetNAddresses(m_interfaceIndex); i++)
383 {
384 for (const auto& addr : m_offeredAddresses)
385 {
386 if (ipv6->GetAddress(m_interfaceIndex, i) == addr)
387 {
388 ipv6->RemoveAddress(m_interfaceIndex, i);
389 break;
390 }
391 }
392 }
393 m_offeredAddresses.clear();
394
397
398 icmpv6->TraceDisconnectWithoutContext("DadSuccess", m_acceptedAddressCb.value());
400 icmpv6->TraceDisconnectWithoutContext("DadFailure", m_declinedAddressCb.value());
402}
403
404void
406 Dhcp6Header header,
407 Inet6SocketAddress server) const
408{
409 // Read Status Code option.
411
412 NS_LOG_DEBUG("Received status " << (uint16_t)statusCode << " from DHCPv6 server");
413 if (statusCode == Options::StatusCodeValues::Success)
414 {
415 NS_LOG_INFO("DHCPv6 client: Server bindings updated successfully.");
416 }
417 else
418 {
419 NS_LOG_INFO("DHCPv6 client: Server bindings update failed.");
420 }
421}
422
423void
425{
426 NS_LOG_FUNCTION(this << iDev << header << server);
427
428 Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
429 int32_t ifIndex = ipv6->GetInterfaceForDevice(iDev);
430
431 Ptr<InterfaceConfig> dhcpInterface = m_interfaces[ifIndex];
432
433 // Read IA_NA options.
434 std::vector<IaOptions> ianaOptionsList = header.GetIanaOptions();
435
436 dhcpInterface->m_declinedAddresses.clear();
437
438 Time earliestRebind{Time::Max()};
439 Time earliestRenew{Time::Max()};
440 std::vector<uint32_t> iaidList;
441
442 for (const auto& iaOpt : ianaOptionsList)
443 {
444 // Iterate through the offered addresses.
445 // Current approach: Try to accept all offers.
446 for (const auto& iaAddrOpt : iaOpt.m_iaAddressOption)
447 {
448 Ipv6Address offeredAddress = iaAddrOpt.GetIaAddress();
449
450 // TODO: In Linux, all leased addresses seem to be /128. Double-check this.
451 Ipv6InterfaceAddress addr(offeredAddress, 128);
452 ipv6->AddAddress(ifIndex, addr);
453 ipv6->SetUp(ifIndex);
454
455 // Set the preferred and valid lifetimes.
456 dhcpInterface->m_prefLifetime = Seconds(iaAddrOpt.GetPreferredLifetime());
457 dhcpInterface->m_validLifetime = Seconds(iaAddrOpt.GetValidLifetime());
458
459 // Add the IPv6 address - IAID association.
460 m_iaidMap[offeredAddress] = iaOpt.GetIaid();
461
462 // TODO: Check whether Release event happens for each address.
463 dhcpInterface->m_releaseEvent.emplace_back(
464 Simulator::Schedule(dhcpInterface->m_validLifetime,
466 this,
467 offeredAddress));
468
469 dhcpInterface->m_offeredAddresses.push_back(offeredAddress);
470 }
471
472 earliestRenew = std::min(earliestRenew, Seconds(iaOpt.GetT1()));
473 earliestRebind = std::min(earliestRebind, Seconds(iaOpt.GetT2()));
474 iaidList.emplace_back(iaOpt.GetIaid());
475 }
476
477 // The renew and rebind events are scheduled for the earliest time across
478 // all IA_NA options. RFC 8415, Section 18.2.4.
479 dhcpInterface->m_renewTime = earliestRenew;
480 dhcpInterface->m_renewEvent.Cancel();
481 dhcpInterface->m_renewEvent =
482 Simulator::Schedule(dhcpInterface->m_renewTime, &Dhcp6Client::SendRenew, this, ifIndex);
483
484 // Set the rebind timer and schedule the event.
485 dhcpInterface->m_rebindTime = earliestRebind;
486 dhcpInterface->m_rebindEvent.Cancel();
487 dhcpInterface->m_rebindEvent =
488 Simulator::Schedule(dhcpInterface->m_rebindTime, &Dhcp6Client::SendRebind, this, ifIndex);
489
490 int32_t interfaceId = ipv6->GetInterfaceForDevice(iDev);
492 ipv6->GetProtocol(Icmpv6L4Protocol::GetStaticProtocolNumber(), interfaceId));
493
494 // If DAD fails, the offer is declined.
495
496 if (!dhcpInterface->m_acceptedAddressCb.has_value())
497 {
498 dhcpInterface->m_acceptedAddressCb =
500 icmpv6->TraceConnectWithoutContext("DadSuccess",
501 dhcpInterface->m_acceptedAddressCb.value());
502 }
503
504 if (!dhcpInterface->m_declinedAddressCb.has_value())
505 {
506 dhcpInterface->m_declinedAddressCb =
508 icmpv6->TraceConnectWithoutContext("DadFailure",
509 dhcpInterface->m_declinedAddressCb.value());
510 }
511}
512
513void
515{
516 NS_LOG_FUNCTION(this);
517
518 Dhcp6Header header;
519 Ptr<Packet> packet = Create<Packet>();
520
521 m_interfaces[dhcpInterfaceIndex]->m_transactId =
522 static_cast<uint32_t>(m_transactionId->GetValue());
523
524 header.SetTransactId(m_interfaces[dhcpInterfaceIndex]->m_transactId);
526
527 // Add client identifier option
529
530 // Add server identifier option
531 header.AddServerIdentifier(m_interfaces[dhcpInterfaceIndex]->m_serverDuid);
532
533 m_interfaces[dhcpInterfaceIndex]->m_msgStartTime = Simulator::Now();
534 header.AddElapsedTime(0);
535
536 // Add IA_NA options.
537 for (const auto& iaidRenew : m_interfaces[dhcpInterfaceIndex]->m_iaids)
538 {
539 header.AddIanaOption(iaidRenew,
540 m_interfaces[dhcpInterfaceIndex]->m_renewTime.GetSeconds(),
541 m_interfaces[dhcpInterfaceIndex]->m_rebindTime.GetSeconds());
542
543 // Iterate through the IPv6Address - IAID map, and add all addresses
544 // that match the IAID to be renewed.
545 for (const auto& itr : m_iaidMap)
546 {
547 Ipv6Address address = itr.first;
548 uint32_t iaid = itr.second;
549 if (iaid == iaidRenew)
550 {
551 header.AddAddress(iaidRenew,
552 address,
553 m_interfaces[dhcpInterfaceIndex]->m_prefLifetime.GetSeconds(),
554 m_interfaces[dhcpInterfaceIndex]->m_validLifetime.GetSeconds());
555 }
556 }
557
558 NS_LOG_DEBUG("Renewing addresses in IAID " << iaidRenew);
559 }
560
561 // Add Option Request option.
563
564 packet->AddHeader(header);
565 if ((m_interfaces[dhcpInterfaceIndex]->m_socket->SendTo(
566 packet,
567 0,
569 0)
570 {
571 NS_LOG_INFO("DHCPv6 client: Renew sent");
572 }
573 else
574 {
575 NS_LOG_INFO("DHCPv6 client: Error while sending Renew");
576 }
577
578 m_interfaces[dhcpInterfaceIndex]->m_state = State::WAIT_REPLY;
579}
580
581void
583{
584 NS_LOG_FUNCTION(this);
585
586 Dhcp6Header header;
587 Ptr<Packet> packet = Create<Packet>();
588
589 m_interfaces[dhcpInterfaceIndex]->m_transactId =
590 static_cast<uint32_t>(m_transactionId->GetValue());
591
592 header.SetTransactId(m_interfaces[dhcpInterfaceIndex]->m_transactId);
594
595 // Add client identifier option
597
598 m_interfaces[dhcpInterfaceIndex]->m_msgStartTime = Simulator::Now();
599 header.AddElapsedTime(0);
600
601 // Add IA_NA options.
602 for (const auto& iaid : m_interfaces[dhcpInterfaceIndex]->m_iaids)
603 {
604 header.AddIanaOption(iaid,
605 m_interfaces[dhcpInterfaceIndex]->m_renewTime.GetSeconds(),
606 m_interfaces[dhcpInterfaceIndex]->m_rebindTime.GetSeconds());
607
608 NS_LOG_DEBUG("Rebinding addresses in IAID " << iaid);
609 }
610
611 // Add Option Request option.
613
614 packet->AddHeader(header);
615 if ((m_interfaces[dhcpInterfaceIndex]->m_socket->SendTo(
616 packet,
617 0,
619 0)
620 {
621 NS_LOG_INFO("DHCPv6 client: Rebind sent.");
622 }
623 else
624 {
625 NS_LOG_INFO("DHCPv6 client: Error while sending Rebind");
626 }
627
628 m_interfaces[dhcpInterfaceIndex]->m_state = State::WAIT_REPLY;
629}
630
631void
633{
634 NS_LOG_FUNCTION(this);
635
636 Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
637
638 Dhcp6Header header;
639 Ptr<Packet> packet = Create<Packet>();
640
641 for (const auto& itr : m_interfaces)
642 {
643 uint32_t ifIndex = itr.first;
644 Ptr<InterfaceConfig> dhcpInterface = itr.second;
645
646 dhcpInterface->m_transactId = static_cast<uint32_t>(m_transactionId->GetValue());
647 bool removed = ipv6->RemoveAddress(ifIndex, address);
648
649 if (!removed)
650 {
651 continue;
652 }
653
654 header.SetTransactId(dhcpInterface->m_transactId);
655 header.SetMessageType(Dhcp6Header::MessageType::RELEASE);
656
657 // Add client identifier option
658 header.AddClientIdentifier(m_clientDuid);
659
660 // Add server identifier option
661 header.AddServerIdentifier(dhcpInterface->m_serverDuid);
662
663 dhcpInterface->m_msgStartTime = Simulator::Now();
664 header.AddElapsedTime(0);
665
666 // IA_NA option, IA address option
667 uint32_t iaid = m_iaidMap[address];
668 header.AddIanaOption(iaid,
669 dhcpInterface->m_renewTime.GetSeconds(),
670 dhcpInterface->m_rebindTime.GetSeconds());
671 header.AddAddress(iaid,
672 address,
673 dhcpInterface->m_prefLifetime.GetSeconds(),
674 dhcpInterface->m_validLifetime.GetSeconds());
675
676 NS_LOG_DEBUG("Releasing address " << address);
677
678 packet->AddHeader(header);
679 if ((dhcpInterface->m_socket->SendTo(packet,
680 0,
683 {
684 NS_LOG_INFO("DHCPv6 client: Release sent.");
685 }
686 else
687 {
688 NS_LOG_INFO("DHCPv6 client: Error while sending Release");
689 }
690
691 dhcpInterface->m_state = State::WAIT_REPLY_AFTER_RELEASE;
692 }
693}
694
695void
697{
698 NS_LOG_FUNCTION(this << socket);
699
700 Address from;
701 Ptr<Packet> packet = socket->RecvFrom(from);
702 Dhcp6Header header;
703
705
706 Ipv6PacketInfoTag interfaceInfo;
707 NS_ASSERT_MSG(packet->RemovePacketTag(interfaceInfo),
708 "No incoming interface on DHCPv6 message.");
709
710 uint32_t incomingIf = interfaceInfo.GetRecvIf();
711 Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
712 Ptr<NetDevice> iDev = GetNode()->GetDevice(incomingIf);
713 uint32_t iIf = ipv6->GetInterfaceForDevice(iDev);
714 Ptr<InterfaceConfig> dhcpInterface = m_interfaces[iIf];
715
716 if (packet->RemoveHeader(header) == 0 || !dhcpInterface)
717 {
718 return;
719 }
720 if (dhcpInterface->m_state == State::WAIT_ADVERTISE &&
722 {
723 NS_LOG_INFO("DHCPv6 client: Received Advertise.");
724 dhcpInterface->m_solicitTimer.Stop();
725 bool check = ValidateAdvertise(header, iDev);
726 if (check)
727 {
728 SendRequest(iDev, header, senderAddr);
729 }
730 }
731 if (dhcpInterface->m_state == State::WAIT_REPLY &&
733 {
734 NS_LOG_INFO("DHCPv6 client: Received Reply.");
735
736 dhcpInterface->m_renewEvent.Cancel();
737 dhcpInterface->m_rebindEvent.Cancel();
738 for (auto itr : dhcpInterface->m_releaseEvent)
739 {
740 itr.Cancel();
741 }
742
743 ProcessReply(iDev, header, senderAddr);
744 }
745 if ((dhcpInterface->m_state == State::WAIT_REPLY_AFTER_DECLINE ||
746 dhcpInterface->m_state == State::WAIT_REPLY_AFTER_RELEASE) &&
748 {
749 NS_LOG_INFO("DHCPv6 client: Received Reply.");
750 CheckLeaseStatus(iDev, header, senderAddr);
751 }
752}
753
754void
756{
757 Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
758 Ptr<InterfaceConfig> dhcpInterface = m_interfaces[ifIndex];
759 if (isUp)
760 {
761 NS_LOG_DEBUG("DHCPv6 client: Link up at " << Simulator::Now().As(Time::S));
763 }
764 else
765 {
766 dhcpInterface->Cleanup();
767 m_interfaces[ifIndex] = nullptr;
768 NS_LOG_DEBUG("DHCPv6 client: Link down at " << Simulator::Now().As(Time::S));
769 }
770}
771
772Duid
774{
775 return m_clientDuid;
776}
777
778void
780{
781 Ptr<Node> node = GetNode();
782
783 NS_ASSERT_MSG(node, "Dhcp6Client::StartApplication: cannot get the node from the device.");
784
785 Ptr<Ipv6> ipv6 = node->GetObject<Ipv6>();
786 NS_ASSERT_MSG(ipv6, "Dhcp6Client::StartApplication: node does not have IPv6.");
787
788 NS_LOG_DEBUG("Starting DHCPv6 application on node " << node->GetId());
789
790 // Set DHCPv6 callback for each interface of the node.
791 uint32_t nInterfaces = ipv6->GetNInterfaces();
792
793 // We skip interface 0 because it's the Loopback.
794 for (uint32_t ifIndex = 1; ifIndex < nInterfaces; ifIndex++)
795 {
796 Ptr<NetDevice> device = ipv6->GetNetDevice(ifIndex);
798 ipv6->GetProtocol(Icmpv6L4Protocol::GetStaticProtocolNumber(), ifIndex));
799
800 // If the RA message contains an M flag, the client starts sending Solicits.
801 icmpv6->SetDhcpv6Callback(MakeCallback(&Dhcp6Client::ReceiveMflag, this));
802 }
803}
804
805void
807{
808 NS_LOG_FUNCTION(this);
809
810 Ptr<Node> node = GetNode();
811 Ptr<Ipv6L3Protocol> ipv6l3 = node->GetObject<Ipv6L3Protocol>();
812
813 if (!m_interfaces[recvInterface])
814 {
815 // Create config object if M flag is received for the first time.
817 dhcpInterface->m_client = this;
818 dhcpInterface->m_interfaceIndex = recvInterface;
819 dhcpInterface->m_socket = nullptr;
820 m_interfaces[recvInterface] = dhcpInterface;
821
822 // Add an IAID to the client interface.
823 // Note: There may be multiple IAIDs per interface. We use only one.
824 std::vector<uint32_t> existingIaNaIds;
825 while (true)
826 {
827 uint32_t iaid = m_iaidStream->GetInteger();
828 if (std::find(existingIaNaIds.begin(), existingIaNaIds.end(), iaid) ==
829 existingIaNaIds.end())
830 {
831 dhcpInterface->m_iaids.push_back(iaid);
832 existingIaNaIds.emplace_back(iaid);
833 break;
834 }
835 }
836
837 Ptr<Ipv6Interface> ipv6Interface =
838 node->GetObject<Ipv6L3Protocol>()->GetInterface(recvInterface);
839 ipv6Interface->TraceConnectWithoutContext(
840 "InterfaceStatus",
842 }
843
844 for (const auto& itr : m_interfaces)
845 {
846 uint32_t interface = itr.first;
847 Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
848 Ptr<NetDevice> device = ipv6->GetNetDevice(interface);
849 Ptr<InterfaceConfig> dhcpInterface = itr.second;
850
851 // Check that RA was received on this interface.
852 if (interface == recvInterface && m_interfaces[interface])
853 {
854 if (!m_interfaces[interface]->m_socket)
855 {
856 Ipv6Address linkLocal =
857 ipv6l3->GetInterface(interface)->GetLinkLocalAddress().GetAddress();
858 TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
859
860 Ptr<Socket> socket = Socket::CreateSocket(node, tid);
861 socket->Bind(Inet6SocketAddress(linkLocal, Dhcp6Header::CLIENT_PORT));
862 socket->BindToNetDevice(device);
863 socket->SetRecvPktInfo(true);
864 socket->SetRecvCallback(MakeCallback(&Dhcp6Client::NetHandler, this));
865
866 m_interfaces[interface]->m_socket = socket;
867
868 // Introduce a random delay before sending the Solicit message.
871 this,
872 device);
873
874 uint32_t minInterval = m_solicitInterval.GetSeconds() / 2;
875 dhcpInterface->m_solicitTimer = TrickleTimer(Seconds(minInterval), 4, 1);
876 dhcpInterface->m_solicitTimer.SetFunction(&Dhcp6Client::Boot, this);
877 dhcpInterface->m_solicitTimer.Enable();
878 break;
879 }
880 }
881 }
882}
883
884void
886{
887 Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
888 int32_t ifIndex = ipv6->GetInterfaceForDevice(device);
889 Ptr<InterfaceConfig> dhcpInterface = m_interfaces[ifIndex];
890
891 if (m_clientDuid.IsInvalid())
892 {
893 Ptr<Node> node = GetNode();
894 // We use enterprise-number 0xf00dcafe (totally arbitrary)
895 // The current largest enterprise number is less than 0xffff, which means
896 // we use an unused number.
897 // The list of numbers is at
898 // https://www.iana.org/assignments/enterprise-numbers/
899
901 NS_LOG_INFO("DHCPv6 client DUID created: " << m_clientDuid);
902 }
903
904 Dhcp6Header header;
905 Ptr<Packet> packet = Create<Packet>();
906
907 // Create a unique transaction ID.
908 dhcpInterface->m_transactId = static_cast<uint32_t>(m_transactionId->GetValue());
909
910 header.SetTransactId(dhcpInterface->m_transactId);
912
913 // Store start time of the message exchange.
914 dhcpInterface->m_msgStartTime = Simulator::Now();
915
916 header.AddElapsedTime(0);
919
920 // Add IA_NA option.
921
922 for (auto iaid : dhcpInterface->m_iaids)
923 {
924 header.AddIanaOption(iaid,
925 dhcpInterface->m_renewTime.GetSeconds(),
926 dhcpInterface->m_rebindTime.GetSeconds());
927 }
928
929 packet->AddHeader(header);
930
931 if ((dhcpInterface->m_socket->SendTo(packet,
932 0,
935 {
936 NS_LOG_INFO("DHCPv6 client: Solicit sent");
937 }
938 else
939 {
940 NS_LOG_INFO("DHCPv6 client: Error while sending Solicit");
941 }
942
943 dhcpInterface->m_state = State::WAIT_ADVERTISE;
944}
945
946void
948{
949 NS_LOG_FUNCTION(this);
950
951 for (auto& itr : m_interfaces)
952 {
953 // Close sockets.
954 if (!itr.second)
955 {
956 continue;
957 }
958 itr.second->Cleanup();
959 }
960
961 m_interfaces.clear();
962 m_iaidMap.clear();
963}
964
965} // namespace ns3
a polymophic address class
Definition address.h:114
void DoDispose() override
Destructor implementation.
Ptr< Node > GetNode() const
State m_state
The DHCPv6 state of the client interface.
void DeclineOffer()
Send a Decline message to the DHCPv6 server.
Ptr< Dhcp6Client > m_client
The Dhcp6Client on which the interface is present.
Time m_msgStartTime
Time when message exchange starts.
uint8_t m_nAcceptedAddresses
Number of addresses accepted by client.
Time m_prefLifetime
Preferred lifetime of the address.
std::vector< Ipv6Address > m_declinedAddresses
List of addresses to be declined by the client.
std::optional< Callback< void, const Ipv6Address & > > m_declinedAddressCb
Callback for the declined addresses - needed for cleanup.
Time m_rebindTime
REB_MAX_RT, Time after which client should send a Rebind message.
Duid m_serverDuid
The server DUID.
Ptr< Socket > m_socket
The socket that has been opened for this interface.
void AcceptedAddress(const Ipv6Address &offeredAddress)
Accept the DHCPv6 offer.
std::vector< Ipv6Address > m_offeredAddresses
List of addresses offered to the client.
std::vector< uint32_t > m_iaids
The IAIDs associated with this DHCPv6 client interface.
EventId m_rebindEvent
Event ID for the rebind event.
uint32_t m_transactId
Transaction ID of the client-initiated message.
EventId m_renewEvent
Event ID for the Renew event.
InterfaceConfig()
The default constructor.
std::optional< Callback< void, const Ipv6Address & > > m_acceptedAddressCb
Callback for the accepted addresses - needed for cleanup.
void DeclinedAddress(const Ipv6Address &offeredAddress)
Add a declined address to the list maintained by the client.
TrickleTimer m_solicitTimer
TrickleTimer to schedule Solicit messages.
void Cleanup()
Cleanup the internal callbacks and timers.
Time m_renewTime
REN_MAX_RT, Time after which lease should be renewed.
std::vector< EventId > m_releaseEvent
Store all the Event IDs for the addresses being Released.
uint32_t m_interfaceIndex
The IPv6 interface index of this configuration.
Time m_validLifetime
Valid lifetime of the address.
void CheckLeaseStatus(Ptr< NetDevice > iDev, Dhcp6Header header, Inet6SocketAddress server) const
Check lease status after sending a Decline or Release message.
Duid GetSelfDuid() const
Get the DUID.
std::unordered_map< uint32_t, Ptr< InterfaceConfig > > m_interfaces
Map each interface to its corresponding configuration details.
void ReceiveMflag(uint32_t recvInterface)
Callback for when an M flag is received.
void LinkStateHandler(bool isUp, int32_t ifIndex)
Handle changes in the link state.
std::unordered_map< Ipv6Address, uint32_t > m_iaidMap
Track the IPv6 Address - IAID association.
Ptr< RandomVariableStream > m_solicitJitter
Random jitter before sending the first Solicit.
void SendRelease(Ipv6Address address)
Send a Release message to the DHCPv6 server.
static TypeId GetTypeId()
Get the type ID.
TracedCallback< const Ipv6Address & > m_newLease
Trace the new lease.
bool ValidateAdvertise(Dhcp6Header header, Ptr< NetDevice > iDev)
Verify the incoming advertise message.
void Boot(Ptr< NetDevice > device)
Used to send the Solicit message and start the DHCPv6 client.
Duid::Type m_duidType
DUID type.
void ProcessReply(Ptr< NetDevice > iDev, Dhcp6Header header, Inet6SocketAddress server)
Send a request to the DHCPv6 server.
Ptr< RandomVariableStream > m_transactionId
Random variable to set transaction ID.
void SendRequest(Ptr< NetDevice > iDev, Dhcp6Header header, Inet6SocketAddress server)
Send a request to the DHCPv6 server.
uint16_t m_DuidEnIdentifierLength
DUID-EN identifier length.
void StartApplication() override
Application specific startup code.
void StopApplication() override
Application specific shutdown code.
void NetHandler(Ptr< Socket > socket)
Handles incoming packets from the network.
int64_t AssignStreams(int64_t stream) override
Assign a fixed random variable stream number to the random variables used by this Application object.
Ptr< RandomVariableStream > m_iaidStream
Random variable used to create the IAID.
void SendRebind(uint32_t interfaceIndex)
Send a rebind message to the DHCPv6 server.
void SendRenew(uint32_t interfaceIndex)
Send a renew message to the DHCPv6 server.
Time m_solicitInterval
SOL_MAX_RT, time between solicitations.
Duid m_clientDuid
The client DUID, built on first usage in the Boot() function.
void DoDispose() override
Destructor implementation.
Implements the DHCPv6 header.
IdentifierOption GetClientIdentifier()
Get the client identifier.
void AddElapsedTime(uint16_t timestamp)
Set the elapsed time option.
void AddServerIdentifier(Duid duid)
Add the server identifier option.
void ResetOptions()
Reset all options.
void AddClientIdentifier(Duid duid)
Add the client identifier option.
uint32_t GetTransactId() const
Get the transaction ID.
static const uint16_t CLIENT_PORT
The port number of the DHCPv6 client.
void AddAddress(uint32_t iaid, Ipv6Address address, uint32_t prefLifetime, uint32_t validLifetime)
Add IA address option to the IANA or IATA.
MessageType GetMessageType() const
Get the type of message.
static const uint16_t SERVER_PORT
The port number of the DHCPv6 server.
void AddOptionRequest(Options::OptionType optionType)
Request additional options.
void AddIanaOption(uint32_t iaid, uint32_t t1, uint32_t t2)
Add IANA option.
void SetMessageType(MessageType msgType)
Set the message type.
std::vector< IaOptions > GetIanaOptions()
Get the list of IA_NA options.
IdentifierOption GetServerIdentifier()
Get the server identifier.
StatusCodeOption GetStatusCodeOption()
Get the status code of the operation.
void SetTransactId(uint32_t transactId)
Set the transaction ID.
Implements the unique identifier for DHCPv6.
Definition dhcp6-duid.h:27
Hold variables of type enum.
Definition enum.h:52
static uint16_t GetStaticProtocolNumber()
Get ICMPv6 protocol number.
Duid GetDuid() const
Get the DUID object.
An Inet6 address class.
static Inet6SocketAddress ConvertFrom(const Address &addr)
Convert the address to a InetSocketAddress.
Hold a signed integer type.
Definition integer.h:34
Describes an IPv6 address.
static Ipv6Address GetAllNodesMulticast()
Get the "all nodes multicast" address.
Access to the IPv6 forwarding table, interfaces, and configuration.
Definition ipv6.h:71
IPv6 address associated with an interface.
IPv6 layer implementation.
This class implements a tag that carries socket ancillary data to the socket interface.
uint32_t GetRecvIf() const
Get the tag's receiving interface.
Ptr< NetDevice > GetDevice(uint32_t index) const
Retrieve the index-th NetDevice associated to this node.
Definition node.cc:138
Ptr< T > GetObject() const
Get a pointer to the requested aggregated Object.
Definition object.h:518
StatusCodeValues
Enum to identify the status code of the operation.
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:580
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:191
static Ptr< Socket > CreateSocket(Ptr< Node > node, TypeId tid)
This method wraps the creation of sockets that is performed on a given node by a SocketFactory specif...
Definition socket.cc:61
StatusCodeValues GetStatusCode() const
Get the status code of the operation.
Hold variables of type string.
Definition string.h:45
Simulation virtual time values and global simulation resolution.
Definition nstime.h:95
@ S
second
Definition nstime.h:106
static Time Max()
Maximum representable Time Not to be confused with Max(Time,Time).
Definition nstime.h:287
AttributeValue implementation for Time.
Definition nstime.h:1375
A Trickle Timer following RFC 6206.
a unique identifier for an interface.
Definition type-id.h:50
static TypeId LookupByName(std::string name)
Get a TypeId by name.
Definition type-id.cc:870
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:999
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition enum.h:223
Ptr< const AttributeChecker > MakeIntegerChecker()
Definition integer.h:99
Ptr< const AttributeAccessor > MakeIntegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition integer.h:35
Ptr< const AttributeAccessor > MakePointerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition pointer.h:250
Ptr< AttributeChecker > MakePointerChecker()
Create a PointerChecker for a type.
Definition pointer.h:273
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition nstime.h:1376
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1396
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:690
Callback< R, Args... > MakeNullCallback()
Build null Callbacks which take no arguments, for varying number of template arguments,...
Definition callback.h:734
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:260
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:267
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:454
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1273
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1290
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
-style-clang-format
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition enum.h:181
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:605