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