Skip to content

Commit 9fbe92a

Browse files
BonnyAD9sedmicha
authored andcommitted
TCP - Add ipfix decoder
1 parent 71387e9 commit 9fbe92a

File tree

4 files changed

+211
-0
lines changed

4 files changed

+211
-0
lines changed

src/plugins/input/tcp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_library(tcp-input MODULE
77
src/Connection.cpp
88
src/Epoll.cpp
99
src/ClientManager.cpp
10+
src/IpfixDecoder.cpp
1011
)
1112

1213
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_HOST_SYSTEM_NAME STREQUAL "OpenBSD")
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* \file
3+
* \author Jakub Antonín Štigler <[email protected]>
4+
* \brief IPFIX decoder for tcp plugin (header file)
5+
* \date 2024
6+
*
7+
* Copyright: (C) 2023 CESNET, z.s.p.o.
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
#include "IpfixDecoder.hpp"
12+
13+
#include <stdexcept> // runtime_error
14+
#include <string> // string
15+
#include <errno.h> // errno, EWOULDBLOCK, EAGAIN
16+
#include <stddef.h> // size_t
17+
18+
#include <unistd.h> // read
19+
20+
#include <ipfixcol2.h> // fds_ipfix_msg_hdr, ipx_strerror
21+
22+
#include "DecodeBuffer.hpp" // DecodeBuffer
23+
#include "read_until_n.hpp" // read_until_n
24+
25+
namespace tcp_in {
26+
27+
DecodeBuffer &IpfixDecoder::decode() {
28+
while (!m_decoded.enough_data()) {
29+
// Read the header of the message.
30+
if (!read_header()) {
31+
// There is not enough data to read the whole header.
32+
break;
33+
}
34+
35+
// Read the body of the message.
36+
if (!read_body()) {
37+
// There is not enough data to read the whole body of the message.
38+
break;
39+
}
40+
}
41+
42+
if (m_decoded.is_eof_reached() && m_part_readed.size() != 0) {
43+
throw std::runtime_error("Received incomplete message.");
44+
}
45+
46+
return m_decoded;
47+
}
48+
49+
bool IpfixDecoder::read_header() {
50+
if (m_msg_size != 0) {
51+
// The header is already read, but the message body is incomplete.
52+
return true;
53+
}
54+
55+
// Read the header
56+
if (!read_until_n(sizeof(fds_ipfix_msg_hdr))) {
57+
// There is not enough data to read the whole header.
58+
return false;
59+
}
60+
61+
// The header has been read, load the size of the message.
62+
auto hdr = reinterpret_cast<fds_ipfix_msg_hdr *>(m_part_readed.data());
63+
m_msg_size = ntohs(hdr->length);
64+
return true;
65+
}
66+
67+
bool IpfixDecoder::read_body() {
68+
// Read the body.
69+
if (!read_until_n(m_msg_size)) {
70+
// There is not enough data to read the whole body
71+
return false;
72+
}
73+
74+
// Whole message has been read. Add it to the decode buffer.
75+
m_decoded.add(std::move(m_part_readed));
76+
m_part_readed = ByteVector();
77+
m_msg_size = 0;
78+
return true;
79+
}
80+
81+
bool IpfixDecoder::read_until_n(size_t n) {
82+
return ::read_until_n(n, m_fd, m_part_readed, m_decoded);
83+
}
84+
85+
} // namespace tcp_in
86+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* \file
3+
* \author Jakub Antonín Štigler <[email protected]>
4+
* \brief IPFIX decoder for tcp plugin (header file)
5+
* \date 2024
6+
*
7+
* Copyright: (C) 2023 CESNET, z.s.p.o.
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
#pragma once
12+
13+
#include <cstdint> // uint16_t
14+
#include <cstddef> // size_t
15+
16+
#include "Decoder.hpp" // Decoder
17+
#include "DecodeBuffer.hpp" // DecodeBuffer
18+
#include "ByteVector.hpp" // ByteVector
19+
20+
namespace tcp_in {
21+
22+
/** Identifies data for which this decoder should be used. */
23+
constexpr uint16_t IPFIX_MAGIC = 10;
24+
25+
/** Decoder for basic IPFX data. */
26+
class IpfixDecoder : public Decoder {
27+
public:
28+
/**
29+
* @brief Creates ipfix decoder.
30+
* @param fd TCP connection file descriptor.
31+
*/
32+
IpfixDecoder(int fd) : m_fd(fd), m_decoded(), m_part_readed(), m_msg_size(0) {}
33+
34+
virtual DecodeBuffer &decode() override;
35+
36+
virtual const char *get_name() const override {
37+
return "IPFIX";
38+
}
39+
40+
private:
41+
/** returns true if there was enough data to read the header or if the header is already read */
42+
bool read_header();
43+
/** returns true if there was enough data to read the body */
44+
bool read_body();
45+
/** returns true if there was enough data to read to the given amount */
46+
bool read_until_n(size_t n);
47+
48+
int m_fd;
49+
DecodeBuffer m_decoded;
50+
51+
ByteVector m_part_readed;
52+
/** Final size of the whole IPFIX message. When 0 IPIFX header is not fully read. */
53+
size_t m_msg_size;
54+
};
55+
56+
} // namespace tcp_in
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* \file
3+
* \author Jakub Antonín Štigler <[email protected]>
4+
* \brief Function for reading from file descriptor (header file)
5+
* \date 2024
6+
*
7+
* Copyright: (C) 2023 CESNET, z.s.p.o.
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
#pragma once
12+
13+
#include <cstddef> // size_t
14+
#include <cerrno> // errno, EWOULDBLOCK, EAGAIN
15+
#include <string> // std::string
16+
#include <stdexcept> // std::runtime_error
17+
18+
#include <sys/socket.h> // recv
19+
20+
#include <ipfixcol2.h> // ipx_strerror
21+
22+
/**
23+
* @brief Reads to vector from file descriptor until the vector has the desired number of bytes
24+
*
25+
* @tparam Vec vector like object
26+
* @tparam EofSig `EofSig::signal_eof()` is called when eof is reached
27+
* @param n number of bytes that should be in the vector
28+
* @param fd file descriptor to read from
29+
* @param result where to read the bytes to
30+
* @param eofSignaler has method to signal eof
31+
* @return true if the vector has at least `n` bytes after the call
32+
* @throws when fails to read from the file descriptor
33+
*/
34+
template<typename Vec, typename EofSig>
35+
bool read_until_n(size_t n, int fd, Vec &result, EofSig &eofSignaler) {
36+
auto filled = result.size();
37+
if (filled >= n) {
38+
return true;
39+
}
40+
41+
auto remaining = n - filled;
42+
result.resize(n);
43+
44+
int res = recv(fd, result.data() + filled, remaining, 0);
45+
if (res == -1) {
46+
result.resize(filled);
47+
int err = errno;
48+
if (err == EWOULDBLOCK || err == EAGAIN) {
49+
return false;
50+
}
51+
52+
const char *err_str;
53+
ipx_strerror(err, err_str);
54+
throw std::runtime_error("Failed to read from descriptor: " + std::string(err_str));
55+
}
56+
57+
result.resize(filled + res);
58+
59+
if (res == 0) {
60+
eofSignaler.signal_eof();
61+
}
62+
63+
if (static_cast<size_t>(res) != remaining) {
64+
return false;
65+
}
66+
67+
return true;
68+
}

0 commit comments

Comments
 (0)