Skip to content

Commit 96dee23

Browse files
authored
Merge pull request #276 from CESNET/top-ports
Top 10 ports
2 parents 7ed39f6 + 69f88e5 commit 96dee23

File tree

9 files changed

+206
-5
lines changed

9 files changed

+206
-5
lines changed

include/ipfixprobe/inputPlugin.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class IPXP_API InputPlugin
9696
};
9797

9898
/// Statistics related to packet parsing.
99-
ParserStats m_parser_stats = {};
99+
ParserStats m_parser_stats {10};
100100

101101
private:
102102
void create_parser_stats_telemetry(

include/ipfixprobe/parser-stats.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
#pragma once
2727

28+
#include "../../src/plugins/input/parser/topPorts.hpp"
29+
2830
#include <array>
2931
#include <cstdint>
3032
#include <string>
@@ -162,6 +164,23 @@ struct VlanStats {
162164
* \brief Structure for storing parser statistics.
163165
*/
164166
struct ParserStats {
167+
ParserStats(size_t top_ports_count)
168+
: top_ports(top_ports_count)
169+
, mpls_packets(0)
170+
, vlan_packets(0)
171+
, pppoe_packets(0)
172+
, trill_packets(0)
173+
, ipv4_packets(0)
174+
, ipv6_packets(0)
175+
, tcp_packets(0)
176+
, udp_packets(0)
177+
, seen_packets(0)
178+
, unknown_packets(0)
179+
{
180+
}
181+
182+
TopPorts top_ports;
183+
165184
uint64_t mpls_packets;
166185
uint64_t vlan_packets;
167186
uint64_t pppoe_packets;

src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ target_link_libraries(ipfixprobe-core
3232
atomic::atomic
3333
unwind::unwind
3434
${CMAKE_DL_LIBS}
35+
top-ports
3536
)
3637

3738
add_executable(ipfixprobe main.cpp)

src/core/inputPlugin.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* SPDX-License-Identifier: BSD-3-Clause
1414
*/
1515

16+
#include <numeric>
17+
1618
#include <ipfixprobe/inputPlugin.hpp>
1719

1820
namespace ipxp {
@@ -35,6 +37,19 @@ static telemetry::Content get_parser_stats_content(const ParserStats& parserStat
3537

3638
dict["seen_packets"] = parserStats.seen_packets;
3739
dict["unknown_packets"] = parserStats.unknown_packets;
40+
const std::vector<TopPorts::PortStats>& ports = parserStats.top_ports.get_top_ports();
41+
if (ports.empty()) {
42+
dict["top_10_ports"] = "";
43+
} else {
44+
std::string top_ports = ports[0].to_string();
45+
dict["top_10_ports"] = std::accumulate(
46+
ports.begin() + 1,
47+
ports.end(),
48+
top_ports,
49+
[](std::string acc, const TopPorts::PortStats& port_frequency) {
50+
return acc + ", " + port_frequency.to_string();
51+
});
52+
}
3853
return dict;
3954
}
4055

src/plugins/input/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_subdirectory(raw)
2+
add_subdirectory(parser)
23

34
if (ENABLE_INPUT_PCAP)
45
add_subdirectory(pcap)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
add_library(top-ports STATIC
2+
topPorts.cpp
3+
topPorts.hpp
4+
)
5+
6+
target_include_directories(top-ports PUBLIC
7+
${CMAKE_SOURCE_DIR}/include
8+
${CMAKE_BINARY_DIR}/src
9+
)
10+
11+
target_compile_options(top-ports PRIVATE -fPIC)

src/plugins/input/parser/parser.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ inline uint16_t parse_ipv6_hdr(const u_char* data_ptr, uint16_t data_len, Packet
465465
* \param [out] pkt Pointer to Packet structure where parsed fields will be stored.
466466
* \return Size of header in bytes.
467467
*/
468-
inline uint16_t parse_tcp_hdr(const u_char* data_ptr, uint16_t data_len, Packet* pkt)
468+
inline uint16_t
469+
parse_tcp_hdr(const u_char* data_ptr, uint16_t data_len, Packet* pkt, ParserStats& stats)
469470
{
470471
struct tcphdr* tcp = (struct tcphdr*) data_ptr;
471472
if (sizeof(struct tcphdr) > data_len) {
@@ -479,6 +480,9 @@ inline uint16_t parse_tcp_hdr(const u_char* data_ptr, uint16_t data_len, Packet*
479480
pkt->tcp_flags = (uint8_t) *(data_ptr + 13) & 0xFF;
480481
pkt->tcp_window = ntohs(tcp->window);
481482

483+
stats.top_ports.increment_tcp_frequency(pkt->src_port);
484+
stats.top_ports.increment_tcp_frequency(pkt->dst_port);
485+
482486
DEBUG_MSG("TCP header:\n");
483487
DEBUG_MSG("\tSrc port:\t%u\n", ntohs(tcp->source));
484488
DEBUG_MSG("\tDest port:\t%u\n", ntohs(tcp->dest));
@@ -544,7 +548,8 @@ inline uint16_t parse_tcp_hdr(const u_char* data_ptr, uint16_t data_len, Packet*
544548
* \param [out] pkt Pointer to Packet structure where parsed fields will be stored.
545549
* \return Size of header in bytes.
546550
*/
547-
inline uint16_t parse_udp_hdr(const u_char* data_ptr, uint16_t data_len, Packet* pkt)
551+
inline uint16_t
552+
parse_udp_hdr(const u_char* data_ptr, uint16_t data_len, Packet* pkt, ParserStats& stats)
548553
{
549554
struct udphdr* udp = (struct udphdr*) data_ptr;
550555
if (sizeof(struct udphdr) > data_len) {
@@ -554,6 +559,9 @@ inline uint16_t parse_udp_hdr(const u_char* data_ptr, uint16_t data_len, Packet*
554559
pkt->src_port = ntohs(udp->source);
555560
pkt->dst_port = ntohs(udp->dest);
556561

562+
stats.top_ports.increment_udp_frequency(pkt->src_port);
563+
stats.top_ports.increment_udp_frequency(pkt->dst_port);
564+
557565
DEBUG_MSG("UDP header:\n");
558566
DEBUG_MSG("\tSrc port:\t%u\n", ntohs(udp->source));
559567
DEBUG_MSG("\tDest port:\t%u\n", ntohs(udp->dest));
@@ -749,10 +757,10 @@ void parse_packet(
749757

750758
l4_hdr_offset = data_offset;
751759
if (pkt->ip_proto == IPPROTO_TCP) {
752-
data_offset += parse_tcp_hdr(data + data_offset, caplen - data_offset, pkt);
760+
data_offset += parse_tcp_hdr(data + data_offset, caplen - data_offset, pkt, stats);
753761
stats.tcp_packets++;
754762
} else if (pkt->ip_proto == IPPROTO_UDP) {
755-
data_offset += parse_udp_hdr(data + data_offset, caplen - data_offset, pkt);
763+
data_offset += parse_udp_hdr(data + data_offset, caplen - data_offset, pkt, stats);
756764
stats.udp_packets++;
757765
}
758766
} catch (const char* err) {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* \file topPorts.cpp
3+
* \brief TopPorts class implementation.
4+
* \author Damir Zainullin <[email protected]>
5+
* \date 2024
6+
*/
7+
8+
#include "topPorts.hpp"
9+
10+
#include <algorithm>
11+
#include <array>
12+
#include <cstdint>
13+
#include <functional>
14+
#include <limits>
15+
#include <string>
16+
#include <vector>
17+
18+
namespace ipxp {
19+
20+
TopPorts::TopPorts(size_t top_ports_count) noexcept
21+
: m_top_ports_count(top_ports_count)
22+
{
23+
}
24+
25+
std::string TopPorts::PortStats::to_string() const noexcept
26+
{
27+
return std::to_string(port) + "[" + (protocol == Protocol::TCP ? "TCP" : "UDP") + "] - "
28+
+ std::to_string(frequency);
29+
}
30+
31+
bool update_port_buffer(
32+
std::vector<TopPorts::PortStats>& port_buffer,
33+
TopPorts::PortStats port_stats) noexcept
34+
{
35+
auto port_pos = std::lower_bound(
36+
port_buffer.begin(),
37+
port_buffer.end(),
38+
port_stats.frequency,
39+
[](const TopPorts::PortStats& port_frequency, size_t count) {
40+
return port_frequency.frequency >= count;
41+
});
42+
43+
if (port_pos != port_buffer.end()) {
44+
std::copy_backward(port_pos, std::prev(port_buffer.end()), port_buffer.end());
45+
*port_pos = port_stats;
46+
return true;
47+
}
48+
return false;
49+
};
50+
51+
std::vector<TopPorts::PortStats> TopPorts::get_top_ports() const noexcept
52+
{
53+
std::vector<PortStats> port_buffer(m_top_ports_count);
54+
size_t ports_inserted = 0;
55+
56+
std::for_each(
57+
m_tcp_port_frequencies.begin(),
58+
m_tcp_port_frequencies.end(),
59+
[&, port = uint16_t {0}](size_t frequency) mutable {
60+
ports_inserted
61+
+= update_port_buffer(port_buffer, {port++, frequency, PortStats::Protocol::TCP});
62+
});
63+
std::for_each(
64+
m_udp_port_frequencies.begin(),
65+
m_udp_port_frequencies.end(),
66+
[&, port = uint16_t {0}](size_t frequency) mutable {
67+
ports_inserted
68+
+= update_port_buffer(port_buffer, {port++, frequency, PortStats::Protocol::UDP});
69+
});
70+
71+
port_buffer.resize(std::min(m_top_ports_count, ports_inserted));
72+
return port_buffer;
73+
}
74+
75+
} // namespace ipxp
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* \file topPorts.hpp
3+
* \brief TopPorts class declaration implementing the most popular ports.
4+
* \author Damir Zainullin <[email protected]>
5+
* \date 2024
6+
*/
7+
#pragma once
8+
9+
#include <array>
10+
#include <cstdint>
11+
#include <limits>
12+
#include <string>
13+
#include <vector>
14+
15+
namespace ipxp {
16+
/**
17+
* \brief Top ports counter.
18+
*/
19+
class TopPorts {
20+
public:
21+
/**
22+
* \brief Constructor.
23+
* \param top_ports_count Number of the most popular ports to track.
24+
*/
25+
TopPorts(size_t top_ports_count) noexcept;
26+
27+
/**
28+
* \brief Increments number of times given tcp port has been seen.
29+
* \param port Port to increment its frequency.
30+
*/
31+
void increment_tcp_frequency(uint16_t port) noexcept { m_tcp_port_frequencies[port]++; }
32+
33+
/**
34+
* \brief Increments number of times given udp port has been seen.
35+
* \param port Port to increment its frequency.
36+
*/
37+
void increment_udp_frequency(uint16_t port) noexcept { m_udp_port_frequencies[port]++; }
38+
39+
/**
40+
* \brief Port frequency and protocol to which it belongs.
41+
*/
42+
struct PortStats {
43+
/**
44+
* \brief Protocol type.
45+
*/
46+
enum class Protocol { TCP, UDP };
47+
48+
uint16_t port; /**< Port number. */
49+
size_t frequency; /**< Number of times the port has been seen. */
50+
Protocol protocol; /**< Protocol to which the port belongs. */
51+
52+
/**
53+
* \brief Convert the port stats to string.
54+
* \return String representation of the port stats.
55+
*/
56+
std::string to_string() const noexcept;
57+
};
58+
59+
/**
60+
* \brief Get the top ports.
61+
* \return Vector of the most popular ports.
62+
*/
63+
std::vector<TopPorts::PortStats> get_top_ports() const noexcept;
64+
65+
private:
66+
std::array<std::size_t, std::numeric_limits<uint16_t>::max() + 1> m_tcp_port_frequencies {};
67+
std::array<std::size_t, std::numeric_limits<uint16_t>::max() + 1> m_udp_port_frequencies {};
68+
const size_t m_top_ports_count;
69+
};
70+
71+
} // namespace ipxp

0 commit comments

Comments
 (0)