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