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