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