Coverage Report

Created: 2025-11-09 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opendnp3/cpp/lib/src/link/LinkFrame.cpp
Line
Count
Source
1
/*
2
 * Copyright 2013-2022 Step Function I/O, LLC
3
 *
4
 * Licensed to Green Energy Corp (www.greenenergycorp.com) and Step Function I/O
5
 * LLC (https://stepfunc.io) under one or more contributor license agreements.
6
 * See the NOTICE file distributed with this work for additional information
7
 * regarding copyright ownership. Green Energy Corp and Step Function I/O LLC license
8
 * this file to you under the Apache License, Version 2.0 (the "License"); you
9
 * may not use this file except in compliance with the License. You may obtain
10
 * a copy of the License at:
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
#include "LinkFrame.h"
21
22
#include "link/CRC.h"
23
#include "link/LinkHeader.h"
24
#include "logging/LogMacros.h"
25
26
#include "opendnp3/logging/LogLevels.h"
27
28
#include <ser4cpp/serialization/LittleEndian.h>
29
30
namespace opendnp3
31
{
32
33
void LinkFrame::ReadUserData(const uint8_t* pSrc, uint8_t* pDest, size_t len)
34
40.4k
{
35
40.4k
    size_t length = len;
36
40.4k
    uint8_t const* pRead = pSrc;
37
40.4k
    uint8_t* pWrite = pDest;
38
39
129k
    while (length > 0)
40
89.2k
    {
41
89.2k
        size_t max = LPDU_DATA_BLOCK_SIZE;
42
89.2k
        size_t num = (length <= max) ? length : max;
43
89.2k
        size_t num_with_crc = num + 2;
44
89.2k
        memmove(pWrite, pRead, num);
45
89.2k
        pRead += num_with_crc;
46
89.2k
        pWrite += num;
47
89.2k
        length -= num;
48
89.2k
    }
49
40.4k
}
50
51
bool LinkFrame::ValidateBodyCRC(const uint8_t* pBody, size_t length)
52
40.4k
{
53
129k
    while (length > 0)
54
89.2k
    {
55
89.2k
        size_t max = LPDU_DATA_BLOCK_SIZE;
56
89.2k
        size_t num = (length <= max) ? length : max;
57
58
89.2k
        if (CRC::IsCorrectCRC(pBody, num))
59
89.2k
        {
60
89.2k
            pBody += (num + 2);
61
89.2k
            length -= num;
62
89.2k
        }
63
0
        else
64
0
        {
65
0
            return false;
66
0
        }
67
89.2k
    }
68
40.4k
    return true;
69
40.4k
}
70
71
size_t LinkFrame::CalcFrameSize(size_t dataLength)
72
83.3k
{
73
83.3k
    return LPDU_HEADER_SIZE + CalcUserDataSize(dataLength);
74
83.3k
}
75
76
size_t LinkFrame::CalcUserDataSize(size_t dataLength)
77
83.3k
{
78
83.3k
    if (dataLength > 0)
79
81.8k
    {
80
81.8k
        size_t mod16 = dataLength % LPDU_DATA_BLOCK_SIZE;
81
81.8k
        size_t size = (dataLength / LPDU_DATA_BLOCK_SIZE) * LPDU_DATA_PLUS_CRC_SIZE; // complete blocks
82
81.8k
        return (mod16 > 0) ? (size + mod16 + LPDU_CRC_SIZE) : size;                  // possible partial block
83
81.8k
    }
84
85
1.47k
    return 0;
86
83.3k
}
87
88
////////////////////////////////////////////////
89
//
90
//  Outgoing frame formatting functions for Sec to Pri transactions
91
//
92
////////////////////////////////////////////////
93
94
ser4cpp::rseq_t LinkFrame::FormatAck(
95
    ser4cpp::wseq_t& buffer, bool aIsMaster, bool aIsRcvBuffFull, uint16_t aDest, uint16_t aSrc, Logger* pLogger)
96
0
{
97
0
    return FormatHeader(buffer, 0, aIsMaster, false, aIsRcvBuffFull, LinkFunction::SEC_ACK, aDest, aSrc, pLogger);
98
0
}
99
100
ser4cpp::rseq_t LinkFrame::FormatNack(
101
    ser4cpp::wseq_t& buffer, bool aIsMaster, bool aIsRcvBuffFull, uint16_t aDest, uint16_t aSrc, Logger* pLogger)
102
0
{
103
0
    return FormatHeader(buffer, 0, aIsMaster, false, aIsRcvBuffFull, LinkFunction::SEC_NACK, aDest, aSrc, pLogger);
104
0
}
105
106
ser4cpp::rseq_t LinkFrame::FormatLinkStatus(
107
    ser4cpp::wseq_t& buffer, bool aIsMaster, bool aIsRcvBuffFull, uint16_t aDest, uint16_t aSrc, Logger* pLogger)
108
0
{
109
0
    return FormatHeader(buffer, 0, aIsMaster, false, aIsRcvBuffFull, LinkFunction::SEC_LINK_STATUS, aDest, aSrc,
110
0
                        pLogger);
111
0
}
112
113
ser4cpp::rseq_t LinkFrame::FormatNotSupported(
114
    ser4cpp::wseq_t& buffer, bool aIsMaster, bool aIsRcvBuffFull, uint16_t aDest, uint16_t aSrc, Logger* pLogger)
115
0
{
116
0
    return FormatHeader(buffer, 0, aIsMaster, false, aIsRcvBuffFull, LinkFunction::SEC_NOT_SUPPORTED, aDest, aSrc,
117
0
                        pLogger);
118
0
}
119
120
////////////////////////////////////////////////
121
//
122
//  Outgoing frame formatting functions for Pri to Sec transactions
123
//
124
////////////////////////////////////////////////
125
126
ser4cpp::rseq_t LinkFrame::FormatResetLinkStates(
127
    ser4cpp::wseq_t& buffer, bool aIsMaster, uint16_t aDest, uint16_t aSrc, Logger* pLogger)
128
0
{
129
0
    return FormatHeader(buffer, 0, aIsMaster, false, false, LinkFunction::PRI_RESET_LINK_STATES, aDest, aSrc, pLogger);
130
0
}
131
132
ser4cpp::rseq_t LinkFrame::FormatRequestLinkStatus(
133
    ser4cpp::wseq_t& buffer, bool aIsMaster, uint16_t aDest, uint16_t aSrc, Logger* pLogger)
134
0
{
135
0
    return FormatHeader(buffer, 0, aIsMaster, false, false, LinkFunction::PRI_REQUEST_LINK_STATUS, aDest, aSrc,
136
0
                        pLogger);
137
0
}
138
139
ser4cpp::rseq_t LinkFrame::FormatTestLinkStatus(
140
    ser4cpp::wseq_t& buffer, bool aIsMaster, bool aFcb, uint16_t aDest, uint16_t aSrc, Logger* pLogger)
141
0
{
142
0
    return FormatHeader(buffer, 0, aIsMaster, aFcb, true, LinkFunction::PRI_TEST_LINK_STATES, aDest, aSrc, pLogger);
143
0
}
144
145
ser4cpp::rseq_t LinkFrame::FormatConfirmedUserData(ser4cpp::wseq_t& buffer,
146
                                                   bool aIsMaster,
147
                                                   bool aFcb,
148
                                                   uint16_t aDest,
149
                                                   uint16_t aSrc,
150
                                                   ser4cpp::rseq_t user_data,
151
                                                   Logger* pLogger)
152
0
{
153
0
    if (user_data.length() > LPDU_MAX_USER_DATA_SIZE)
154
0
    {
155
0
        return ser4cpp::rseq_t::empty();
156
0
    }
157
158
0
    auto userDataSize = CalcUserDataSize(user_data.length());
159
0
    auto ret = buffer.readonly().take(userDataSize + LPDU_HEADER_SIZE);
160
0
    FormatHeader(buffer, static_cast<uint8_t>(user_data.length()), aIsMaster, aFcb, true,
161
0
                 LinkFunction::PRI_CONFIRMED_USER_DATA, aDest, aSrc, pLogger);
162
0
    WriteUserData(user_data, buffer, user_data.length());
163
0
    buffer.advance(userDataSize);
164
0
    return ret;
165
0
}
166
167
ser4cpp::rseq_t LinkFrame::FormatUnconfirmedUserData(
168
    ser4cpp::wseq_t& buffer, bool aIsMaster, uint16_t aDest, uint16_t aSrc, ser4cpp::rseq_t user_data, Logger* pLogger)
169
0
{
170
0
    if (user_data.length() > LPDU_MAX_USER_DATA_SIZE)
171
0
    {
172
0
        return ser4cpp::rseq_t::empty();
173
0
    }
174
175
0
    auto userDataSize = CalcUserDataSize(user_data.length());
176
0
    auto ret = buffer.readonly().take(userDataSize + LPDU_HEADER_SIZE);
177
0
    FormatHeader(buffer, static_cast<uint8_t>(user_data.length()), aIsMaster, false, false,
178
0
                 LinkFunction::PRI_UNCONFIRMED_USER_DATA, aDest, aSrc, pLogger);
179
0
    WriteUserData(user_data, buffer, user_data.length());
180
0
    buffer.advance(userDataSize);
181
0
    return ret;
182
0
}
183
184
ser4cpp::rseq_t LinkFrame::FormatHeader(ser4cpp::wseq_t& buffer,
185
                                        uint8_t aDataLength,
186
                                        bool aIsMaster,
187
                                        bool aFcb,
188
                                        bool aFcvDfc,
189
                                        LinkFunction aFuncCode,
190
                                        uint16_t aDest,
191
                                        uint16_t aSrc,
192
                                        Logger* pLogger)
193
0
{
194
0
    if (buffer.length() < LPDU_HEADER_SIZE)
195
0
    {
196
0
        return ser4cpp::rseq_t::empty();
197
0
    }
198
199
0
    LinkHeader header(aDataLength + LPDU_MIN_LENGTH, aSrc, aDest, aIsMaster, aFcvDfc, aFcb, aFuncCode);
200
201
0
    FORMAT_LOGGER_BLOCK(pLogger, flags::LINK_TX, "Function: %s Dest: %u Source: %u Length: %u",
202
0
                        LinkFunctionSpec::to_human_string(aFuncCode), aDest, aSrc, aDataLength);
203
204
0
    header.Write(buffer);
205
0
    auto ret = buffer.readonly().take(10);
206
0
    buffer.advance(10);
207
0
    return ret;
208
0
}
209
210
void LinkFrame::WriteUserData(const uint8_t* pSrc, uint8_t* pDest, size_t length)
211
0
{
212
0
    while (length > 0)
213
0
    {
214
0
        uint8_t max = LPDU_DATA_BLOCK_SIZE;
215
0
        size_t num = length > max ? max : length;
216
0
        memcpy(pDest, pSrc, num);
217
0
        CRC::AddCrc(pDest, num);
218
0
        pSrc += num;
219
0
        pDest += (num + 2);
220
0
        length -= num;
221
0
    }
222
0
}
223
224
} // namespace opendnp3