1 /*
2 * Copyright (c) 2020, 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 checksum calculation.
32 */
33
34 #include "checksum.hpp"
35
36 #include "common/code_utils.hpp"
37 #include "common/message.hpp"
38 #include "net/icmp6.hpp"
39 #include "net/ip4_types.hpp"
40 #include "net/tcp6.hpp"
41 #include "net/udp6.hpp"
42
43 namespace ot {
44
AddUint8(uint8_t aUint8)45 void Checksum::AddUint8(uint8_t aUint8)
46 {
47 uint16_t newValue = mValue;
48
49 // BigEndian encoding: Even index is MSB and odd index is LSB.
50
51 newValue += mAtOddIndex ? aUint8 : (static_cast<uint16_t>(aUint8) << 8);
52
53 // Calculate one's complement sum.
54
55 if (newValue < mValue)
56 {
57 newValue++;
58 }
59
60 mValue = newValue;
61 mAtOddIndex = !mAtOddIndex;
62 }
63
AddUint16(uint16_t aUint16)64 void Checksum::AddUint16(uint16_t aUint16)
65 {
66 // BigEndian encoding
67 AddUint8(static_cast<uint8_t>(aUint16 >> 8));
68 AddUint8(static_cast<uint8_t>(aUint16 & 0xff));
69 }
70
AddData(const uint8_t * aBuffer,uint16_t aLength)71 void Checksum::AddData(const uint8_t *aBuffer, uint16_t aLength)
72 {
73 for (uint16_t i = 0; i < aLength; i++)
74 {
75 AddUint8(aBuffer[i]);
76 }
77 }
78
WriteToMessage(uint16_t aOffset,Message & aMessage) const79 void Checksum::WriteToMessage(uint16_t aOffset, Message &aMessage) const
80 {
81 uint16_t checksum = GetValue();
82
83 if (checksum != 0xffff)
84 {
85 checksum = ~checksum;
86 }
87
88 checksum = Encoding::BigEndian::HostSwap16(checksum);
89
90 aMessage.Write(aOffset, checksum);
91 }
92
Calculate(const Ip6::Address & aSource,const Ip6::Address & aDestination,uint8_t aIpProto,const Message & aMessage)93 void Checksum::Calculate(const Ip6::Address &aSource,
94 const Ip6::Address &aDestination,
95 uint8_t aIpProto,
96 const Message & aMessage)
97 {
98 Message::Chunk chunk;
99 uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
100
101 // Pseudo-header for checksum calculation (RFC-2460).
102
103 AddData(aSource.GetBytes(), sizeof(Ip6::Address));
104 AddData(aDestination.GetBytes(), sizeof(Ip6::Address));
105 AddUint16(length);
106 AddUint16(static_cast<uint16_t>(aIpProto));
107
108 // Add message content (from offset to the end) to checksum.
109
110 aMessage.GetFirstChunk(aMessage.GetOffset(), length, chunk);
111
112 while (chunk.GetLength() > 0)
113 {
114 AddData(chunk.GetBytes(), chunk.GetLength());
115 aMessage.GetNextChunk(length, chunk);
116 }
117 }
118
Calculate(const Ip4::Address & aSource,const Ip4::Address & aDestination,uint8_t aIpProto,const Message & aMessage)119 void Checksum::Calculate(const Ip4::Address &aSource,
120 const Ip4::Address &aDestination,
121 uint8_t aIpProto,
122 const Message & aMessage)
123 {
124 Message::Chunk chunk;
125 uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
126
127 // Pseudo-header for checksum calculation (RFC-768/792/793).
128 // Note: ICMP checksum won't count the presudo header like TCP and UDP.
129 if (aIpProto != Ip4::kProtoIcmp)
130 {
131 AddData(aSource.GetBytes(), sizeof(Ip4::Address));
132 AddData(aDestination.GetBytes(), sizeof(Ip4::Address));
133 AddUint16(static_cast<uint16_t>(aIpProto));
134 AddUint16(length);
135 }
136
137 // Add message content (from offset to the end) to checksum.
138
139 aMessage.GetFirstChunk(aMessage.GetOffset(), length, chunk);
140
141 while (chunk.GetLength() > 0)
142 {
143 AddData(chunk.GetBytes(), chunk.GetLength());
144 aMessage.GetNextChunk(length, chunk);
145 }
146 }
147
VerifyMessageChecksum(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo,uint8_t aIpProto)148 Error Checksum::VerifyMessageChecksum(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint8_t aIpProto)
149 {
150 Checksum checksum;
151
152 checksum.Calculate(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), aIpProto, aMessage);
153
154 return (checksum.GetValue() == kValidRxChecksum) ? kErrorNone : kErrorDrop;
155 }
156
UpdateMessageChecksum(Message & aMessage,const Ip6::Address & aSource,const Ip6::Address & aDestination,uint8_t aIpProto)157 void Checksum::UpdateMessageChecksum(Message & aMessage,
158 const Ip6::Address &aSource,
159 const Ip6::Address &aDestination,
160 uint8_t aIpProto)
161 {
162 uint16_t headerOffset;
163 Checksum checksum;
164
165 switch (aIpProto)
166 {
167 case Ip6::kProtoTcp:
168 headerOffset = Ip6::Tcp::Header::kChecksumFieldOffset;
169 break;
170
171 case Ip6::kProtoUdp:
172 headerOffset = Ip6::Udp::Header::kChecksumFieldOffset;
173 break;
174
175 case Ip6::kProtoIcmp6:
176 headerOffset = Ip6::Icmp::Header::kChecksumFieldOffset;
177 break;
178
179 default:
180 ExitNow();
181 }
182
183 // Clear the checksum before calculating it.
184 aMessage.Write<uint16_t>(aMessage.GetOffset() + headerOffset, 0);
185 checksum.Calculate(aSource, aDestination, aIpProto, aMessage);
186 checksum.WriteToMessage(aMessage.GetOffset() + headerOffset, aMessage);
187
188 exit:
189 return;
190 }
191
UpdateMessageChecksum(Message & aMessage,const Ip4::Address & aSource,const Ip4::Address & aDestination,uint8_t aIpProto)192 void Checksum::UpdateMessageChecksum(Message & aMessage,
193 const Ip4::Address &aSource,
194 const Ip4::Address &aDestination,
195 uint8_t aIpProto)
196 {
197 uint16_t headerOffset;
198 Checksum checksum;
199
200 switch (aIpProto)
201 {
202 case Ip4::kProtoTcp:
203 headerOffset = Ip4::Tcp::Header::kChecksumFieldOffset;
204 break;
205
206 case Ip4::kProtoUdp:
207 headerOffset = Ip4::Udp::Header::kChecksumFieldOffset;
208 break;
209
210 case Ip4::kProtoIcmp:
211 headerOffset = Ip4::Icmp::Header::kChecksumFieldOffset;
212 break;
213
214 default:
215 ExitNow();
216 }
217
218 // Clear the checksum before calculating it.
219 aMessage.Write<uint16_t>(aMessage.GetOffset() + headerOffset, 0);
220 checksum.Calculate(aSource, aDestination, aIpProto, aMessage);
221 checksum.WriteToMessage(aMessage.GetOffset() + headerOffset, aMessage);
222
223 exit:
224 return;
225 }
226
UpdateIp4HeaderChecksum(Ip4::Header & aHeader)227 void Checksum::UpdateIp4HeaderChecksum(Ip4::Header &aHeader)
228 {
229 Checksum checksum;
230
231 aHeader.SetChecksum(0);
232 checksum.AddData(reinterpret_cast<const uint8_t *>(&aHeader), sizeof(aHeader));
233 aHeader.SetChecksum(~checksum.GetValue());
234 }
235
236 } // namespace ot
237