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