blob: 06f9136e4bcf269ddf92243d94d42f91b3e1cbf8 [file] [log] [blame]
Jonathan Hui4f9945c2016-05-10 20:48:47 -07001/*
Marcin K Szczodrak99870fa2016-05-12 22:39:11 -07002 * Copyright (c) 2016, Nest Labs, Inc.
3 * All rights reserved.
Jonathan Hui4f9945c2016-05-10 20:48:47 -07004 *
Marcin K Szczodrak99870fa2016-05-12 22:39:11 -07005 * 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.
Jonathan Hui4f9945c2016-05-10 20:48:47 -070015 *
Marcin K Szczodrak99870fa2016-05-12 22:39:11 -070016 * 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.
Jonathan Hui4f9945c2016-05-10 20:48:47 -070027 */
28
29/**
30 * @file
31 * This file implements ICMPv6.
32 */
33
34#include <string.h>
35
36#include <common/code_utils.hpp>
37#include <common/debug.hpp>
38#include <common/logging.hpp>
39#include <common/message.hpp>
40#include <net/icmp6.hpp>
41#include <net/ip6.hpp>
42
43using Thread::Encoding::BigEndian::HostSwap16;
44
45namespace Thread {
46namespace Ip6 {
47
Jonathan Huia43fb452016-06-24 12:43:02 -070048bool Icmp::sIsEchoEnabled = true;
49
Jonathan Hui027e0092016-08-29 09:31:26 -070050uint16_t Icmp::sEchoSequence = 1;
Jonathan Hui4f9945c2016-05-10 20:48:47 -070051IcmpHandler *IcmpHandler::sHandlers = NULL;
Jonathan Hui027e0092016-08-29 09:31:26 -070052Icmp::EchoReplyHandler Icmp::sEchoReplyHandler = NULL;
53void *Icmp::sEchoReplyContext = NULL;
Jonathan Hui4f9945c2016-05-10 20:48:47 -070054
Jonathan Hui027e0092016-08-29 09:31:26 -070055Message *Icmp::NewMessage(uint16_t aReserved)
Jonathan Hui4f9945c2016-05-10 20:48:47 -070056{
Jonathan Hui027e0092016-08-29 09:31:26 -070057 return Ip6::NewMessage(sizeof(IcmpHeader) + aReserved);
Jonathan Hui4f9945c2016-05-10 20:48:47 -070058}
59
60ThreadError Icmp::RegisterCallbacks(IcmpHandler &aHandler)
61{
62 ThreadError error = kThreadError_None;
63
64 for (IcmpHandler *cur = IcmpHandler::sHandlers; cur; cur = cur->mNext)
65 {
66 if (cur == &aHandler)
67 {
68 ExitNow(error = kThreadError_Busy);
69 }
70 }
71
72 aHandler.mNext = IcmpHandler::sHandlers;
73 IcmpHandler::sHandlers = &aHandler;
74
75exit:
76 return error;
77}
78
Jonathan Hui027e0092016-08-29 09:31:26 -070079void Icmp::SetEchoReplyHandler(EchoReplyHandler aHandler, void *aContext)
80{
81 sEchoReplyHandler = aHandler;
82 sEchoReplyContext = aContext;
83}
84
85ThreadError Icmp::SendEchoRequest(Message &aMessage, const MessageInfo &aMessageInfo)
86{
87 ThreadError error = kThreadError_None;
88 MessageInfo messageInfoLocal;
89 IcmpHeader icmpHeader;
90
91 messageInfoLocal = aMessageInfo;
92
93 icmpHeader.Init();
94 icmpHeader.SetType(IcmpHeader::kTypeEchoRequest);
95 icmpHeader.SetId(1);
96 icmpHeader.SetSequence(sEchoSequence++);
97
98 SuccessOrExit(error = aMessage.Prepend(&icmpHeader, sizeof(icmpHeader)));
99 aMessage.SetOffset(0);
100 SuccessOrExit(error = Ip6::SendDatagram(aMessage, messageInfoLocal, kProtoIcmp6));
101
102 otLogInfoIcmp("Sent echo request\n");
103
104exit:
105 return error;
106}
107
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700108ThreadError Icmp::SendError(const Address &aDestination, IcmpHeader::Type aType, IcmpHeader::Code aCode,
109 const Header &aHeader)
110{
111 ThreadError error = kThreadError_None;
112 MessageInfo messageInfo;
Jonathan Hui3a439e52016-05-11 17:01:38 -0700113 Message *message = NULL;
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700114 IcmpHeader icmp6Header;
115
116 VerifyOrExit((message = Ip6::NewMessage(0)) != NULL, error = kThreadError_NoBufs);
117 SuccessOrExit(error = message->SetLength(sizeof(icmp6Header) + sizeof(aHeader)));
118
119 message->Write(sizeof(icmp6Header), sizeof(aHeader), &aHeader);
120
121 icmp6Header.Init();
122 icmp6Header.SetType(aType);
123 icmp6Header.SetCode(aCode);
124 message->Write(0, sizeof(icmp6Header), &icmp6Header);
125
126 memset(&messageInfo, 0, sizeof(messageInfo));
127 messageInfo.mPeerAddr = aDestination;
128
129 SuccessOrExit(error = Ip6::SendDatagram(*message, messageInfo, kProtoIcmp6));
130
131 otLogInfoIcmp("Sent ICMPv6 Error\n");
132
133exit:
Jonathan Hui3a439e52016-05-11 17:01:38 -0700134
135 if (error != kThreadError_None && message != NULL)
136 {
137 Message::Free(*message);
138 }
139
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700140 return error;
141}
142
143ThreadError Icmp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo)
144{
145 ThreadError error = kThreadError_None;
146 uint16_t payloadLength;
147 IcmpHeader icmp6Header;
148 uint16_t checksum;
149
150 payloadLength = aMessage.GetLength() - aMessage.GetOffset();
151
152 // check length
153 VerifyOrExit(payloadLength >= IcmpHeader::GetDataOffset(), error = kThreadError_Drop);
154 aMessage.Read(aMessage.GetOffset(), sizeof(icmp6Header), &icmp6Header);
155
156 // verify checksum
157 checksum = Ip6::ComputePseudoheaderChecksum(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(),
158 payloadLength, kProtoIcmp6);
159 checksum = aMessage.UpdateChecksum(checksum, aMessage.GetOffset(), payloadLength);
160 VerifyOrExit(checksum == 0xffff, ;);
161
162 switch (icmp6Header.GetType())
163 {
164 case IcmpHeader::kTypeEchoRequest:
165 return HandleEchoRequest(aMessage, aMessageInfo);
166
167 case IcmpHeader::kTypeEchoReply:
Jonathan Hui027e0092016-08-29 09:31:26 -0700168 return HandleEchoReply(aMessage, aMessageInfo);
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700169
170 case IcmpHeader::kTypeDstUnreach:
171 return HandleDstUnreach(aMessage, aMessageInfo, icmp6Header);
172 }
173
174exit:
175 return error;
176}
177
178ThreadError Icmp::HandleDstUnreach(Message &aMessage, const MessageInfo &aMessageInfo,
179 const IcmpHeader &aIcmpheader)
180{
181 aMessage.MoveOffset(sizeof(aIcmpheader));
182
183 for (IcmpHandler *handler = IcmpHandler::sHandlers; handler; handler = handler->mNext)
184 {
185 handler->HandleDstUnreach(aMessage, aMessageInfo, aIcmpheader);
186 }
187
188 return kThreadError_None;
189}
190
191ThreadError Icmp::HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMessageInfo)
192{
193 ThreadError error = kThreadError_None;
194 IcmpHeader icmp6Header;
Jonathan Hui3a439e52016-05-11 17:01:38 -0700195 Message *replyMessage = NULL;
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700196 MessageInfo replyMessageInfo;
197 uint16_t payloadLength;
198
Jonathan Huia43fb452016-06-24 12:43:02 -0700199 VerifyOrExit(sIsEchoEnabled, ;);
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700200
201 otLogInfoIcmp("Received Echo Request\n");
202
203 icmp6Header.Init();
204 icmp6Header.SetType(IcmpHeader::kTypeEchoReply);
205
206 VerifyOrExit((replyMessage = Ip6::NewMessage(0)) != NULL, otLogDebgIcmp("icmp fail\n"));
Jonathan Huia43fb452016-06-24 12:43:02 -0700207 payloadLength = aRequestMessage.GetLength() - aRequestMessage.GetOffset() - IcmpHeader::GetDataOffset();
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700208 SuccessOrExit(replyMessage->SetLength(IcmpHeader::GetDataOffset() + payloadLength));
209
210 replyMessage->Write(0, IcmpHeader::GetDataOffset(), &icmp6Header);
211 aRequestMessage.CopyTo(aRequestMessage.GetOffset() + IcmpHeader::GetDataOffset(),
212 IcmpHeader::GetDataOffset(), payloadLength, *replyMessage);
213
214 memset(&replyMessageInfo, 0, sizeof(replyMessageInfo));
215 replyMessageInfo.GetPeerAddr() = aMessageInfo.GetPeerAddr();
216
217 if (!aMessageInfo.GetSockAddr().IsMulticast())
218 {
219 replyMessageInfo.GetSockAddr() = aMessageInfo.GetSockAddr();
220 }
221
222 replyMessageInfo.mInterfaceId = aMessageInfo.mInterfaceId;
223
224 SuccessOrExit(error = Ip6::SendDatagram(*replyMessage, replyMessageInfo, kProtoIcmp6));
225
226 otLogInfoIcmp("Sent Echo Reply\n");
227
228exit:
Jonathan Hui3a439e52016-05-11 17:01:38 -0700229
230 if (error != kThreadError_None && replyMessage != NULL)
231 {
232 Message::Free(*replyMessage);
233 }
234
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700235 return error;
236}
237
Jonathan Hui027e0092016-08-29 09:31:26 -0700238ThreadError Icmp::HandleEchoReply(Message &aMessage, const MessageInfo &aMessageInfo)
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700239{
Jonathan Hui027e0092016-08-29 09:31:26 -0700240 VerifyOrExit(sIsEchoEnabled && sEchoReplyHandler, ;);
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700241
Jonathan Hui027e0092016-08-29 09:31:26 -0700242 sEchoReplyHandler(sEchoReplyContext, aMessage, aMessageInfo);
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700243
Jonathan Huia43fb452016-06-24 12:43:02 -0700244exit:
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700245 return kThreadError_None;
246}
247
248ThreadError Icmp::UpdateChecksum(Message &aMessage, uint16_t aChecksum)
249{
250 aChecksum = aMessage.UpdateChecksum(aChecksum, aMessage.GetOffset(),
251 aMessage.GetLength() - aMessage.GetOffset());
252
253 if (aChecksum != 0xffff)
254 {
255 aChecksum = ~aChecksum;
256 }
257
258 aChecksum = HostSwap16(aChecksum);
259 aMessage.Write(aMessage.GetOffset() + IcmpHeader::GetChecksumOffset(), sizeof(aChecksum), &aChecksum);
260 return kThreadError_None;
261}
262
Jonathan Huia43fb452016-06-24 12:43:02 -0700263bool Icmp::IsEchoEnabled(void)
264{
265 return sIsEchoEnabled;
266}
267
268void Icmp::SetEchoEnabled(bool aEnabled)
269{
270 sIsEchoEnabled = aEnabled;
271}
272
Jonathan Hui4f9945c2016-05-10 20:48:47 -0700273} // namespace Ip6
274} // namespace Thread