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 typedef otBorderRoutingCounters BrCounters; ///< Border Routing counters. 332 333 /** 334 * Returns a reference to the Border Routing counters. 335 * 336 * @returns A reference to the Border Routing counters. 337 * 338 */ GetBorderRoutingCounters(void) const339 const BrCounters &GetBorderRoutingCounters(void) const { return mBrCounters; } 340 341 /** 342 * Returns a reference to the Border Routing counters. 343 * 344 * @returns A reference to the Border Routing counters. 345 * 346 */ GetBorderRoutingCounters(void)347 BrCounters &GetBorderRoutingCounters(void) { return mBrCounters; } 348 349 /** 350 * Resets the Border Routing counters. 351 * 352 */ ResetBorderRoutingCounters(void)353 void ResetBorderRoutingCounters(void) { ClearAllBytes(mBrCounters); } 354 355 #endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 356 357 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 358 359 /** 360 * Enables or disables the filter that drops TMF UDP messages from untrusted origin. 361 * 362 * @param[in] aEnabled TRUE to enable filter, FALSE otherwise. 363 * 364 */ SetTmfOriginFilterEnabled(bool aEnabled)365 void SetTmfOriginFilterEnabled(bool aEnabled) { mTmfOriginFilterEnabled = aEnabled; } 366 367 /** 368 * Indicates whether the filter that drops TMF UDP messages from untrusted origin is enabled or not. 369 * 370 * @returns TRUE if the filter is enabled, FALSE otherwise. 371 * 372 */ IsTmfOriginFilterEnabled(void)373 bool IsTmfOriginFilterEnabled(void) { return mTmfOriginFilterEnabled; } 374 375 #endif 376 377 private: 378 static constexpr uint8_t kDefaultHopLimit = OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT; 379 static constexpr uint8_t kIp6ReassemblyTimeout = OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT; 380 381 static constexpr uint16_t kMinimalMtu = 1280; 382 383 static uint8_t PriorityToDscp(Message::Priority aPriority); 384 static Error TakeOrCopyMessagePtr(OwnedPtr<Message> &aTargetPtr, 385 OwnedPtr<Message> &aMessagePtr, 386 Message::Ownership aMessageOwnership); 387 388 void EnqueueDatagram(Message &aMessage); 389 void HandleSendQueue(void); 390 Error PassToHost(OwnedPtr<Message> &aMessagePtr, 391 const Header &aHeader, 392 uint8_t aIpProto, 393 bool aReceive, 394 Message::Ownership aMessageOwnership); 395 Error HandleExtensionHeaders(OwnedPtr<Message> &aMessagePtr, 396 const Header &aHeader, 397 uint8_t &aNextHeader, 398 bool &aReceive); 399 Error FragmentDatagram(Message &aMessage, uint8_t aIpProto); 400 Error HandleFragment(Message &aMessage); 401 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE 402 void CleanupFragmentationBuffer(void); 403 void HandleTimeTick(void); 404 void UpdateReassemblyList(void); 405 void SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode); 406 #endif 407 Error ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange, HopByHopHeader &aHbhHeader) const; 408 Error AddMplOption(Message &aMessage, Header &aHeader); 409 Error PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Header &aHeader); 410 Error InsertMplOption(Message &aMessage, Header &aHeader); 411 Error RemoveMplOption(Message &aMessage); 412 Error HandleOptions(Message &aMessage, const Header &aHeader, bool &aReceive); 413 Error Receive(Header &aIp6Header, 414 OwnedPtr<Message> &aMessagePtr, 415 uint8_t aIpProto, 416 Message::Ownership aMessageOwnership); 417 bool IsOnLink(const Address &aAddress) const; 418 Error RouteLookup(const Address &aSource, const Address &aDestination) const; 419 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 420 void UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound); 421 #endif 422 423 using SendQueueTask = TaskletIn<Ip6, &Ip6::HandleSendQueue>; 424 425 bool mIsReceiveIp6FilterEnabled; 426 427 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 428 bool mTmfOriginFilterEnabled : 1; 429 #endif 430 431 Callback<otIp6ReceiveCallback> mReceiveIp6DatagramCallback; 432 433 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE 434 Callback<otNat64ReceiveIp4Callback> mReceiveIp4DatagramCallback; 435 #endif 436 437 PriorityQueue mSendQueue; 438 SendQueueTask mSendQueueTask; 439 440 Icmp mIcmp; 441 Udp mUdp; 442 Mpl mMpl; 443 444 #if OPENTHREAD_CONFIG_TCP_ENABLE 445 Tcp mTcp; 446 #endif 447 448 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE 449 MessageQueue mReassemblyList; 450 #endif 451 452 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 453 BrCounters mBrCounters; 454 #endif 455 }; 456 457 /** 458 * Represents parsed IPv6 header along with UDP/TCP/ICMP6 headers from a received message/frame. 459 * 460 */ 461 class Headers : private Clearable<Headers> 462 { 463 friend class Clearable<Headers>; 464 465 public: 466 /** 467 * Parses the IPv6 and UDP/TCP/ICMP6 headers from a given message. 468 * 469 * @param[in] aMessage The message to parse the headers from. 470 * 471 * @retval kErrorNone The headers are parsed successfully. 472 * @retval kErrorParse Failed to parse the headers. 473 * 474 */ 475 Error ParseFrom(const Message &aMessage); 476 477 /** 478 * Decompresses lowpan frame and parses the IPv6 and UDP/TCP/ICMP6 headers. 479 * 480 * @param[in] aMessage The message from which to read the lowpan frame. 481 * @param[in] aOffset The offset in @p aMessage to start reading the frame. 482 * @param[in] aMacAddrs The MAC source and destination addresses. 483 * 484 * @retval kErrorNone Successfully decompressed and parsed IPv6 and UDP/TCP/ICMP6 headers. 485 * @retval kErrorNotFound Lowpan frame is a next fragment and does not contain IPv6 headers. 486 * @retval kErrorParse Failed to parse the headers. 487 * 488 */ 489 Error DecompressFrom(const Message &aMessage, uint16_t aOffset, const Mac::Addresses &aMacAddrs); 490 491 /** 492 * Decompresses lowpan frame and parses the IPv6 and UDP/TCP/ICMP6 headers. 493 * 494 * @param[in] aFrameData The lowpan frame data. 495 * @param[in] aMacAddrs The MAC source and destination addresses. 496 * @param[in] aInstance The OpenThread instance. 497 * 498 * @retval kErrorNone Successfully decompressed and parsed IPv6 and UDP/TCP/ICMP6 headers. 499 * @retval kErrorNotFound Lowpan frame is a next fragment and does not contain IPv6 headers. 500 * @retval kErrorParse Failed to parse the headers. 501 * 502 */ 503 Error DecompressFrom(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Instance &aInstance); 504 505 /** 506 * Returns the IPv6 header. 507 * 508 * @returns The IPv6 header. 509 * 510 */ GetIp6Header(void) const511 const Header &GetIp6Header(void) const { return mIp6Header; } 512 513 /** 514 * Returns the IP protocol number from IPv6 Next Header field. 515 * 516 * @returns The IP protocol number. 517 * 518 */ GetIpProto(void) const519 uint8_t GetIpProto(void) const { return mIp6Header.GetNextHeader(); } 520 521 /** 522 * Returns the 2-bit Explicit Congestion Notification (ECN) from Traffic Class field from IPv6 header. 523 * 524 * @returns The ECN value. 525 * 526 */ GetEcn(void) const527 Ecn GetEcn(void) const { return mIp6Header.GetEcn(); } 528 529 /** 530 * Indicates if the protocol number from IPv6 header is UDP. 531 * 532 * @retval TRUE If the protocol number in IPv6 header is UDP. 533 * @retval FALSE If the protocol number in IPv6 header is not UDP. 534 * 535 */ IsUdp(void) const536 bool IsUdp(void) const { return GetIpProto() == kProtoUdp; } 537 538 /** 539 * Indicates if the protocol number from IPv6 header is TCP. 540 * 541 * @retval TRUE If the protocol number in IPv6 header is TCP. 542 * @retval FALSE If the protocol number in IPv6 header is not TCP. 543 * 544 */ IsTcp(void) const545 bool IsTcp(void) const { return GetIpProto() == kProtoTcp; } 546 547 /** 548 * Indicates if the protocol number from IPv6 header is ICMPv6. 549 * 550 * @retval TRUE If the protocol number in IPv6 header is ICMPv6. 551 * @retval FALSE If the protocol number in IPv6 header is not ICMPv6. 552 * 553 */ IsIcmp6(void) const554 bool IsIcmp6(void) const { return GetIpProto() == kProtoIcmp6; } 555 556 /** 557 * Returns the source IPv6 address from IPv6 header. 558 * 559 * @returns The source IPv6 address. 560 * 561 */ GetSourceAddress(void) const562 const Address &GetSourceAddress(void) const { return mIp6Header.GetSource(); } 563 564 /** 565 * Returns the destination IPv6 address from IPv6 header. 566 * 567 * @returns The destination IPv6 address. 568 * 569 */ GetDestinationAddress(void) const570 const Address &GetDestinationAddress(void) const { return mIp6Header.GetDestination(); } 571 572 /** 573 * Returns the UDP header. 574 * 575 * MUST be used when `IsUdp() == true`. Otherwise its behavior is undefined 576 * 577 * @returns The UDP header. 578 * 579 */ GetUdpHeader(void) const580 const Udp::Header &GetUdpHeader(void) const { return mHeader.mUdp; } 581 582 /** 583 * Returns the TCP header. 584 * 585 * MUST be used when `IsTcp() == true`. Otherwise its behavior is undefined 586 * 587 * @returns The TCP header. 588 * 589 */ GetTcpHeader(void) const590 const Tcp::Header &GetTcpHeader(void) const { return mHeader.mTcp; } 591 592 /** 593 * Returns the ICMPv6 header. 594 * 595 * MUST be used when `IsIcmp6() == true`. Otherwise its behavior is undefined 596 * 597 * @returns The ICMPv6 header. 598 * 599 */ GetIcmpHeader(void) const600 const Icmp::Header &GetIcmpHeader(void) const { return mHeader.mIcmp; } 601 602 /** 603 * Returns the source port number if header is UDP or TCP, or zero otherwise 604 * 605 * @returns The source port number under UDP / TCP or zero. 606 * 607 */ 608 uint16_t GetSourcePort(void) const; 609 610 /** 611 * Returns the destination port number if header is UDP or TCP, or zero otherwise. 612 * 613 * @returns The destination port number under UDP / TCP or zero. 614 * 615 */ 616 uint16_t GetDestinationPort(void) const; 617 618 /** 619 * Returns the checksum values from corresponding UDP, TCP, or ICMPv6 header. 620 * 621 * @returns The checksum value. 622 * 623 */ 624 uint16_t GetChecksum(void) const; 625 626 private: 627 Header mIp6Header; 628 union 629 { 630 Udp::Header mUdp; 631 Tcp::Header mTcp; 632 Icmp::Header mIcmp; 633 } mHeader; 634 }; 635 636 /** 637 * @} 638 * 639 */ 640 641 } // namespace Ip6 642 } // namespace ot 643 644 #endif // IP6_HPP_ 645