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 includes definitions for IPv6 packet processing. 32 */ 33 34 #ifndef IP6_HPP_ 35 #define IP6_HPP_ 36 37 #include "openthread-core-config.h" 38 39 #include <stddef.h> 40 41 #include <openthread/ip6.h> 42 #include <openthread/nat64.h> 43 #include <openthread/udp.h> 44 45 #include "common/callback.hpp" 46 #include "common/encoding.hpp" 47 #include "common/frame_data.hpp" 48 #include "common/locator.hpp" 49 #include "common/log.hpp" 50 #include "common/message.hpp" 51 #include "common/non_copyable.hpp" 52 #include "common/owned_ptr.hpp" 53 #include "common/time_ticker.hpp" 54 #include "common/timer.hpp" 55 #include "net/icmp6.hpp" 56 #include "net/ip6_address.hpp" 57 #include "net/ip6_headers.hpp" 58 #include "net/ip6_mpl.hpp" 59 #include "net/ip6_types.hpp" 60 #include "net/netif.hpp" 61 #include "net/socket.hpp" 62 #include "net/tcp6.hpp" 63 #include "net/udp6.hpp" 64 65 namespace ot { 66 67 /** 68 * @namespace ot::Ip6 69 * 70 * @brief 71 * This namespace includes definitions for IPv6 networking. 72 * 73 */ 74 namespace Ip6 { 75 76 /** 77 * @addtogroup core-ipv6 78 * 79 * @brief 80 * This module includes definitions for the IPv6 network layer. 81 * 82 * @{ 83 * 84 * @defgroup core-ip6-icmp6 ICMPv6 85 * @defgroup core-ip6-ip6 IPv6 86 * @defgroup core-ip6-mpl MPL 87 * @defgroup core-ip6-netif Network Interfaces 88 * 89 * @} 90 * 91 */ 92 93 /** 94 * @addtogroup core-ip6-ip6 95 * 96 * @brief 97 * This module includes definitions for core IPv6 networking. 98 * 99 * @{ 100 * 101 */ 102 103 /** 104 * Implements the core IPv6 message processing. 105 * 106 */ 107 class Ip6 : public InstanceLocator, private NonCopyable 108 { 109 friend class ot::Instance; 110 friend class ot::TimeTicker; 111 friend class Mpl; 112 113 public: 114 /** 115 * Initializes the object. 116 * 117 * @param[in] aInstance A reference to the otInstance object. 118 * 119 */ 120 explicit Ip6(Instance &aInstance); 121 122 /** 123 * Allocates a new message buffer from the buffer pool with default settings (link security 124 * enabled and `kPriorityMedium`). 125 * 126 * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. 127 * 128 */ 129 Message *NewMessage(void); 130 131 /** 132 * Allocates a new message buffer from the buffer pool with default settings (link security 133 * enabled and `kPriorityMedium`). 134 * 135 * @param[in] aReserved The number of header bytes to reserve following the IPv6 header. 136 * 137 * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. 138 * 139 */ 140 Message *NewMessage(uint16_t aReserved); 141 142 /** 143 * Allocates a new message buffer from the buffer pool. 144 * 145 * @param[in] aReserved The number of header bytes to reserve following the IPv6 header. 146 * @param[in] aSettings The message settings. 147 * 148 * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. 149 * 150 */ 151 Message *NewMessage(uint16_t aReserved, const Message::Settings &aSettings); 152 153 /** 154 * Allocates a new message buffer from the buffer pool and writes the IPv6 datagram to the message. 155 * 156 * The message priority is always determined from IPv6 message itself (@p aData) and the priority included in 157 * @p aSetting is ignored. 158 * 159 * @param[in] aData A pointer to the IPv6 datagram buffer. 160 * @param[in] aDataLength The size of the IPV6 datagram buffer pointed by @p aData. 161 * @param[in] aSettings The message settings. 162 * 163 * @returns A pointer to the message or `nullptr` if malformed IPv6 header or insufficient message buffers are 164 * available. 165 * 166 */ 167 Message *NewMessageFromData(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings); 168 169 /** 170 * Converts the IPv6 DSCP value to message priority level. 171 * 172 * @param[in] aDscp The IPv6 DSCP value. 173 * 174 * @returns The message priority level. 175 * 176 */ 177 static Message::Priority DscpToPriority(uint8_t aDscp); 178 179 /** 180 * Sends an IPv6 datagram. 181 * 182 * @param[in] aMessage A reference to the message. 183 * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. 184 * @param[in] aIpProto The Internet Protocol value. 185 * 186 * @retval kErrorNone Successfully enqueued the message into an output interface. 187 * @retval kErrorNoBufs Insufficient available buffer to add the IPv6 headers. 188 * 189 */ 190 Error SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto); 191 192 /** 193 * Sends a raw IPv6 datagram with a fully formed IPv6 header. 194 * 195 * @param[in] aMessage An owned pointer to a message (ownership is transferred to the method). 196 * 197 * @retval kErrorNone Successfully processed the message. 198 * @retval kErrorDrop Message was well-formed but not fully processed due to packet processing rules. 199 * @retval kErrorNoBufs Could not allocate necessary message buffers when processing the datagram. 200 * @retval kErrorNoRoute No route to host. 201 * @retval kErrorParse Encountered a malformed header when processing the message. 202 * 203 */ 204 Error SendRaw(OwnedPtr<Message> aMessage); 205 206 /** 207 * Processes a received IPv6 datagram. 208 * 209 * @param[in] aMessage An owned pointer to a message. 210 * 211 * @retval kErrorNone Successfully processed the message. 212 * @retval kErrorDrop Message was well-formed but not fully processed due to packet processing rules. 213 * @retval kErrorNoBufs Could not allocate necessary message buffers when processing the datagram. 214 * @retval kErrorNoRoute No route to host. 215 * @retval kErrorParse Encountered a malformed header when processing the message. 216 * 217 */ 218 Error HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled = false); 219 220 /** 221 * Registers a callback to provide received raw IPv6 datagrams. 222 * 223 * By default, this callback does not pass Thread control traffic. See SetReceiveIp6FilterEnabled() to change 224 * the Thread control traffic filter setting. 225 * 226 * @param[in] aCallback A pointer to a function that is called when an IPv6 datagram is received 227 * or `nullptr` to disable the callback. 228 * @param[in] aCallbackContext A pointer to application-specific context. 229 * 230 * @sa IsReceiveIp6FilterEnabled 231 * @sa SetReceiveIp6FilterEnabled 232 * 233 */ SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback,void * aCallbackContext)234 void SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCallbackContext) 235 { 236 mReceiveIp6DatagramCallback.Set(aCallback, aCallbackContext); 237 } 238 239 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE 240 /** 241 * Registers a callback to provide received translated IPv4 datagrams. 242 * 243 * @param[in] aCallback A pointer to a function that is called when a translated IPv4 datagram is received 244 * or `nullptr` to disable the callback. 245 * @param[in] aCallbackContext A pointer to application-specific context. 246 * 247 * @sa SetReceiveDatagramCallback 248 * 249 */ SetNat64ReceiveIp4DatagramCallback(otNat64ReceiveIp4Callback aCallback,void * aCallbackContext)250 void SetNat64ReceiveIp4DatagramCallback(otNat64ReceiveIp4Callback aCallback, void *aCallbackContext) 251 { 252 mReceiveIp4DatagramCallback.Set(aCallback, aCallbackContext); 253 } 254 #endif 255 256 /** 257 * Indicates whether or not Thread control traffic is filtered out when delivering IPv6 datagrams 258 * via the callback specified in SetReceiveIp6DatagramCallback(). 259 * 260 * @returns TRUE if Thread control traffic is filtered out, FALSE otherwise. 261 * 262 * @sa SetReceiveDatagramCallback 263 * @sa SetReceiveIp6FilterEnabled 264 * 265 */ IsReceiveIp6FilterEnabled(void) const266 bool IsReceiveIp6FilterEnabled(void) const { return mIsReceiveIp6FilterEnabled; } 267 268 /** 269 * Sets whether or not Thread control traffic is filtered out when delivering IPv6 datagrams 270 * via the callback specified in SetReceiveIp6DatagramCallback(). 271 * 272 * @param[in] aEnabled TRUE if Thread control traffic is filtered out, FALSE otherwise. 273 * 274 * @sa SetReceiveDatagramCallback 275 * @sa IsReceiveIp6FilterEnabled 276 * 277 */ SetReceiveIp6FilterEnabled(bool aEnabled)278 void SetReceiveIp6FilterEnabled(bool aEnabled) { mIsReceiveIp6FilterEnabled = aEnabled; } 279 280 /** 281 * Performs default source address selection. 282 * 283 * @param[in,out] aMessageInfo A reference to the message information. 284 * 285 * @retval kErrorNone Found a source address and updated SockAddr of @p aMessageInfo. 286 * @retval kErrorNotFound No source address was found and @p aMessageInfo is unchanged. 287 * 288 */ 289 Error SelectSourceAddress(MessageInfo &aMessageInfo) const; 290 291 /** 292 * Performs default source address selection. 293 * 294 * @param[in] aDestination The destination address. 295 * 296 * @returns A pointer to the selected IPv6 source address or `nullptr` if no source address was found. 297 * 298 */ 299 const Address *SelectSourceAddress(const Address &aDestination) const; 300 301 /** 302 * Returns a reference to the send queue. 303 * 304 * @returns A reference to the send queue. 305 * 306 */ GetSendQueue(void) const307 const PriorityQueue &GetSendQueue(void) const { return mSendQueue; } 308 309 /** 310 * Converts an IP protocol number to a string. 311 * 312 * @param[in] aIpProto An IP protocol number. 313 * 314 * @returns The string representation of @p aIpProto. 315 * 316 */ 317 static const char *IpProtoToString(uint8_t aIpProto); 318 319 /** 320 * Converts an IP header ECN value to a string. 321 * 322 * @param[in] aEcn The 2-bit ECN value. 323 * 324 * @returns The string representation of @p aEcn. 325 * 326 */ 327 static const char *EcnToString(Ecn aEcn); 328 329 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 330 /** 331 * Returns a reference to the Border Routing counters. 332 * 333 * @returns A reference to the Border Routing counters. 334 * 335 */ GetBorderRoutingCounters(void) const336 const otBorderRoutingCounters &GetBorderRoutingCounters(void) const { return mBorderRoutingCounters; } 337 338 /** 339 * Returns a reference to the Border Routing counters. 340 * 341 * @returns A reference to the Border Routing counters. 342 * 343 */ GetBorderRoutingCounters(void)344 otBorderRoutingCounters &GetBorderRoutingCounters(void) { return mBorderRoutingCounters; } 345 346 /** 347 * Resets the Border Routing counters. 348 * 349 */ ResetBorderRoutingCounters(void)350 void ResetBorderRoutingCounters(void) { ClearAllBytes(mBorderRoutingCounters); } 351 #endif 352 353 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 354 355 /** 356 * Enables or disables the filter that drops TMF UDP messages from untrusted origin. 357 * 358 * @param[in] aEnabled TRUE to enable filter, FALSE otherwise. 359 * 360 */ SetTmfOriginFilterEnabled(bool aEnabled)361 void SetTmfOriginFilterEnabled(bool aEnabled) { mTmfOriginFilterEnabled = aEnabled; } 362 363 /** 364 * Indicates whether the filter that drops TMF UDP messages from untrusted origin is enabled or not. 365 * 366 * @returns TRUE if the filter is enabled, FALSE otherwise. 367 * 368 */ IsTmfOriginFilterEnabled(void)369 bool IsTmfOriginFilterEnabled(void) { return mTmfOriginFilterEnabled; } 370 371 #endif 372 373 private: 374 static constexpr uint8_t kDefaultHopLimit = OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT; 375 static constexpr uint8_t kIp6ReassemblyTimeout = OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT; 376 377 static constexpr uint16_t kMinimalMtu = 1280; 378 379 static uint8_t PriorityToDscp(Message::Priority aPriority); 380 static Error TakeOrCopyMessagePtr(OwnedPtr<Message> &aTargetPtr, 381 OwnedPtr<Message> &aMessagePtr, 382 Message::Ownership aMessageOwnership); 383 384 void EnqueueDatagram(Message &aMessage); 385 void HandleSendQueue(void); 386 Error PassToHost(OwnedPtr<Message> &aMessagePtr, 387 const MessageInfo &aMessageInfo, 388 uint8_t aIpProto, 389 bool aApplyFilter, 390 bool aReceive, 391 Message::Ownership aMessageOwnership); 392 Error HandleExtensionHeaders(OwnedPtr<Message> &aMessagePtr, 393 MessageInfo &aMessageInfo, 394 Header &aHeader, 395 uint8_t &aNextHeader, 396 bool &aReceive); 397 Error FragmentDatagram(Message &aMessage, uint8_t aIpProto); 398 Error HandleFragment(Message &aMessage); 399 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE 400 void CleanupFragmentationBuffer(void); 401 void HandleTimeTick(void); 402 void UpdateReassemblyList(void); 403 void SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode); 404 #endif 405 Error AddMplOption(Message &aMessage, Header &aHeader); 406 Error PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Header &aHeader); 407 Error InsertMplOption(Message &aMessage, Header &aHeader); 408 Error RemoveMplOption(Message &aMessage); 409 Error HandleOptions(Message &aMessage, Header &aHeader, bool &aReceive); 410 Error HandlePayload(Header &aIp6Header, 411 OwnedPtr<Message> &aMessagePtr, 412 MessageInfo &aMessageInfo, 413 uint8_t aIpProto, 414 Message::Ownership aMessageOwnership); 415 bool IsOnLink(const Address &aAddress) const; 416 Error RouteLookup(const Address &aSource, const Address &aDestination) const; 417 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 418 void UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound); 419 #endif 420 421 using SendQueueTask = TaskletIn<Ip6, &Ip6::HandleSendQueue>; 422 423 bool mIsReceiveIp6FilterEnabled; 424 425 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 426 bool mTmfOriginFilterEnabled : 1; 427 #endif 428 429 Callback<otIp6ReceiveCallback> mReceiveIp6DatagramCallback; 430 431 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE 432 Callback<otNat64ReceiveIp4Callback> mReceiveIp4DatagramCallback; 433 #endif 434 435 PriorityQueue mSendQueue; 436 SendQueueTask mSendQueueTask; 437 438 Icmp mIcmp; 439 Udp mUdp; 440 Mpl mMpl; 441 442 #if OPENTHREAD_CONFIG_TCP_ENABLE 443 Tcp mTcp; 444 #endif 445 446 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE 447 MessageQueue mReassemblyList; 448 #endif 449 450 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 451 otBorderRoutingCounters mBorderRoutingCounters; 452 #endif 453 }; 454 455 /** 456 * Represents parsed IPv6 header along with UDP/TCP/ICMP6 headers from a received message/frame. 457 * 458 */ 459 class Headers : private Clearable<Headers> 460 { 461 friend class Clearable<Headers>; 462 463 public: 464 /** 465 * Parses the IPv6 and UDP/TCP/ICMP6 headers from a given message. 466 * 467 * @param[in] aMessage The message to parse the headers from. 468 * 469 * @retval kErrorNone The headers are parsed successfully. 470 * @retval kErrorParse Failed to parse the headers. 471 * 472 */ 473 Error ParseFrom(const Message &aMessage); 474 475 /** 476 * Decompresses lowpan frame and parses the IPv6 and UDP/TCP/ICMP6 headers. 477 * 478 * @param[in] aMessage The message from which to read the lowpan frame. 479 * @param[in] aOffset The offset in @p aMessage to start reading the frame. 480 * @param[in] aMacAddrs The MAC source and destination addresses. 481 * 482 * @retval kErrorNone Successfully decompressed and parsed IPv6 and UDP/TCP/ICMP6 headers. 483 * @retval kErrorNotFound Lowpan frame is a next fragment and does not contain IPv6 headers. 484 * @retval kErrorParse Failed to parse the headers. 485 * 486 */ 487 Error DecompressFrom(const Message &aMessage, uint16_t aOffset, const Mac::Addresses &aMacAddrs); 488 489 /** 490 * Decompresses lowpan frame and parses the IPv6 and UDP/TCP/ICMP6 headers. 491 * 492 * @param[in] aFrameData The lowpan frame data. 493 * @param[in] aMacAddrs The MAC source and destination addresses. 494 * @param[in] aInstance The OpenThread instance. 495 * 496 * @retval kErrorNone Successfully decompressed and parsed IPv6 and UDP/TCP/ICMP6 headers. 497 * @retval kErrorNotFound Lowpan frame is a next fragment and does not contain IPv6 headers. 498 * @retval kErrorParse Failed to parse the headers. 499 * 500 */ 501 Error DecompressFrom(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Instance &aInstance); 502 503 /** 504 * Returns the IPv6 header. 505 * 506 * @returns The IPv6 header. 507 * 508 */ GetIp6Header(void) const509 const Header &GetIp6Header(void) const { return mIp6Header; } 510 511 /** 512 * Returns the IP protocol number from IPv6 Next Header field. 513 * 514 * @returns The IP protocol number. 515 * 516 */ GetIpProto(void) const517 uint8_t GetIpProto(void) const { return mIp6Header.GetNextHeader(); } 518 519 /** 520 * Returns the 2-bit Explicit Congestion Notification (ECN) from Traffic Class field from IPv6 header. 521 * 522 * @returns The ECN value. 523 * 524 */ GetEcn(void) const525 Ecn GetEcn(void) const { return mIp6Header.GetEcn(); } 526 527 /** 528 * Indicates if the protocol number from IPv6 header is UDP. 529 * 530 * @retval TRUE If the protocol number in IPv6 header is UDP. 531 * @retval FALSE If the protocol number in IPv6 header is not UDP. 532 * 533 */ IsUdp(void) const534 bool IsUdp(void) const { return GetIpProto() == kProtoUdp; } 535 536 /** 537 * Indicates if the protocol number from IPv6 header is TCP. 538 * 539 * @retval TRUE If the protocol number in IPv6 header is TCP. 540 * @retval FALSE If the protocol number in IPv6 header is not TCP. 541 * 542 */ IsTcp(void) const543 bool IsTcp(void) const { return GetIpProto() == kProtoTcp; } 544 545 /** 546 * Indicates if the protocol number from IPv6 header is ICMPv6. 547 * 548 * @retval TRUE If the protocol number in IPv6 header is ICMPv6. 549 * @retval FALSE If the protocol number in IPv6 header is not ICMPv6. 550 * 551 */ IsIcmp6(void) const552 bool IsIcmp6(void) const { return GetIpProto() == kProtoIcmp6; } 553 554 /** 555 * Returns the source IPv6 address from IPv6 header. 556 * 557 * @returns The source IPv6 address. 558 * 559 */ GetSourceAddress(void) const560 const Address &GetSourceAddress(void) const { return mIp6Header.GetSource(); } 561 562 /** 563 * Returns the destination IPv6 address from IPv6 header. 564 * 565 * @returns The destination IPv6 address. 566 * 567 */ GetDestinationAddress(void) const568 const Address &GetDestinationAddress(void) const { return mIp6Header.GetDestination(); } 569 570 /** 571 * Returns the UDP header. 572 * 573 * MUST be used when `IsUdp() == true`. Otherwise its behavior is undefined 574 * 575 * @returns The UDP header. 576 * 577 */ GetUdpHeader(void) const578 const Udp::Header &GetUdpHeader(void) const { return mHeader.mUdp; } 579 580 /** 581 * Returns the TCP header. 582 * 583 * MUST be used when `IsTcp() == true`. Otherwise its behavior is undefined 584 * 585 * @returns The TCP header. 586 * 587 */ GetTcpHeader(void) const588 const Tcp::Header &GetTcpHeader(void) const { return mHeader.mTcp; } 589 590 /** 591 * Returns the ICMPv6 header. 592 * 593 * MUST be used when `IsIcmp6() == true`. Otherwise its behavior is undefined 594 * 595 * @returns The ICMPv6 header. 596 * 597 */ GetIcmpHeader(void) const598 const Icmp::Header &GetIcmpHeader(void) const { return mHeader.mIcmp; } 599 600 /** 601 * Returns the source port number if header is UDP or TCP, or zero otherwise 602 * 603 * @returns The source port number under UDP / TCP or zero. 604 * 605 */ 606 uint16_t GetSourcePort(void) const; 607 608 /** 609 * Returns the destination port number if header is UDP or TCP, or zero otherwise. 610 * 611 * @returns The destination port number under UDP / TCP or zero. 612 * 613 */ 614 uint16_t GetDestinationPort(void) const; 615 616 /** 617 * Returns the checksum values from corresponding UDP, TCP, or ICMPv6 header. 618 * 619 * @returns The checksum value. 620 * 621 */ 622 uint16_t GetChecksum(void) const; 623 624 private: 625 Header mIp6Header; 626 union 627 { 628 Udp::Header mUdp; 629 Tcp::Header mTcp; 630 Icmp::Header mIcmp; 631 } mHeader; 632 }; 633 634 /** 635 * @} 636 * 637 */ 638 639 } // namespace Ip6 640 } // namespace ot 641 642 #endif // IP6_HPP_ 643