From 019fcad4a05070842c99a11f3c2eaceabe9ed424 Mon Sep 17 00:00:00 2001 From: Jaroslav Pesek Date: Tue, 12 Nov 2024 19:00:15 +0100 Subject: [PATCH] ndp ctt table controller - init --- include/ipfixprobe/flowifc.hpp | 2 + include/ipfixprobe/packet.hpp | 3 +- include/ipfixprobe/storage.hpp | 7 +++ input/ndp.cpp | 5 +- input/parser.cpp | 21 +++++-- input/parser.hpp | 2 +- storage/ctt-controller.cpp | 111 +++++++++++++++++++++++++++++++++ storage/ctt-controller.hpp | 107 +++++++++++++++++++++++++++++++ 8 files changed, 250 insertions(+), 8 deletions(-) create mode 100644 storage/ctt-controller.cpp create mode 100644 storage/ctt-controller.hpp diff --git a/include/ipfixprobe/flowifc.hpp b/include/ipfixprobe/flowifc.hpp index e95db465..51964c0a 100644 --- a/include/ipfixprobe/flowifc.hpp +++ b/include/ipfixprobe/flowifc.hpp @@ -263,6 +263,8 @@ struct Flow : public Record { }; uint64_t flow_hash; + uint64_t flow_hash_ctt; /**< Flow hash for CTT. */ + bool ctt_valid; /**< CTT validity flag. */ PluginsStatus plugins_status; /**< Statuses of the process plugins for this flow, used to check if the flow process plugins requires all available data, only metadata or nothing of this. */ diff --git a/include/ipfixprobe/packet.hpp b/include/ipfixprobe/packet.hpp index 8ac75f3b..bd1e5aba 100644 --- a/include/ipfixprobe/packet.hpp +++ b/include/ipfixprobe/packet.hpp @@ -47,6 +47,7 @@ namespace ipxp { */ struct Packet : public Record { Metadata_CTT cttmeta; /**< Metadata from CTT */ + bool cttmeta_valid; /**< True if CTT metadata is valid */ struct timeval ts; uint8_t dst_mac[6]; @@ -108,7 +109,7 @@ struct Packet : public Record { * \brief Constructor. */ Packet() : - ts({0}), + cttmeta_valid(false), ts({0}), dst_mac(), src_mac(), ethertype(0), ip_len(0), ip_payload_len(0), ip_version(0), ip_ttl(0), ip_proto(0), ip_tos(0), ip_flags(0), src_ip({0}), dst_ip({0}), vlan_id(0), diff --git a/include/ipfixprobe/storage.hpp b/include/ipfixprobe/storage.hpp index 8a58f57e..d9573bbb 100644 --- a/include/ipfixprobe/storage.hpp +++ b/include/ipfixprobe/storage.hpp @@ -189,6 +189,13 @@ class StoragePlugin : public Plugin */ int plugins_post_create(Flow& rec, const Packet& pkt) { + // if metadata are valid, add flow hash ctt to the flow record + if (pkt.cttmeta_valid) { + rec.ctt_valid = true; + rec.flow_hash_ctt = pkt.cttmeta.flow_hash; + } else { + rec.ctt_valid = false; + } PluginStatusConverter plugin_status_converter(m_plugins_status); int ret = 0; for (unsigned int i = 0; i < m_plugin_cnt; i++) { diff --git a/input/ndp.cpp b/input/ndp.cpp index 8c72e0c3..398ccd34 100644 --- a/input/ndp.cpp +++ b/input/ndp.cpp @@ -170,7 +170,10 @@ InputPlugin::Result NdpPacketReader::get(PacketBlock &packets) m_stats.bad_metadata++; parse_packet(&opt, m_parser_stats, timestamp, ndp_packet->data, ndp_packet->data_length, ndp_packet->data_length); } else { - parse_packet_ctt_metadata(&opt, m_parser_stats, ctt, ndp_packet->data, ndp_packet->data_length, ndp_packet->data_length); + if (parse_packet_ctt_metadata(&opt, m_parser_stats, ctt, ndp_packet->data, ndp_packet->data_length, ndp_packet->data_length) == -1) { + m_stats.bad_metadata++; + parse_packet(&opt, m_parser_stats, timestamp, ndp_packet->data, ndp_packet->data_length, ndp_packet->data_length); + } } } else { parse_packet(&opt, m_parser_stats, timestamp, ndp_packet->data, ndp_packet->data_length, ndp_packet->data_length); diff --git a/input/parser.cpp b/input/parser.cpp index 6b914683..ec5a2b5b 100644 --- a/input/parser.cpp +++ b/input/parser.cpp @@ -35,6 +35,7 @@ #include "parser.hpp" #include "headers.hpp" +#include #include namespace ipxp { @@ -776,12 +777,21 @@ void parse_packet(parser_opt_t *opt, ParserStats& stats, struct timeval ts, cons opt->pblock->bytes += len; } -void parse_packet_ctt_metadata(parser_opt_t *opt, ParserStats& stats, const Metadata_CTT& metadata, const uint8_t *data, uint16_t len, uint16_t caplen) +int parse_packet_ctt_metadata(parser_opt_t *opt, ParserStats& stats, const Metadata_CTT& metadata, const uint8_t *data, uint16_t len, uint16_t caplen) { if (opt->pblock->cnt >= opt->pblock->size) { - return; + return 0; } Packet *pkt = &opt->pblock->pkts[opt->pblock->cnt]; + + // check metadata validity + if (metadata.parser_status == PA_OK) { + pkt->cttmeta_valid = true; + } else { + pkt->cttmeta_valid = false; + return -1; + } + pkt->cttmeta = metadata; pkt->packet_len_wire = len; @@ -831,7 +841,7 @@ void parse_packet_ctt_metadata(parser_opt_t *opt, ParserStats& stats, const Meta stats.pppoe_packets++; } else { // if not previous, we try delegate to original parser parse_packet(opt, stats, metadata.ts, data, len, caplen); - return; + return 0; } // L4 @@ -843,11 +853,11 @@ void parse_packet_ctt_metadata(parser_opt_t *opt, ParserStats& stats, const Meta stats.udp_packets++; } else { // if not previous, we try delegate to original parser parse_packet(opt, stats, metadata.ts, data, len, caplen); - return; + return 0; } } catch (const char *err) { DEBUG_MSG("%s\n", err); - return; + return 0; } if (pkt->vlan_id) { @@ -880,6 +890,7 @@ void parse_packet_ctt_metadata(parser_opt_t *opt, ParserStats& stats, const Meta opt->packet_valid = true; opt->pblock->cnt++; opt->pblock->bytes += len; + return 0; } } diff --git a/input/parser.hpp b/input/parser.hpp index 580e6703..d9a3b7f4 100644 --- a/input/parser.hpp +++ b/input/parser.hpp @@ -86,7 +86,7 @@ typedef struct parser_opt_s { */ void parse_packet(parser_opt_t *opt, ParserStats& stats, struct timeval ts, const uint8_t *data, uint16_t len, uint16_t caplen); -void parse_packet_ctt_metadata(parser_opt_t *opt, ParserStats& stats, const Metadata_CTT& metadata, const uint8_t *data, uint16_t len, uint16_t caplen); +int parse_packet_ctt_metadata(parser_opt_t *opt, ParserStats& stats, const Metadata_CTT& metadata, const uint8_t *data, uint16_t len, uint16_t caplen); } #endif /* IPXP_INPUT_PARSER_HPP */ diff --git a/storage/ctt-controller.cpp b/storage/ctt-controller.cpp new file mode 100644 index 00000000..649efeb2 --- /dev/null +++ b/storage/ctt-controller.cpp @@ -0,0 +1,111 @@ +/** + * \file ctt-controller.cpp + * \brief Connection Tracking Table (CTT) controller + * \author Jaroslav Pesek + * \date 2024 + */ +/* + * Copyright (C) 2024 CESNET + * + * LICENSE TERMS + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * + * + */ + +#include "ctt-controller.hpp" +#include +#include + +namespace ipxp { + +CttController::CttController(const std::string& nfb_dev, unsigned ctt_comp_index) + : m_commander(ctt::NfbParams{nfb_dev, ctt_comp_index}) +{ + try { + // Get UserInfo to determine key, state, and state_mask sizes + ctt::UserInfo user_info = m_commander.get_user_info(); + key_size_bytes = (user_info.key_bit_width + 7) / 8; + state_size_bytes = (user_info.state_bit_width + 7) / 8; + state_mask_size_bytes = (user_info.state_mask_bit_width + 7) / 8; + + // Enable the CTT + std::future enable_future = m_commander.enable(true); + enable_future.wait(); + } + catch (const std::exception& e) { + throw; + } +} + +void CttController::create_record(uint64_t flow_hash_ctt, const struct timeval& ts) +{ + try { + std::vector key = assemble_key(flow_hash_ctt); + std::vector state = assemble_state( + OffloadMode::PACKET_OFFLOAD_WITH_EXPORT, + MetaType::FULL, + ts); + + m_commander.write_record(std::move(key), std::move(state)); + } + catch (const std::exception& e) { + throw; + } +} + +void CttController::export_record(uint64_t flow_hash_ctt) +{ + try { + std::vector key = assemble_key(flow_hash_ctt); + + m_commander.export_record(std::move(key)); + } + catch (const std::exception& e) { + throw; + } +} + +std::vector CttController::assemble_key(uint64_t flow_hash_ctt) +{ + std::vector key(key_size_bytes, std::byte(0)); + for (size_t i = 0; i < sizeof(flow_hash_ctt) && i < key_size_bytes; ++i) { + key[i] = static_cast((flow_hash_ctt >> (8 * i)) & 0xFF); + } + return key; + +} + +std::vector CttController::assemble_state( + OffloadMode offload_mode, MetaType meta_type, const struct timeval& ts) +{ + std::vector state(state_size_bytes, std::byte(0)); + std::vector state_mask(state_mask_size_bytes, std::byte(0)); + + state[0] = static_cast(offload_mode); + state[1] = static_cast(meta_type); + + // timestamp in sec/ns format, 32+32 bits - 64 bits in total + for (size_t i = 0; i < sizeof(ts.tv_sec) && i < 4; ++i) { + state[2 + i] = static_cast((ts.tv_sec >> (8 * i)) & 0xFF); + } + for (size_t i = 0; i < sizeof(ts.tv_usec) && i < 4; ++i) { + state[6 + i] = static_cast((ts.tv_usec >> (8 * i)) & 0xFF); + } + return state; +} + +} // namespace ipxp \ No newline at end of file diff --git a/storage/ctt-controller.hpp b/storage/ctt-controller.hpp new file mode 100644 index 00000000..f5da0af0 --- /dev/null +++ b/storage/ctt-controller.hpp @@ -0,0 +1,107 @@ +/** + * \file ctt-controller.hpp + * \brief Connection Tracking Table (CTT) controller + * \author Jaroslav Pesek + * \date 2024 + */ +/* + * Copyright (C) 2024 CESNET + * + * LICENSE TERMS + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * + * + */ + +#ifndef IPXP_CTT_CONTROLLER_HPP +#define IPXP_CTT_CONTROLLER_HPP + +#include + +#include +#include +#include +#include + +#include + +namespace ipxp { + +class CttController { +public: + enum class OffloadMode : uint8_t { + NO_OFFLOAD = 0x0, + PACKET_OFFLOAD = 0x1, + META_EXPORT = 0x2, + PACKET_OFFLOAD_WITH_EXPORT = 0x3 + }; + enum class MetaType : uint8_t { + FULL = 0x0, + HALF = 0x1, + TS_ONLY = 0x2, + NO_META = 0x3 + }; + /** + * @brief Constructor that initializes the CTT. + * + * @param nfb_dev The NFB device file (e.g., "/dev/nfb0"). + * @param ctt_comp_index The index of the CTT component. + */ + CttController(const std::string& nfb_dev, unsigned ctt_comp_index); + + /** + * @brief Command: mark a flow for offload. + * + * @param flow_hash_ctt The flow hash to be offloaded. + */ + void create_record(uint64_t flow_hash_ctt, const struct timeval& timestamp_first); + + /** + * @brief Command: export a flow from the CTT. + * + * @param flow_hash_ctt The flow hash to be exported. + */ + void export_record(uint64_t flow_hash_ctt); + +private: + ctt::AsyncCommander m_commander; + size_t key_size_bytes; + size_t state_size_bytes; + size_t state_mask_size_bytes; + + /** + * @brief Assembles the state vector from the given values. + * + * @param offload_mode The offload mode. + * @param meta_type The metadata type. + * @param timestamp_first The first timestamp of the flow. + * @return A byte vector representing the assembled state vector. + */ + std::vector assemble_state( + OffloadMode offload_mode, MetaType meta_type, + const struct timeval& timestamp_first); + + /** + * @brief Assembles the key vector from the given flow hash. + * + * @param flow_hash_ctt The flow hash. + * @return A byte vector representing the assembled key vector. + */ + std::vector assemble_key(uint64_t flow_hash_ctt); +}; +} // namespace ipxp + +#endif /* IPXP_CTT_CONTROLLER_HPP */