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(void)57 Message *Icmp::NewMessage(void) { return Get<Ip6>().NewMessage(sizeof(Header)); }
58
RegisterHandler(Handler & aHandler)59 Error Icmp::RegisterHandler(Handler &aHandler) { return mHandlers.Add(aHandler); }
60
SendEchoRequest(Message & aMessage,const MessageInfo & aMessageInfo,uint16_t aIdentifier)61 Error Icmp::SendEchoRequest(Message &aMessage, const MessageInfo &aMessageInfo, uint16_t aIdentifier)
62 {
63 Error error = kErrorNone;
64 MessageInfo messageInfoLocal;
65 Header icmpHeader;
66
67 messageInfoLocal = aMessageInfo;
68
69 icmpHeader.Clear();
70 icmpHeader.SetType(Header::kTypeEchoRequest);
71 icmpHeader.SetId(aIdentifier);
72 icmpHeader.SetSequence(mEchoSequence++);
73
74 SuccessOrExit(error = aMessage.Prepend(icmpHeader));
75 aMessage.SetOffset(0);
76 SuccessOrExit(error = Get<Ip6>().SendDatagram(aMessage, messageInfoLocal, kProtoIcmp6));
77
78 LogInfo("Sent echo request: (seq = %d)", icmpHeader.GetSequence());
79
80 exit:
81 return error;
82 }
83
SendError(Header::Type aType,Header::Code aCode,const MessageInfo & aMessageInfo,const Message & aMessage)84 Error Icmp::SendError(Header::Type aType, Header::Code aCode, const MessageInfo &aMessageInfo, const Message &aMessage)
85 {
86 Error error;
87 Headers headers;
88
89 SuccessOrExit(error = headers.ParseFrom(aMessage));
90 error = SendError(aType, aCode, aMessageInfo, headers);
91
92 exit:
93 return error;
94 }
95
SendError(Header::Type aType,Header::Code aCode,const MessageInfo & aMessageInfo,const Headers & aHeaders)96 Error Icmp::SendError(Header::Type aType, Header::Code aCode, const MessageInfo &aMessageInfo, const Headers &aHeaders)
97 {
98 Error error = kErrorNone;
99 MessageInfo messageInfoLocal;
100 Message *message = nullptr;
101 Header icmp6Header;
102 Message::Settings settings(Message::kWithLinkSecurity, Message::kPriorityNet);
103
104 if (aHeaders.GetIpProto() == kProtoIcmp6)
105 {
106 VerifyOrExit(!aHeaders.GetIcmpHeader().IsError());
107 }
108
109 messageInfoLocal = aMessageInfo;
110
111 VerifyOrExit((message = Get<Ip6>().NewMessage(0, settings)) != nullptr, error = kErrorNoBufs);
112
113 // Prepare the ICMPv6 error message. We only include the IPv6 header
114 // of the original message causing the error.
115
116 icmp6Header.Clear();
117 icmp6Header.SetType(aType);
118 icmp6Header.SetCode(aCode);
119 SuccessOrExit(error = message->Append(icmp6Header));
120 SuccessOrExit(error = message->Append(aHeaders.GetIp6Header()));
121
122 SuccessOrExit(error = Get<Ip6>().SendDatagram(*message, messageInfoLocal, kProtoIcmp6));
123
124 LogInfo("Sent ICMPv6 Error");
125
126 exit:
127 FreeMessageOnError(message, error);
128 return error;
129 }
130
HandleMessage(Message & aMessage,MessageInfo & aMessageInfo)131 Error Icmp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo)
132 {
133 Error error = kErrorNone;
134 Header icmp6Header;
135
136 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), icmp6Header));
137
138 SuccessOrExit(error = Checksum::VerifyMessageChecksum(aMessage, aMessageInfo, kProtoIcmp6));
139
140 if (icmp6Header.GetType() == Header::kTypeEchoRequest)
141 {
142 SuccessOrExit(error = HandleEchoRequest(aMessage, aMessageInfo));
143 }
144
145 aMessage.MoveOffset(sizeof(icmp6Header));
146
147 for (Handler &handler : mHandlers)
148 {
149 handler.HandleReceiveMessage(aMessage, aMessageInfo, icmp6Header);
150 }
151
152 exit:
153 return error;
154 }
155
ShouldHandleEchoRequest(const MessageInfo & aMessageInfo)156 bool Icmp::ShouldHandleEchoRequest(const MessageInfo &aMessageInfo)
157 {
158 bool rval = false;
159
160 switch (mEchoMode)
161 {
162 case OT_ICMP6_ECHO_HANDLER_DISABLED:
163 rval = false;
164 break;
165 case OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY:
166 rval = !aMessageInfo.GetSockAddr().IsMulticast();
167 break;
168 case OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY:
169 rval = aMessageInfo.GetSockAddr().IsMulticast();
170 break;
171 case OT_ICMP6_ECHO_HANDLER_ALL:
172 rval = true;
173 break;
174 }
175
176 return rval;
177 }
178
HandleEchoRequest(Message & aRequestMessage,const MessageInfo & aMessageInfo)179 Error Icmp::HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMessageInfo)
180 {
181 Error error = kErrorNone;
182 Header icmp6Header;
183 Message *replyMessage = nullptr;
184 MessageInfo replyMessageInfo;
185 uint16_t dataOffset;
186
187 // always handle Echo Request destined for RLOC or ALOC
188 VerifyOrExit(ShouldHandleEchoRequest(aMessageInfo) || aMessageInfo.GetSockAddr().GetIid().IsLocator());
189
190 LogInfo("Received Echo Request");
191
192 icmp6Header.Clear();
193 icmp6Header.SetType(Header::kTypeEchoReply);
194
195 if ((replyMessage = Get<Ip6>().NewMessage(0)) == nullptr)
196 {
197 LogDebg("Failed to allocate a new message");
198 ExitNow();
199 }
200
201 dataOffset = aRequestMessage.GetOffset() + Header::kDataFieldOffset;
202
203 SuccessOrExit(error = replyMessage->AppendBytes(&icmp6Header, Header::kDataFieldOffset));
204 SuccessOrExit(error = replyMessage->AppendBytesFromMessage(aRequestMessage, dataOffset,
205 aRequestMessage.GetLength() - dataOffset));
206
207 replyMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
208
209 if (!aMessageInfo.GetSockAddr().IsMulticast())
210 {
211 replyMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr());
212 }
213
214 SuccessOrExit(error = Get<Ip6>().SendDatagram(*replyMessage, replyMessageInfo, kProtoIcmp6));
215
216 IgnoreError(replyMessage->Read(replyMessage->GetOffset(), icmp6Header));
217 LogInfo("Sent Echo Reply (seq = %d)", icmp6Header.GetSequence());
218
219 exit:
220 FreeMessageOnError(replyMessage, error);
221 return error;
222 }
223
224 } // namespace Ip6
225 } // namespace ot
226