• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements ICMPv6.
32  */
33 
34 #include "icmp6.hpp"
35 
36 #include "common/code_utils.hpp"
37 #include "common/debug.hpp"
38 #include "common/instance.hpp"
39 #include "common/locator_getters.hpp"
40 #include "common/log.hpp"
41 #include "common/message.hpp"
42 #include "net/checksum.hpp"
43 #include "net/ip6.hpp"
44 
45 namespace ot {
46 namespace Ip6 {
47 
48 RegisterLogModule("Icmp6");
49 
Icmp(Instance & aInstance)50 Icmp::Icmp(Instance &aInstance)
51     : InstanceLocator(aInstance)
52     , mEchoSequence(1)
53     , mEchoMode(OT_ICMP6_ECHO_HANDLER_ALL)
54 {
55 }
56 
NewMessage(uint16_t aReserved)57 Message *Icmp::NewMessage(uint16_t aReserved)
58 {
59     return Get<Ip6>().NewMessage(sizeof(Header) + aReserved);
60 }
61 
RegisterHandler(Handler & aHandler)62 Error Icmp::RegisterHandler(Handler &aHandler)
63 {
64     return mHandlers.Add(aHandler);
65 }
66 
SendEchoRequest(Message & aMessage,const MessageInfo & aMessageInfo,uint16_t aIdentifier)67 Error Icmp::SendEchoRequest(Message &aMessage, const MessageInfo &aMessageInfo, uint16_t aIdentifier)
68 {
69     Error       error = kErrorNone;
70     MessageInfo messageInfoLocal;
71     Header      icmpHeader;
72 
73     messageInfoLocal = aMessageInfo;
74 
75     icmpHeader.Clear();
76     icmpHeader.SetType(Header::kTypeEchoRequest);
77     icmpHeader.SetId(aIdentifier);
78     icmpHeader.SetSequence(mEchoSequence++);
79 
80     SuccessOrExit(error = aMessage.Prepend(icmpHeader));
81     aMessage.SetOffset(0);
82     SuccessOrExit(error = Get<Ip6>().SendDatagram(aMessage, messageInfoLocal, kProtoIcmp6));
83 
84     LogInfo("Sent echo request: (seq = %d)", icmpHeader.GetSequence());
85 
86 exit:
87     return error;
88 }
89 
SendError(Header::Type aType,Header::Code aCode,const MessageInfo & aMessageInfo,const Message & aMessage)90 Error Icmp::SendError(Header::Type aType, Header::Code aCode, const MessageInfo &aMessageInfo, const Message &aMessage)
91 {
92     Error   error;
93     Headers headers;
94 
95     SuccessOrExit(error = headers.ParseFrom(aMessage));
96     error = SendError(aType, aCode, aMessageInfo, headers);
97 
98 exit:
99     return error;
100 }
101 
SendError(Header::Type aType,Header::Code aCode,const MessageInfo & aMessageInfo,const Headers & aHeaders)102 Error Icmp::SendError(Header::Type aType, Header::Code aCode, const MessageInfo &aMessageInfo, const Headers &aHeaders)
103 {
104     Error             error = kErrorNone;
105     MessageInfo       messageInfoLocal;
106     Message *         message = nullptr;
107     Header            icmp6Header;
108     Message::Settings settings(Message::kWithLinkSecurity, Message::kPriorityNet);
109 
110     if (aHeaders.GetIpProto() == kProtoIcmp6)
111     {
112         VerifyOrExit(!aHeaders.GetIcmpHeader().IsError());
113     }
114 
115     messageInfoLocal = aMessageInfo;
116 
117     VerifyOrExit((message = Get<Ip6>().NewMessage(0, settings)) != nullptr, error = kErrorNoBufs);
118 
119     // Prepare the ICMPv6 error message. We only include the IPv6 header
120     // of the original message causing the error.
121 
122     icmp6Header.Clear();
123     icmp6Header.SetType(aType);
124     icmp6Header.SetCode(aCode);
125     SuccessOrExit(error = message->Append(icmp6Header));
126     SuccessOrExit(error = message->Append(aHeaders.GetIp6Header()));
127 
128     SuccessOrExit(error = Get<Ip6>().SendDatagram(*message, messageInfoLocal, kProtoIcmp6));
129 
130     LogInfo("Sent ICMPv6 Error");
131 
132 exit:
133     FreeMessageOnError(message, error);
134     return error;
135 }
136 
HandleMessage(Message & aMessage,MessageInfo & aMessageInfo)137 Error Icmp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo)
138 {
139     Error  error = kErrorNone;
140     Header icmp6Header;
141 
142     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), icmp6Header));
143 
144     SuccessOrExit(error = Checksum::VerifyMessageChecksum(aMessage, aMessageInfo, kProtoIcmp6));
145 
146     if (icmp6Header.GetType() == Header::kTypeEchoRequest)
147     {
148         SuccessOrExit(error = HandleEchoRequest(aMessage, aMessageInfo));
149     }
150 
151     aMessage.MoveOffset(sizeof(icmp6Header));
152 
153     for (Handler &handler : mHandlers)
154     {
155         handler.HandleReceiveMessage(aMessage, aMessageInfo, icmp6Header);
156     }
157 
158 exit:
159     return error;
160 }
161 
ShouldHandleEchoRequest(const MessageInfo & aMessageInfo)162 bool Icmp::ShouldHandleEchoRequest(const MessageInfo &aMessageInfo)
163 {
164     bool rval = false;
165 
166     switch (mEchoMode)
167     {
168     case OT_ICMP6_ECHO_HANDLER_DISABLED:
169         rval = false;
170         break;
171     case OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY:
172         rval = !aMessageInfo.GetSockAddr().IsMulticast();
173         break;
174     case OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY:
175         rval = aMessageInfo.GetSockAddr().IsMulticast();
176         break;
177     case OT_ICMP6_ECHO_HANDLER_ALL:
178         rval = true;
179         break;
180     }
181 
182     return rval;
183 }
184 
HandleEchoRequest(Message & aRequestMessage,const MessageInfo & aMessageInfo)185 Error Icmp::HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMessageInfo)
186 {
187     Error       error = kErrorNone;
188     Header      icmp6Header;
189     Message *   replyMessage = nullptr;
190     MessageInfo replyMessageInfo;
191     uint16_t    payloadLength;
192 
193     // always handle Echo Request destined for RLOC or ALOC
194     VerifyOrExit(ShouldHandleEchoRequest(aMessageInfo) || aMessageInfo.GetSockAddr().GetIid().IsLocator());
195 
196     LogInfo("Received Echo Request");
197 
198     icmp6Header.Clear();
199     icmp6Header.SetType(Header::kTypeEchoReply);
200 
201     if ((replyMessage = Get<Ip6>().NewMessage(0)) == nullptr)
202     {
203         LogDebg("Failed to allocate a new message");
204         ExitNow();
205     }
206 
207     payloadLength = aRequestMessage.GetLength() - aRequestMessage.GetOffset() - Header::kDataFieldOffset;
208     SuccessOrExit(error = replyMessage->SetLength(Header::kDataFieldOffset + payloadLength));
209 
210     replyMessage->WriteBytes(0, &icmp6Header, Header::kDataFieldOffset);
211     aRequestMessage.CopyTo(aRequestMessage.GetOffset() + Header::kDataFieldOffset, Header::kDataFieldOffset,
212                            payloadLength, *replyMessage);
213 
214     replyMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
215 
216     if (!aMessageInfo.GetSockAddr().IsMulticast())
217     {
218         replyMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr());
219     }
220 
221     SuccessOrExit(error = Get<Ip6>().SendDatagram(*replyMessage, replyMessageInfo, kProtoIcmp6));
222 
223     IgnoreError(replyMessage->Read(replyMessage->GetOffset(), icmp6Header));
224     LogInfo("Sent Echo Reply (seq = %d)", icmp6Header.GetSequence());
225 
226 exit:
227     FreeMessageOnError(replyMessage, error);
228     return error;
229 }
230 
231 } // namespace Ip6
232 } // namespace ot
233