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/time_ticker.hpp" 53 #include "common/timer.hpp" 54 #include "net/icmp6.hpp" 55 #include "net/ip6_address.hpp" 56 #include "net/ip6_headers.hpp" 57 #include "net/ip6_mpl.hpp" 58 #include "net/ip6_types.hpp" 59 #include "net/netif.hpp" 60 #include "net/socket.hpp" 61 #include "net/tcp6.hpp" 62 #include "net/udp6.hpp" 63 64 namespace ot { 65 66 /** 67 * @namespace ot::Ip6 68 * 69 * @brief 70 * This namespace includes definitions for IPv6 networking. 71 * 72 */ 73 namespace Ip6 { 74 75 using ot::Encoding::BigEndian::HostSwap16; 76 using ot::Encoding::BigEndian::HostSwap32; 77 78 /** 79 * @addtogroup core-ipv6 80 * 81 * @brief 82 * This module includes definitions for the IPv6 network layer. 83 * 84 * @{ 85 * 86 * @defgroup core-ip6-icmp6 ICMPv6 87 * @defgroup core-ip6-ip6 IPv6 88 * @defgroup core-ip6-mpl MPL 89 * @defgroup core-ip6-netif Network Interfaces 90 * 91 * @} 92 * 93 */ 94 95 /** 96 * @addtogroup core-ip6-ip6 97 * 98 * @brief 99 * This module includes definitions for core IPv6 networking. 100 * 101 * @{ 102 * 103 */ 104 105 /** 106 * This class implements the core IPv6 message processing. 107 * 108 */ 109 class Ip6 : public InstanceLocator, private NonCopyable 110 { 111 friend class ot::Instance; 112 friend class ot::TimeTicker; 113 friend class Mpl; 114 115 public: 116 /** 117 * This enumeration represents an IPv6 message origin. 118 * 119 * In case the message is originating from host, it also indicates whether or not it is allowed to passed back the 120 * message to the host. 121 * 122 */ 123 enum MessageOrigin : uint8_t 124 { 125 kFromThreadNetif, ///< Message originates from Thread Netif. 126 kFromHostDisallowLoopBack, ///< Message originates from host and should not be passed back to host. 127 kFromHostAllowLoopBack, ///< Message originates from host and can be passed back to host. 128 }; 129 130 /** 131 * This constructor initializes the object. 132 * 133 * @param[in] aInstance A reference to the otInstance object. 134 * 135 */ 136 explicit Ip6(Instance &aInstance); 137 138 /** 139 * This method allocates a new message buffer from the buffer pool with default settings (link security 140 * enabled and `kPriorityMedium`). 141 * 142 * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. 143 * 144 */ 145 Message *NewMessage(void); 146 147 /** 148 * This method allocates a new message buffer from the buffer pool with default settings (link security 149 * enabled and `kPriorityMedium`). 150 * 151 * @param[in] aReserved The number of header bytes to reserve following the IPv6 header. 152 * 153 * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. 154 * 155 */ 156 Message *NewMessage(uint16_t aReserved); 157 158 /** 159 * This method allocates a new message buffer from the buffer pool. 160 * 161 * @param[in] aReserved The number of header bytes to reserve following the IPv6 header. 162 * @param[in] aSettings The message settings. 163 * 164 * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. 165 * 166 */ 167 Message *NewMessage(uint16_t aReserved, const Message::Settings &aSettings); 168 169 /** 170 * This method allocates a new message buffer from the buffer pool and writes the IPv6 datagram to the message. 171 * 172 * The message priority is always determined from IPv6 message itself (@p aData) and the priority included in 173 * @p aSetting is ignored. 174 * 175 * @param[in] aData A pointer to the IPv6 datagram buffer. 176 * @param[in] aDataLength The size of the IPV6 datagram buffer pointed by @p aData. 177 * @param[in] aSettings The message settings. 178 * 179 * @returns A pointer to the message or `nullptr` if malformed IPv6 header or insufficient message buffers are 180 * available. 181 * 182 */ 183 Message *NewMessageFromData(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings); 184 185 /** 186 * This method converts the IPv6 DSCP value to message priority level. 187 * 188 * @param[in] aDscp The IPv6 DSCP value. 189 * 190 * @returns The message priority level. 191 * 192 */ 193 static Message::Priority DscpToPriority(uint8_t aDscp); 194 195 /** 196 * This method sends an IPv6 datagram. 197 * 198 * @param[in] aMessage A reference to the message. 199 * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. 200 * @param[in] aIpProto The Internet Protocol value. 201 * 202 * @retval kErrorNone Successfully enqueued the message into an output interface. 203 * @retval kErrorNoBufs Insufficient available buffer to add the IPv6 headers. 204 * 205 */ 206 Error SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto); 207 208 /** 209 * This method sends a raw IPv6 datagram with a fully formed IPv6 header. 210 * 211 * The caller transfers ownership of @p aMessage when making this call. OpenThread will free @p aMessage when 212 * processing is complete, including when a value other than `kErrorNone` is returned. 213 * 214 * @param[in] aMessage A reference to the message. 215 * @param[in] aAllowLoopBackToHost Indicate whether or not the message is allowed to be passed back to host. 216 * 217 * @retval kErrorNone Successfully processed the message. 218 * @retval kErrorDrop Message was well-formed but not fully processed due to packet processing rules. 219 * @retval kErrorNoBufs Could not allocate necessary message buffers when processing the datagram. 220 * @retval kErrorNoRoute No route to host. 221 * @retval kErrorParse Encountered a malformed header when processing the message. 222 * 223 */ 224 Error SendRaw(Message &aMessage, bool aAllowLoopBackToHost); 225 226 /** 227 * This method processes a received IPv6 datagram. 228 * 229 * @param[in] aMessage A reference to the message. 230 * @param[in] aOrigin The message oirgin. 231 * @param[in] aLinkMessageInfo A pointer to link-specific message information. 232 * 233 * @retval kErrorNone Successfully processed the message. 234 * @retval kErrorDrop Message was well-formed but not fully processed due to packet processing rules. 235 * @retval kErrorNoBufs Could not allocate necessary message buffers when processing the datagram. 236 * @retval kErrorNoRoute No route to host. 237 * @retval kErrorParse Encountered a malformed header when processing the message. 238 * 239 */ 240 Error HandleDatagram(Message &aMessage, 241 MessageOrigin aOrigin, 242 const void *aLinkMessageInfo = nullptr, 243 bool aIsReassembled = false); 244 245 /** 246 * This method registers a callback to provide received raw IPv6 datagrams. 247 * 248 * By default, this callback does not pass Thread control traffic. See SetReceiveIp6FilterEnabled() to change 249 * the Thread control traffic filter setting. 250 * 251 * @param[in] aCallback A pointer to a function that is called when an IPv6 datagram is received 252 * or `nullptr` to disable the callback. 253 * @param[in] aCallbackContext A pointer to application-specific context. 254 * 255 * @sa IsReceiveIp6FilterEnabled 256 * @sa SetReceiveIp6FilterEnabled 257 * 258 */ SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback,void * aCallbackContext)259 void SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCallbackContext) 260 { 261 mReceiveIp6DatagramCallback.Set(aCallback, aCallbackContext); 262 } 263 264 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE 265 /** 266 * This method registers a callback to provide received translated IPv4 datagrams. 267 * 268 * @param[in] aCallback A pointer to a function that is called when a translated IPv4 datagram is received 269 * or `nullptr` to disable the callback. 270 * @param[in] aCallbackContext A pointer to application-specific context. 271 * 272 * @sa SetReceiveDatagramCallback 273 * 274 */ SetNat64ReceiveIp4DatagramCallback(otNat64ReceiveIp4Callback aCallback,void * aCallbackContext)275 void SetNat64ReceiveIp4DatagramCallback(otNat64ReceiveIp4Callback aCallback, void *aCallbackContext) 276 { 277 mReceiveIp4DatagramCallback.Set(aCallback, aCallbackContext); 278 } 279 #endif 280 281 /** 282 * This method indicates whether or not Thread control traffic is filtered out when delivering IPv6 datagrams 283 * via the callback specified in SetReceiveIp6DatagramCallback(). 284 * 285 * @returns TRUE if Thread control traffic is filtered out, FALSE otherwise. 286 * 287 * @sa SetReceiveDatagramCallback 288 * @sa SetReceiveIp6FilterEnabled 289 * 290 */ IsReceiveIp6FilterEnabled(void) const291 bool IsReceiveIp6FilterEnabled(void) const { return mIsReceiveIp6FilterEnabled; } 292 293 /** 294 * This method sets whether or not Thread control traffic is filtered out when delivering IPv6 datagrams 295 * via the callback specified in SetReceiveIp6DatagramCallback(). 296 * 297 * @param[in] aEnabled TRUE if Thread control traffic is filtered out, FALSE otherwise. 298 * 299 * @sa SetReceiveDatagramCallback 300 * @sa IsReceiveIp6FilterEnabled 301 * 302 */ SetReceiveIp6FilterEnabled(bool aEnabled)303 void SetReceiveIp6FilterEnabled(bool aEnabled) { mIsReceiveIp6FilterEnabled = aEnabled; } 304 305 /** 306 * This method performs default source address selection. 307 * 308 * @param[in,out] aMessageInfo A reference to the message information. 309 * 310 * @retval kErrorNone Found a source address and updated SockAddr of @p aMessageInfo. 311 * @retval kErrorNotFound No source address was found and @p aMessageInfo is unchanged. 312 * 313 */ 314 Error SelectSourceAddress(MessageInfo &aMessageInfo) const; 315 316 /** 317 * This method performs default source address selection. 318 * 319 * @param[in] aDestination The destination address. 320 * 321 * @returns A pointer to the selected IPv6 source address or `nullptr` if no source address was found. 322 * 323 */ 324 const Address *SelectSourceAddress(const Address &aDestination) const; 325 326 /** 327 * This method returns a reference to the send queue. 328 * 329 * @returns A reference to the send queue. 330 * 331 */ GetSendQueue(void) const332 const PriorityQueue &GetSendQueue(void) const { return mSendQueue; } 333 334 /** 335 * This static method converts an IP protocol number to a string. 336 * 337 * @param[in] aIpProto An IP protocol number. 338 * 339 * @returns The string representation of @p aIpProto. 340 * 341 */ 342 static const char *IpProtoToString(uint8_t aIpProto); 343 344 /** 345 * This static method converts an IP header ECN value to a string. 346 * 347 * @param[in] aEcn The 2-bit ECN value. 348 * 349 * @returns The string representation of @p aEcn. 350 * 351 */ 352 static const char *EcnToString(Ecn aEcn); 353 354 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 355 /** 356 * This method returns a reference to the Border Routing counters. 357 * 358 * @returns A reference to the Border Routing counters. 359 * 360 */ GetBorderRoutingCounters(void) const361 const otBorderRoutingCounters &GetBorderRoutingCounters(void) const { return mBorderRoutingCounters; } 362 363 /** 364 * This method returns a reference to the Border Routing counters. 365 * 366 * @returns A reference to the Border Routing counters. 367 * 368 */ GetBorderRoutingCounters(void)369 otBorderRoutingCounters &GetBorderRoutingCounters(void) { return mBorderRoutingCounters; } 370 371 /** 372 * This method resets the Border Routing counters. 373 * 374 */ ResetBorderRoutingCounters(void)375 void ResetBorderRoutingCounters(void) { memset(&mBorderRoutingCounters, 0, sizeof(mBorderRoutingCounters)); } 376 #endif 377 378 private: 379 static constexpr uint8_t kDefaultHopLimit = OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT; 380 static constexpr uint8_t kIp6ReassemblyTimeout = OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT; 381 382 static constexpr uint16_t kMinimalMtu = 1280; 383 384 void HandleSendQueue(void); 385 386 static uint8_t PriorityToDscp(Message::Priority aPriority); 387 388 void EnqueueDatagram(Message &aMessage); 389 Error PassToHost(Message &aMessage, 390 MessageOrigin aOrigin, 391 const MessageInfo &aMessageInfo, 392 uint8_t aIpProto, 393 bool aApplyFilter, 394 Message::Ownership aMessageOwnership); 395 Error HandleExtensionHeaders(Message &aMessage, 396 MessageOrigin aOrigin, 397 MessageInfo &aMessageInfo, 398 Header &aHeader, 399 uint8_t &aNextHeader, 400 bool &aReceive); 401 Error FragmentDatagram(Message &aMessage, uint8_t aIpProto); 402 Error HandleFragment(Message &aMessage, MessageOrigin aOrigin, MessageInfo &aMessageInfo); 403 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE 404 void CleanupFragmentationBuffer(void); 405 void HandleTimeTick(void); 406 void UpdateReassemblyList(void); 407 void SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode); 408 #endif 409 Error AddMplOption(Message &aMessage, Header &aHeader); 410 Error AddTunneledMplOption(Message &aMessage, Header &aHeader); 411 Error InsertMplOption(Message &aMessage, Header &aHeader); 412 Error RemoveMplOption(Message &aMessage); 413 Error HandleOptions(Message &aMessage, Header &aHeader, bool aIsOutbound, bool &aReceive); 414 Error HandlePayload(Header &aIp6Header, 415 Message &aMessage, 416 MessageInfo &aMessageInfo, 417 uint8_t aIpProto, 418 Message::Ownership aMessageOwnership); 419 bool IsOnLink(const Address &aAddress) const; 420 Error RouteLookup(const Address &aSource, const Address &aDestination) const; 421 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 422 void UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound); 423 #endif 424 425 using SendQueueTask = TaskletIn<Ip6, &Ip6::HandleSendQueue>; 426 427 bool mIsReceiveIp6FilterEnabled; 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 * This class 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method 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 * This method returns the UDP header. 572 * 573 * This method 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 * This method returns the TCP header. 582 * 583 * This method 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 * This method returns the ICMPv6 header. 592 * 593 * This method 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 * This method 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 * This method 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 * This method 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