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