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/tcp6.hpp"
40 #include "net/udp6.hpp"
41
42 namespace ot {
43
AddUint8(uint8_t aUint8)44 void Checksum::AddUint8(uint8_t aUint8)
45 {
46 uint16_t newValue = mValue;
47
48 // BigEndian encoding: Even index is MSB and odd index is LSB.
49
50 newValue += mAtOddIndex ? aUint8 : (static_cast<uint16_t>(aUint8) << 8);
51
52 // Calculate one's complement sum.
53
54 if (newValue < mValue)
55 {
56 newValue++;
57 }
58
59 mValue = newValue;
60 mAtOddIndex = !mAtOddIndex;
61 }
62
AddUint16(uint16_t aUint16)63 void Checksum::AddUint16(uint16_t aUint16)
64 {
65 // BigEndian encoding
66 AddUint8(static_cast<uint8_t>(aUint16 >> 8));
67 AddUint8(static_cast<uint8_t>(aUint16 & 0xff));
68 }
69
AddData(const uint8_t * aBuffer,uint16_t aLength)70 void Checksum::AddData(const uint8_t *aBuffer, uint16_t aLength)
71 {
72 for (uint16_t i = 0; i < aLength; i++)
73 {
74 AddUint8(aBuffer[i]);
75 }
76 }
77
WriteToMessage(uint16_t aOffset,Message & aMessage) const78 void Checksum::WriteToMessage(uint16_t aOffset, Message &aMessage) const
79 {
80 uint16_t checksum = GetValue();
81
82 if (checksum != 0xffff)
83 {
84 checksum = ~checksum;
85 }
86
87 checksum = Encoding::BigEndian::HostSwap16(checksum);
88
89 aMessage.Write(aOffset, checksum);
90 }
91
Calculate(const Ip6::Address & aSource,const Ip6::Address & aDestination,uint8_t aIpProto,const Message & aMessage)92 void Checksum::Calculate(const Ip6::Address &aSource,
93 const Ip6::Address &aDestination,
94 uint8_t aIpProto,
95 const Message & aMessage)
96 {
97 Message::Chunk chunk;
98 uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
99
100 // Pseudo-header for checksum calculation (RFC-2460).
101
102 AddData(aSource.GetBytes(), sizeof(Ip6::Address));
103 AddData(aDestination.GetBytes(), sizeof(Ip6::Address));
104 AddUint16(length);
105 AddUint16(static_cast<uint16_t>(aIpProto));
106
107 // Add message content (from offset to the end) to checksum.
108
109 aMessage.GetFirstChunk(aMessage.GetOffset(), length, chunk);
110
111 while (chunk.GetLength() > 0)
112 {
113 AddData(chunk.GetData(), chunk.GetLength());
114 aMessage.GetNextChunk(length, chunk);
115 }
116 }
117
VerifyMessageChecksum(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo,uint8_t aIpProto)118 Error Checksum::VerifyMessageChecksum(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint8_t aIpProto)
119 {
120 Checksum checksum;
121
122 checksum.Calculate(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), aIpProto, aMessage);
123
124 return (checksum.GetValue() == kValidRxChecksum) ? kErrorNone : kErrorDrop;
125 }
126
UpdateMessageChecksum(Message & aMessage,const Ip6::Address & aSource,const Ip6::Address & aDestination,uint8_t aIpProto)127 void Checksum::UpdateMessageChecksum(Message & aMessage,
128 const Ip6::Address &aSource,
129 const Ip6::Address &aDestination,
130 uint8_t aIpProto)
131 {
132 uint16_t headerOffset;
133 Checksum checksum;
134
135 switch (aIpProto)
136 {
137 case Ip6::kProtoTcp:
138 headerOffset = Ip6::Tcp::Header::kChecksumFieldOffset;
139 break;
140
141 case Ip6::kProtoUdp:
142 headerOffset = Ip6::Udp::Header::kChecksumFieldOffset;
143 break;
144
145 case Ip6::kProtoIcmp6:
146 headerOffset = Ip6::Icmp::Header::kChecksumFieldOffset;
147 break;
148
149 default:
150 ExitNow();
151 }
152
153 checksum.Calculate(aSource, aDestination, aIpProto, aMessage);
154 checksum.WriteToMessage(aMessage.GetOffset() + headerOffset, aMessage);
155
156 exit:
157 return;
158 }
159
160 } // namespace ot
161