• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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