Uploading attachment code

This commit is contained in:
noam
2022-06-29 21:50:10 +03:00
parent feefcbcd6b
commit 7e8e141c53
133 changed files with 40692 additions and 62 deletions

2
core/CMakeLists.txt Normal file
View File

@@ -0,0 +1,2 @@
add_subdirectory(shmem_ipc)
add_subdirectory(compression)

View File

@@ -0,0 +1,5 @@
add_definitions(-DZLIB_CONST)
add_library(osrc_compression_utils SHARED compression_utils.cc)
install(TARGETS osrc_compression_utils DESTINATION lib)

View File

@@ -0,0 +1,383 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "compression_utils.h"
#include <iostream>
#include <sstream>
#include <array>
#include <vector>
#include <tuple>
#include <strings.h>
#include <string.h>
#include <zlib.h>
using namespace std;
using DebugFunction = void(*)(const char *);
static const int max_debug_level = static_cast<int>(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION);
static void
defaultPrint(const char *debug_message)
{
cerr << debug_message;
};
class ZlibDebugStream
{
public:
ZlibDebugStream(const CompressionUtilsDebugLevel _debug_level) : debug_level(_debug_level) {}
~ZlibDebugStream()
{
ZlibDebugStream::debug_funcs[debug_level](debug_message.str().c_str());
if (debug_level == CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION) abort();
}
static void
resetDebugFunctions()
{
for (auto &func : debug_funcs) {
func = defaultPrint;
}
}
static void
setDebugFunction(const CompressionUtilsDebugLevel debug_level, DebugFunction function)
{
if (static_cast<int>(debug_level) > max_debug_level) return;
debug_funcs[static_cast<int>(debug_level)] = function;
}
template <typename T>
ZlibDebugStream & operator<<(const T &message) { debug_message << message; return *this; }
private:
ostringstream debug_message;
CompressionUtilsDebugLevel debug_level;
static array<DebugFunction, max_debug_level + 1> debug_funcs;
};
array<DebugFunction, max_debug_level + 1> ZlibDebugStream::debug_funcs = {
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_TRACE
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_DEBUG
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_INFO
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_WARNING
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ERROR
defaultPrint // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION
};
#define zlibDbgError ZlibDebugStream(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ERROR)
#define zlibDbgAssertion ZlibDebugStream(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION)
static const int default_num_window_bits = 15; // Default used by zlib.
static const int default_compression_level = Z_DEFAULT_COMPRESSION;
static const int default_compression_method = Z_DEFLATED;
static const int default_mem_level = 8; // Default recommended in zlib documentation.
static const int default_strategy = Z_DEFAULT_STRATEGY;
static const int zlib_ok_return_value = Z_OK;
static const int zlib_stream_done_return_value = Z_STREAM_END;
static const int zlib_bad_stream_state_error = Z_STREAM_ERROR;
static const int zlib_invalid_data_error = Z_DATA_ERROR;
static const int zlib_out_of_memory_error = Z_MEM_ERROR;
static const int zlib_version_mismatch_error = Z_VERSION_ERROR;
static const int zlib_buf_error = Z_BUF_ERROR;
static const int zlib_finish_flush = Z_FINISH;
static const int zlib_sync_flush = Z_SYNC_FLUSH;
static const int zlib_no_flush = Z_NO_FLUSH;
struct CompressionStream
{
CompressionStream() { bzero(&stream, sizeof(z_stream)); }
tuple<basic_string<unsigned char>, bool>
decompress(const unsigned char *data, uint32_t size)
{
initInflate();
if (state != TYPE::DECOMPRESS) throw runtime_error("Could not start decompression");
stream.avail_in = size;
stream.next_in = data;
vector<unsigned char> work_space;
work_space.reserve(4096);
basic_string<unsigned char> res;
int retries = 0;
while (stream.avail_in != 0) {
stream.avail_out = work_space.capacity();
stream.next_out = work_space.data();
auto old_total_out = stream.total_out;
auto inflate_res = inflate(&stream, zlib_no_flush);
if (inflate_res != Z_OK && inflate_res != Z_STREAM_END) {
fini();
throw runtime_error("error in 'inflate': " + getZlibError(inflate_res));
}
if (stream.total_out != old_total_out) {
res.append(work_space.data(), stream.total_out - old_total_out);
} else {
++retries;
if (retries > 3) {
fini();
throw runtime_error("No results from inflate more than three times");
}
}
if (inflate_res == Z_STREAM_END) {
fini();
return make_tuple(res, true);
}
}
return make_tuple(res, false);
}
basic_string<unsigned char>
compress(CompressionType type, const unsigned char *data, uint32_t size, int is_last_chunk)
{
initDeflate(type);
if (state != TYPE::COMPRESS) throw runtime_error("Could not start compression");
stream.avail_in = size;
stream.next_in = data;
vector<unsigned char> work_space;
work_space.reserve(deflateBound(&stream, stream.avail_in));
basic_string<unsigned char> res;
int retries = 0;
while (stream.avail_in != 0) {
stream.avail_out = work_space.capacity();
stream.next_out = work_space.data();
auto old_total_out = stream.total_out;
int deflate_res = deflate(&stream, is_last_chunk ? zlib_finish_flush : zlib_sync_flush);
if (deflate_res != Z_OK && deflate_res != Z_STREAM_END) {
fini();
throw runtime_error("error in 'deflate': " + getZlibError(deflate_res));
}
if (stream.total_out != old_total_out) {
res.append(work_space.data(), stream.total_out - old_total_out);
} else {
++retries;
if (retries > 3) {
fini();
throw runtime_error("No results from deflate more than three times");
}
}
if (deflate_res == Z_STREAM_END) {
fini();
return res;
}
}
return res;
}
private:
void
initInflate()
{
if (state != TYPE::UNINITIALIZAED) return;
auto init_status = inflateInit2(&stream, default_num_window_bits + 32);
if (init_status != zlib_ok_return_value) {
throw runtime_error(
"Failed to initialize decompression stream. Error: " + getZlibError(init_status)
);
}
state = TYPE::DECOMPRESS;
}
void
initDeflate(CompressionType type)
{
if (state != TYPE::UNINITIALIZAED) return;
int num_history_window_bits;
switch (type) {
case CompressionType::GZIP: {
num_history_window_bits = default_num_window_bits + 16;
break;
}
case CompressionType::ZLIB: {
num_history_window_bits = default_num_window_bits;
break;
}
default: {
zlibDbgAssertion
<< "Invalid compression type value: "
<< static_cast<int>(type);
return;
}
}
int init_status = deflateInit2(
&stream,
default_compression_level,
default_compression_method,
num_history_window_bits,
default_mem_level,
default_strategy
);
if (init_status != zlib_ok_return_value) {
throw runtime_error(
"Failed to initialize compression stream. Error: " + getZlibError(init_status)
);
}
state = TYPE::COMPRESS;
}
void
fini()
{
int end_stream_res = zlib_ok_return_value;
if (state == TYPE::DECOMPRESS) end_stream_res = inflateEnd(&stream);
if (state == TYPE::COMPRESS) end_stream_res = deflateEnd(&stream);
if (end_stream_res != zlib_ok_return_value) {
zlibDbgError << "Failed to clean state: " << getZlibError(end_stream_res);
}
state = TYPE::UNINITIALIZAED;
}
string
getZlibError(int zlibErrorCode)
{
switch (zlibErrorCode) {
case zlib_buf_error:
return "No progress was possible (possibly no more input data or not enough output buffer space)";
case zlib_bad_stream_state_error:
return "Inconsistent compression stream state";
case zlib_invalid_data_error:
return "Invalid or corrupted stream data";
case zlib_out_of_memory_error:
return "Out of memory";
case zlib_version_mismatch_error:
return "zlib version mismatch";
default:
return "zlib error occurred. Error code: " + to_string(zlibErrorCode);
}
}
z_stream stream;
enum class TYPE { UNINITIALIZAED, COMPRESS, DECOMPRESS } state = TYPE::UNINITIALIZAED;
};
void
resetCompressionDebugFunctionsToStandardError()
{
ZlibDebugStream::resetDebugFunctions();
}
void
setCompressionDebugFunction(const CompressionUtilsDebugLevel debug_level, void (*debug_function)(const char *))
{
ZlibDebugStream::setDebugFunction(debug_level, debug_function);
}
CompressionStream *
initCompressionStream()
{
return new CompressionStream();
}
void
finiCompressionStream(CompressionStream *compression_stream)
{
delete compression_stream;
}
static unsigned char *
duplicateMemory(const basic_string<unsigned char> &str)
{
auto res = static_cast<unsigned char *>(malloc(str.size()));
if (res == nullptr) throw bad_alloc();
memcpy(res, str.data(), str.size());
return res;
}
CompressionResult
compressData(
CompressionStream *compression_stream,
const CompressionType compression_type,
const uint32_t data_size,
const unsigned char *uncompressed_data,
const int is_last_chunk
)
{
CompressionResult result;
try {
if (compression_stream == nullptr) throw invalid_argument("Compression stream is NULL");
if (uncompressed_data == nullptr) throw invalid_argument("Data pointer is NULL");
auto compress = compression_stream->compress(compression_type, uncompressed_data, data_size, is_last_chunk);
result.output = duplicateMemory(compress);
result.num_output_bytes = compress.size();
result.ok = 1;
} catch (const exception &e) {
zlibDbgError << "Compression failed " << e.what();
result.ok = 0;
}
return result;
}
DecompressionResult
decompressData(
CompressionStream *compression_stream,
const uint32_t compressed_data_size,
const unsigned char *compressed_data
)
{
DecompressionResult result;
try {
if (compression_stream == nullptr) throw invalid_argument("Compression stream is NULL");
if (compressed_data == nullptr) throw invalid_argument("Data pointer is NULL");
if (compressed_data_size == 0) throw invalid_argument("Data size is 0");
auto decompress = compression_stream->decompress(compressed_data, compressed_data_size);
result.output = duplicateMemory(get<0>(decompress));
result.num_output_bytes = get<0>(decompress).size();
result.is_last_chunk = get<1>(decompress);
result.ok = 1;
} catch (const exception &e) {
zlibDbgError << "Decompression failed " << e.what();
result.ok = 0;
}
return result;
}

View File

@@ -0,0 +1,31 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ATTACHMENT_TYPES_H__
#define __ATTACHMENT_TYPES_H__
#ifdef __cplusplus
enum class AttachmentType
#else // __cplusplus
enum AttachmentType
#endif
{
NGINX_ATT_ID,
PRELOAD_ATT_ID,
#ifdef __cplusplus
COUNT
#endif
};
#endif // __ATTACHMENT_TYPES_H__

View File

@@ -0,0 +1,86 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __COMPRESSION_UTILS_H__
#define __COMPRESSION_UTILS_H__
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
typedef enum CompressionUtilsDebugLevel
{
COMPRESSION_DBG_LEVEL_TRACE,
COMPRESSION_DBG_LEVEL_DEBUG,
COMPRESSION_DBG_LEVEL_INFO,
COMPRESSION_DBG_LEVEL_WARNING,
COMPRESSION_DBG_LEVEL_ERROR,
COMPRESSION_DBG_LEVEL_ASSERTION
} CompressionUtilsDebugLevel;
void resetCompressionDebugFunctionsToStandardError();
void setCompressionDebugFunction(const CompressionUtilsDebugLevel debug_level, void (*debug_function)(const char *));
typedef struct CompressionStream CompressionStream;
CompressionStream * initCompressionStream();
void finiCompressionStream(CompressionStream *compression_stream);
typedef enum CompressionType
{
NO_COMPRESSION,
GZIP,
ZLIB
} CompressionType;
typedef struct CompressionResult
{
int ok;
uint32_t num_output_bytes;
unsigned char *output;
} CompressionResult;
CompressionResult
compressData(
CompressionStream *compression_stream,
const CompressionType compression_type,
const uint32_t uncompressed_data_size,
const unsigned char *uncompressed_data,
const int is_last_chunk
);
typedef struct DecompressionResult
{
int ok;
uint32_t num_output_bytes;
unsigned char *output;
int is_last_chunk;
} DecompressionResult;
DecompressionResult
decompressData(
CompressionStream *compression_stream,
const uint32_t compressed_data_size,
const unsigned char *compressed_data
);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // __COMPRESSION_UTILS_H__

View File

@@ -0,0 +1,68 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __HTTP_CONFIGURATION_H__
#define __HTTP_CONFIGURATION_H__
#include <string>
#include <map>
#include <vector>
#include "cereal/archives/json.hpp"
struct DebugConfig
{
void save(cereal::JSONOutputArchive &archive) const;
void load(cereal::JSONInputArchive &archive);
bool operator==(const DebugConfig &another) const;
std::string client;
std::string server;
uint port = 0;
std::string method;
std::string host;
std::string uri;
};
class HttpAttachmentConfiguration
{
public:
int init(const std::string &conf_file);
void save(cereal::JSONOutputArchive &archive) const;
void load(cereal::JSONInputArchive &archive);
bool operator==(const HttpAttachmentConfiguration &other) const;
uint getNumericalValue(const std::string &key) const;
const std::string & getStringValue(const std::string &key) const;
const std::vector<std::string> & getExcludeSources() const { return exclude_sources; }
const DebugConfig & getDebugContext() const { return dbg; }
void setNumericalValue(const std::string &key, uint value) { numerical_values[key] = value; }
void setStringValue(const std::string &key, const std::string &value) { string_values[key] = value; }
void setExcludeSources(const std::vector<std::string> &new_sources) { exclude_sources = new_sources; }
void setDebugContext(const DebugConfig &_dbg) { dbg = _dbg; }
private:
void loadNumericalValue(cereal::JSONInputArchive &archive, const std::string &name, uint default_value);
DebugConfig dbg;
std::map<std::string, uint> numerical_values;
std::map<std::string, std::string> string_values;
std::vector<std::string> exclude_sources;
std::string empty;
};
#endif // __HTTP_CONFIGURATION_H__

View File

@@ -0,0 +1,275 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __NGINX_ATTACHMENT_COMMON_H__
#define __NGINX_ATTACHMENT_COMMON_H__
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#define MAX_NGINX_UID_LEN 32
#define NUM_OF_NGINX_IPC_ELEMENTS 200
#define DEFAULT_KEEP_ALIVE_INTERVAL_MSEC 300000
#define SHARED_MEM_PATH "/dev/shm/"
#define SHARED_REGISTRATION_SIGNAL_PATH SHARED_MEM_PATH "check-point/cp-nano-attachment-registration"
#define SHARED_KEEP_ALIVE_PATH SHARED_MEM_PATH "check-point/cp-nano-attachment-registration-expiration-socket"
#define SHARED_VERDICT_SIGNAL_PATH SHARED_MEM_PATH "check-point/cp-nano-http-transaction-handler"
#define SHARED_ATTACHMENT_CONF_PATH SHARED_MEM_PATH "cp_nano_http_attachment_conf"
#define DEFAULT_STATIC_RESOURCES_PATH SHARED_MEM_PATH "static_resources"
#define INJECT_POS_IRRELEVANT -1
#define CORRUPTED_SESSION_ID 0
#define METRIC_PERIODIC_TIMEOUT 600
extern char shared_verdict_signal_path[];
extern int workers_amount_to_send;
typedef int64_t ngx_http_cp_inject_pos_t;
#ifdef __cplusplus
typedef enum class ngx_http_modification_type
#else
typedef enum ngx_http_modification_type
#endif
{
APPEND,
INJECT,
REPLACE
} ngx_http_modification_type_e;
#ifdef __cplusplus
typedef enum class ngx_http_chunk_type
#else
typedef enum ngx_http_chunk_type
#endif
{
REQUEST_START,
REQUEST_HEADER,
REQUEST_BODY,
REQUEST_END,
RESPONSE_CODE,
RESPONSE_HEADER,
RESPONSE_BODY,
RESPONSE_END,
CONTENT_LENGTH,
METRIC_DATA_FROM_PLUGIN,
COUNT
} ngx_http_chunk_type_e;
#ifdef __cplusplus
typedef enum class ngx_http_plugin_metric_type
#else
typedef enum ngx_http_plugin_metric_type
#endif
{
TRANSPARENTS_COUNT,
TOTAL_TRANSPARENTS_TIME,
INSPECTION_OPEN_FAILURES_COUNT,
INSPECTION_CLOSE_FAILURES_COUNT,
INSPECTION_SUCCESSES_COUNT,
INJECT_VERDICTS_COUNT,
DROP_VERDICTS_COUNT,
ACCEPT_VERDICTS_COUNT,
IRRELEVANT_VERDICTS_COUNT,
RECONF_VERDICTS_COUNT,
INSPECT_VERDICTS_COUNT,
AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT,
MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT,
MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT,
THREAD_TIMEOUT,
REG_THREAD_TIMEOUT,
REQ_HEADER_THREAD_TIMEOUT,
REQ_BODY_THREAD_TIMEOUT,
AVERAGE_REQ_BODY_SIZE_UPON_TIMEOUT,
MAX_REQ_BODY_SIZE_UPON_TIMEOUT,
MIN_REQ_BODY_SIZE_UPON_TIMEOUT,
RES_HEADER_THREAD_TIMEOUT,
RES_BODY_THREAD_TIMEOUT,
AVERAGE_RES_BODY_SIZE_UPON_TIMEOUT,
MAX_RES_BODY_SIZE_UPON_TIMEOUT,
MIN_RES_BODY_SIZE_UPON_TIMEOUT,
THREAD_FAILURE,
REQ_PROCCESSING_TIMEOUT,
RES_PROCCESSING_TIMEOUT,
REQ_FAILED_TO_REACH_UPSTREAM,
REQ_FAILED_COMPRESSION_COUNT,
RES_FAILED_COMPRESSION_COUNT,
REQ_FAILED_DECOMPRESSION_COUNT,
RES_FAILED_DECOMPRESSION_COUNT,
REQ_SUCCESSFUL_COMPRESSION_COUNT,
RES_SUCCESSFUL_COMPRESSION_COUNT,
REQ_SUCCESSFUL_DECOMPRESSION_COUNT,
RES_SUCCESSFUL_DECOMPRESSION_COUNT,
CORRUPTED_ZIP_SKIPPED_SESSION_COUNT,
CPU_USAGE,
AVERAGE_VM_MEMORY_USAGE,
AVERAGE_RSS_MEMORY_USAGE,
MAX_VM_MEMORY_USAGE,
MAX_RSS_MEMORY_USAGE,
METRIC_TYPES_COUNT
} ngx_http_plugin_metric_type_e;
#ifdef __cplusplus
typedef enum class ngx_http_cp_verdict
#else
typedef enum ngx_http_cp_verdict
#endif
{
TRAFFIC_VERDICT_INSPECT,
TRAFFIC_VERDICT_ACCEPT,
TRAFFIC_VERDICT_DROP,
TRAFFIC_VERDICT_INJECT,
TRAFFIC_VERDICT_IRRELEVANT,
TRAFFIC_VERDICT_RECONF
} ngx_http_cp_verdict_e;
#ifdef __cplusplus
typedef enum class ngx_http_cp_debug_level
#else
typedef enum ngx_http_cp_debug_level
#endif
{
DBG_LEVEL_TRACE,
DBG_LEVEL_DEBUG,
DBG_LEVEL_INFO,
DBG_LEVEL_WARNING,
DBG_LEVEL_ERROR,
#ifndef __cplusplus
DBG_LEVEL_ASSERT,
#endif
DBG_LEVEL_COUNT
} ngx_http_cp_debug_level_e;
#ifdef __cplusplus
typedef enum class ngx_http_meta_data
#else
typedef enum ngx_http_meta_data
#endif
{
HTTP_PROTOCOL_SIZE,
HTTP_PROTOCOL_DATA,
HTTP_METHOD_SIZE,
HTTP_METHOD_DATA,
HOST_NAME_SIZE,
HOST_NAME_DATA,
LISTENING_ADDR_SIZE,
LISTENING_ADDR_DATA,
LISTENING_PORT,
URI_SIZE,
URI_DATA,
CLIENT_ADDR_SIZE,
CLIENT_ADDR_DATA,
CLIENT_PORT,
META_DATA_COUNT
} ngx_http_meta_data_e;
#ifdef __cplusplus
typedef enum class ngx_http_header_data
#else
typedef enum ngx_http_header_data
#endif
{
HEADER_KEY_SIZE,
HEADER_KEY_DATA,
HEADER_VAL_SIZE,
HEADER_VAL_DATA,
HEADER_DATA_COUNT
} ngx_http_header_data_e;
typedef enum ngx_http_inspection_mode
{
NON_BLOCKING_THREAD,
BLOCKING_THREAD,
NO_THREAD,
INSPECTION_MODE_COUNT
} ngx_http_inspection_mode_e;
#ifdef __cplusplus
typedef enum class ngx_web_response_type
#else
typedef enum ngx_web_response_type
#endif
{
CUSTOM_WEB_RESPONSE,
REDIRECT_WEB_RESPONSE
} ngx_web_response_type_e;
typedef struct __attribute__((__packed__)) ngx_http_cp_inject_data {
ngx_http_cp_inject_pos_t injection_pos;
ngx_http_modification_type_e mod_type;
uint16_t injection_size;
uint8_t is_header;
uint8_t orig_buff_index;
char data[0];
} ngx_http_cp_inject_data_t;
typedef struct __attribute__((__packed__)) ngx_http_cp_web_response_data {
uint8_t web_repsonse_type;
uint8_t uuid_size;
union {
struct __attribute__((__packed__)) ngx_http_cp_custom_web_response_data {
uint16_t response_code;
uint8_t title_size;
uint8_t body_size;
char data[0];
} custom_response_data;
struct __attribute__((__packed__)) ngx_http_cp_redirect_data {
uint8_t add_event_id;
uint16_t redirect_location_size;
char redirect_location[0];
} redirect_data;
} response_data;
} ngx_http_cp_web_response_data_t;
typedef union __attribute__((__packed__)) ngx_http_cp_modify_data {
ngx_http_cp_inject_data_t inject_data[0];
ngx_http_cp_web_response_data_t web_response_data[0];
} ngx_http_cp_modify_data_t;
typedef struct __attribute__((__packed__)) ngx_http_cp_reply_from_service {
uint16_t verdict;
uint32_t session_id;
uint8_t modification_count;
ngx_http_cp_modify_data_t modify_data[0];
} ngx_http_cp_reply_from_service_t;
typedef struct __attribute__((__packed__)) ngx_http_cp_request_data {
uint16_t data_type;
uint32_t session_id;
unsigned char data[0];
} ngx_http_cp_request_data_t;
typedef struct __attribute__((__packed__)) ngx_http_cp_metric_data {
uint16_t data_type;
#ifdef __cplusplus
uint64_t data[static_cast<int>(ngx_http_plugin_metric_type::METRIC_TYPES_COUNT)];
#else
uint64_t data[METRIC_TYPES_COUNT];
#endif
} ngx_http_cp_metric_data_t;
#endif // __NGINX_ATTACHMENT_COMMON_H__

View File

@@ -0,0 +1,62 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __NGINX_ATTACHMENT_UTIL__
#define __NGINX_ATTACHMENT_UTIL__
#include <stdio.h>
#include "nginx_attachment_common.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#define IP_STR_MAX_LEN 40
typedef const char * c_str;
int initAttachmentConfig(c_str conf_file);
ngx_http_inspection_mode_e getInspectionMode();
unsigned int getNumOfNginxIpcElements();
unsigned int getKeepAliveIntervalMsec();
unsigned int getDbgLevel();
int isDebugContext(c_str client, c_str server, unsigned int port, c_str method, c_str host , c_str uri);
c_str getStaticResourcesPath();
int isFailOpenMode();
unsigned int getFailOpenTimeout();
unsigned int getMaxSessionsPerMinute();
int isFailOpenOnSessionLimit();
unsigned int getRegistrationThreadTimeout();
unsigned int getReqProccessingTimeout();
unsigned int getReqHeaderThreadTimeout();
unsigned int getReqBodyThreadTimeout();
unsigned int getResProccessingTimeout();
unsigned int getResHeaderThreadTimeout();
unsigned int getResBodyThreadTimeout();
int isIPAddress(c_str ip_str);
int isSkipSource(c_str ip_str);
#ifdef __cplusplus
}
#endif
#endif // __NGINX_ATTACHMENT_UTIL__

View File

@@ -0,0 +1,66 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __SHMEM_IPC_H__
#define __SHMEM_IPC_H__
#include <stdint.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
typedef struct SharedMemoryIPC SharedMemoryIPC;
extern const int corrupted_shmem_error;
SharedMemoryIPC * initIpc(
const char queue_name[32],
const uint32_t user_id,
const uint32_t group_id,
int is_owner,
uint16_t num_of_queue_elem,
void (*debug_func)(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...)
);
void destroyIpc(SharedMemoryIPC *ipc, int is_owner);
int sendData(SharedMemoryIPC *ipc, const uint16_t data_to_send_size, const char *data_to_send);
int
sendChunkedData(
SharedMemoryIPC *ipc,
const uint16_t *data_to_send_sizes,
const char **data_elem_to_send,
const uint8_t num_of_data_elem
);
int receiveData(SharedMemoryIPC *ipc, uint16_t *received_data_size, const char **received_data);
int popData(SharedMemoryIPC *ipc);
int isDataAvailable(SharedMemoryIPC *ipc);
void resetIpc(SharedMemoryIPC *ipc, uint16_t num_of_data_segments);
void dumpIpcMemory(SharedMemoryIPC *ipc);
int isCorruptedShmem(SharedMemoryIPC *ipc, int is_owner);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // __SHMEM_IPC_H__

View File

@@ -0,0 +1,5 @@
add_library(osrc_shmem_ipc SHARED shmem_ipc.c shared_ring_queue.c)
target_link_libraries(osrc_shmem_ipc -lrt)
install(TARGETS osrc_shmem_ipc DESTINATION lib)

View File

@@ -0,0 +1,31 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __SHARED_IPC_DEBUG_H__
#define __SHARED_IPC_DEBUG_H__
extern void (*debug_int)(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...);
#ifndef __FILENAME__
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#endif
enum debugLevel { TraceLevel = 0, WarningLevel = 3 };
#define writeDebug(debug_level, fmt, ...) \
{ \
debug_int(debug_level, __func__, __FILENAME__, __LINE__, fmt, ##__VA_ARGS__); \
}
#endif // __SHARED_IPC_DEBUG_H__

View File

@@ -0,0 +1,513 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "shared_ring_queue.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include "shared_ipc_debug.h"
static const uint16_t empty_buff_mgmt_magic = 0xcafe;
static const uint16_t skip_buff_mgmt_magic = 0xbeef;
const uint16_t max_num_of_data_segments = sizeof(DataSegment)/sizeof(uint16_t);
char g_rx_location_name[MAX_ONE_WAY_QUEUE_NAME_LENGTH] = "";
char g_tx_location_name[MAX_ONE_WAY_QUEUE_NAME_LENGTH] = "";
int32_t g_rx_fd = -1;
int32_t g_tx_fd = -1;
int32_t g_memory_size = -1;
static uint16_t g_num_of_data_segments = 0;
static int
getNumOfDataSegmentsNeeded(uint16_t data_size)
{
int res = (data_size + SHARED_MEMORY_SEGMENT_ENTRY_SIZE - 1) / SHARED_MEMORY_SEGMENT_ENTRY_SIZE;
writeDebug(
TraceLevel, "Checking amount of segments needed. Res: %d, data size: %u, shmem entry size: %u",
res,
data_size,
SHARED_MEMORY_SEGMENT_ENTRY_SIZE
);
return res;
}
static int
isThereEnoughMemoryInQueue(SharedRingQueue *queue, uint8_t num_of_elem_to_push)
{
uint16_t write_pos = queue->write_pos;
uint16_t read_pos = queue->read_pos;
uint16_t num_of_data_segments = queue->num_of_data_segments;
int res;
writeDebug(
TraceLevel, "Checking if memory has space for new elements. "
"Num of elements to push: %u, write index: %u, read index: %u, amount of queue segments: %u",
num_of_elem_to_push,
write_pos,
read_pos,
num_of_data_segments
);
if (num_of_elem_to_push >= num_of_data_segments) {
writeDebug(TraceLevel, "Amount of elements to push is larger then amount of available elements in the queue");
return 0;
}
// add skipped elements during write that does not fit from cur write position till end of queue
if (write_pos + num_of_elem_to_push > num_of_data_segments) {
num_of_elem_to_push += num_of_data_segments - write_pos;
}
// removing the aspect of circularity in queue and simulating as if the queue continued at its end
if (write_pos + num_of_elem_to_push >= num_of_data_segments) {
read_pos += num_of_data_segments;
}
res = write_pos + num_of_elem_to_push < read_pos || write_pos >= read_pos;
writeDebug(TraceLevel, "Finished checking if there is enough place in shared memory. Res: %d", res);
return res;
}
void
resetRingQueue(SharedRingQueue *queue, uint16_t num_of_data_segments)
{
uint16_t *buffer_mgmt;
unsigned int idx;
queue->read_pos = 0;
queue->write_pos = 0;
queue->num_of_data_segments = num_of_data_segments;
buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
for (idx = 0; idx < queue->num_of_data_segments; idx++) {
buffer_mgmt[idx] = empty_buff_mgmt_magic;
}
}
SharedRingQueue *
createSharedRingQueue(const char *shared_location_name, uint16_t num_of_data_segments, int is_owner, int is_tx)
{
SharedRingQueue *queue = NULL;
uint16_t *buffer_mgmt;
uint16_t shmem_fd_flags = is_owner ? O_RDWR | O_CREAT : O_RDWR;
int32_t fd = -1;
uint32_t size_of_memory;
unsigned int idx;
writeDebug(TraceLevel, "Creating a new shared ring queue");
if (num_of_data_segments > max_num_of_data_segments) {
writeDebug(
WarningLevel,
"createSharedRingQueue: Cannot create data segment with %d elements (max number of elements is %u)\n",
num_of_data_segments,
max_num_of_data_segments
);
return NULL;
}
g_num_of_data_segments = num_of_data_segments;
fd = shm_open(shared_location_name, shmem_fd_flags, S_IRWXU | S_IRWXG | S_IRWXO);
if (fd == -1) {
writeDebug(
WarningLevel,
"createSharedRingQueue: Failed to open shared memory for '%s'. Errno: %d\n",
shared_location_name,
errno
);
return NULL;
}
size_of_memory = sizeof(SharedRingQueue) + (num_of_data_segments * sizeof(DataSegment));
if (is_owner && ftruncate(fd, size_of_memory + 1) != 0) {
writeDebug(
WarningLevel,
"createSharedRingQueue: Failed to ftruncate shared memory '%s' to size '%x'\n",
shared_location_name,
size_of_memory
);
close(fd);
return NULL;
}
queue = (SharedRingQueue *)mmap(0, size_of_memory, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (queue == NULL) {
writeDebug(
WarningLevel,
"createSharedRingQueue: Error allocating queue for '%s' of size=%x\n",
shared_location_name,
size_of_memory
);
close(fd);
return NULL;
}
if (is_owner) {
snprintf(queue->shared_location_name, MAX_ONE_WAY_QUEUE_NAME_LENGTH, "%s", shared_location_name);
queue->num_of_data_segments = num_of_data_segments;
queue->read_pos = 0;
queue->write_pos = 0;
queue->size_of_memory = size_of_memory;
buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
for (idx = 0; idx < queue->num_of_data_segments; idx++) {
buffer_mgmt[idx] = empty_buff_mgmt_magic;
}
queue->owner_fd = fd;
} else {
queue->user_fd = fd;
}
g_memory_size = size_of_memory;
if (is_tx) {
g_tx_fd = fd;
snprintf(g_tx_location_name, MAX_ONE_WAY_QUEUE_NAME_LENGTH, "%s", shared_location_name);
} else {
g_rx_fd = fd;
snprintf(g_rx_location_name, MAX_ONE_WAY_QUEUE_NAME_LENGTH, "%s", shared_location_name);
}
writeDebug(
TraceLevel,
"Successfully created a new shared ring queue. "
"Shared memory path: %s, number of segments: %u, is owner: %d, "
"fd flags: %u, fd: %d, memory size: %u, read index: %u, write index: %u",
shared_location_name,
queue->num_of_data_segments,
is_owner,
shmem_fd_flags,
fd,
queue->size_of_memory,
queue->read_pos,
queue->write_pos
);
return queue;
}
void
destroySharedRingQueue(SharedRingQueue *queue, int is_owner, int is_tx)
{
uint32_t size_of_memory = g_memory_size;
int32_t fd = 0;
if(is_owner) {
queue->owner_fd = 0;
} else {
queue->user_fd = 0;
}
if (is_tx) {
fd = g_tx_fd;
g_tx_fd = -1;
} else {
fd = g_rx_fd;
g_rx_fd = -1;
}
if (munmap(queue, size_of_memory) != 0) {
writeDebug(WarningLevel, "destroySharedRingQueue: Failed to unmap shared ring queue\n");
}
if (fd > 0) close(fd);
fd = 0;
// shm_open cleanup
if(is_owner) {
shm_unlink(is_tx ? g_tx_location_name : g_rx_location_name);
}
writeDebug(TraceLevel, "Successfully destroyed shared ring queue. Is owner: %d", is_owner);
}
void
dumpRingQueueShmem(SharedRingQueue *queue)
{
uint16_t segment_idx;
uint16_t data_idx;
uint16_t *buffer_mgmt = NULL;
char data_byte;
writeDebug(
WarningLevel,
"owner_fd: %d, user_fd: %d, size_of_memory: %d, write_pos: %d, read_pos: %d, num_of_data_segments: %d\n",
queue->owner_fd,
queue->user_fd,
queue->size_of_memory,
queue->write_pos,
queue->read_pos,
queue->num_of_data_segments
);
writeDebug(WarningLevel, "mgmt_segment:");
buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
for (segment_idx = 0; segment_idx < max_num_of_data_segments; segment_idx++) {
writeDebug(WarningLevel, "%s%u", (segment_idx == 0 ? " " : ", "), buffer_mgmt[segment_idx]);
}
writeDebug(WarningLevel, "\ndata_segment: ");
for (segment_idx = 0; segment_idx < queue->num_of_data_segments; segment_idx++) {
writeDebug(WarningLevel, "\nMgmt index: %u, value: %u,\nactual data: ", segment_idx, buffer_mgmt[segment_idx]);
for (data_idx = 0; data_idx < SHARED_MEMORY_SEGMENT_ENTRY_SIZE; data_idx++) {
data_byte = queue->data_segment[segment_idx].data[data_idx];
writeDebug(WarningLevel, isprint(data_byte) ? "%c" : "%02X", data_byte);
}
}
writeDebug(WarningLevel, "\nEnd of memory\n");
}
int
peekToQueue(SharedRingQueue *queue, const char **output_buffer, uint16_t *output_buffer_size)
{
uint16_t read_pos = queue->read_pos;
const uint16_t num_of_data_segments = queue->num_of_data_segments;
uint16_t *buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
writeDebug(
TraceLevel,
"Reading data from queue. Read index: %u, number of queue elements: %u",
read_pos,
num_of_data_segments
);
if (isQueueEmpty(queue)) {
writeDebug(WarningLevel, "peekToQueue: Failed to read from an empty queue\n");
return -1;
}
if (read_pos >= num_of_data_segments) {
writeDebug(
WarningLevel,
"peekToQueue: Failed to read from a corrupted queue! (read_pos= %d > num_of_data_segments=%d)\n",
read_pos,
num_of_data_segments
);
return CORRUPTED_SHMEM_ERROR;
}
if (buffer_mgmt[read_pos] == skip_buff_mgmt_magic) {
for ( ; read_pos < num_of_data_segments && buffer_mgmt[read_pos] == skip_buff_mgmt_magic; ++read_pos) {
buffer_mgmt[read_pos] = empty_buff_mgmt_magic;
}
}
if (read_pos == num_of_data_segments) read_pos = 0;
*output_buffer_size = buffer_mgmt[read_pos];
*output_buffer = queue->data_segment[read_pos].data;
queue->read_pos = read_pos;
writeDebug(
TraceLevel,
"Successfully read data from queue. Data size: %u, new Read index: %u",
*output_buffer_size,
queue->read_pos
);
return 0;
}
int
pushBuffersToQueue(
SharedRingQueue *queue,
const char **input_buffers,
const uint16_t *input_buffers_sizes,
const uint8_t num_of_input_buffers
)
{
int idx;
const uint16_t num_of_queue_elem = queue->num_of_data_segments;
uint16_t write_pos = queue->write_pos;
uint16_t total_elem_size = 0;
uint16_t *buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
uint16_t end_pos;
uint16_t num_of_segments_to_write;
char *current_copy_pos;
writeDebug(
TraceLevel,
"Writing new data to queue. write index: %u, number of queue elements: %u, number of elements to push: %u",
write_pos,
num_of_queue_elem,
num_of_input_buffers
);
for (idx = 0; idx < num_of_input_buffers; idx++) {
total_elem_size += input_buffers_sizes[idx];
}
num_of_segments_to_write = getNumOfDataSegmentsNeeded(total_elem_size);
writeDebug(
TraceLevel,
"Checking if there is enough space to push new data. Total new data size: %u, number of segments needed: %u",
total_elem_size,
num_of_segments_to_write
);
if (!isThereEnoughMemoryInQueue(queue, num_of_segments_to_write)) {
writeDebug(WarningLevel, "Cannot write to a full queue\n");
return -1;
}
if (write_pos >= num_of_queue_elem) {
writeDebug(
WarningLevel,
"Cannot write to a location outside the queue. Write index: %u, number of queue elements: %u",
write_pos,
num_of_queue_elem
);
return -1;
}
if (write_pos + num_of_segments_to_write > num_of_queue_elem) {
for ( ; write_pos < num_of_queue_elem; ++write_pos) {
buffer_mgmt[write_pos] = skip_buff_mgmt_magic;
}
write_pos = 0;
}
writeDebug(
TraceLevel,
"Setting new management data. Write index: %u, total elements in index: %u",
write_pos,
total_elem_size
);
buffer_mgmt[write_pos] = total_elem_size;
current_copy_pos = queue->data_segment[write_pos].data;
for (idx = 0; idx < num_of_input_buffers; idx++) {
writeDebug(
TraceLevel,
"Writing data to queue. Data index: %u, data size: %u, copy destination: %p",
idx,
input_buffers_sizes[idx],
current_copy_pos
);
memcpy(current_copy_pos, input_buffers[idx], input_buffers_sizes[idx]);
current_copy_pos += input_buffers_sizes[idx];
}
write_pos++;
end_pos = write_pos + num_of_segments_to_write - 1;
for ( ; write_pos < end_pos; ++write_pos) {
buffer_mgmt[write_pos] = skip_buff_mgmt_magic;
}
queue->write_pos = write_pos < num_of_queue_elem ? write_pos : 0;
writeDebug(TraceLevel, "Successfully pushed data to queue. New write index: %u", queue->write_pos);
return 0;
}
int
pushToQueue(SharedRingQueue *queue, const char *input_buffer, const uint16_t input_buffer_size)
{
return pushBuffersToQueue(queue, &input_buffer, &input_buffer_size, 1);
}
int
popFromQueue(SharedRingQueue *queue)
{
uint16_t num_of_read_segments;
uint16_t read_pos = queue->read_pos;
uint16_t end_pos;
uint16_t num_of_data_segments = queue->num_of_data_segments;
uint16_t *buffer_mgmt = (uint16_t *)queue->mgmt_segment.data;
writeDebug(
TraceLevel,
"Removing data from queue. new data to queue. Read index: %u, number of queue elements: %u",
read_pos,
num_of_data_segments
);
if (isQueueEmpty(queue)) {
writeDebug(TraceLevel, "Cannot pop data from empty queue");
return -1;
}
num_of_read_segments = getNumOfDataSegmentsNeeded(buffer_mgmt[read_pos]);
end_pos = read_pos + num_of_read_segments;
writeDebug(
TraceLevel,
"Size of data to remove: %u, number of queue elements to free: %u, current read index: %u, end index: %u",
buffer_mgmt[read_pos],
num_of_read_segments,
read_pos,
end_pos
);
for ( ; read_pos < end_pos; ++read_pos ) {
buffer_mgmt[read_pos] = empty_buff_mgmt_magic;
}
if (read_pos < num_of_data_segments && buffer_mgmt[read_pos] == skip_buff_mgmt_magic) {
for ( ; read_pos < num_of_data_segments; ++read_pos ) {
buffer_mgmt[read_pos] = empty_buff_mgmt_magic;
}
}
if (read_pos == num_of_data_segments) read_pos = 0;
queue->read_pos = read_pos;
writeDebug(TraceLevel, "Successfully popped data from queue. New read index: %u", queue->read_pos);
return 0;
}
int
isQueueEmpty(SharedRingQueue *queue)
{
return queue->read_pos == queue->write_pos;
}
int
isCorruptedQueue(SharedRingQueue *queue, int is_tx)
{
writeDebug(
TraceLevel,
"Checking if shared ring queue is corrupted. "
"g_num_of_data_segments = %u, queue->num_of_data_segments = %u, queue->read_pos = %u, queue->write_pos = %u, "
"g_memory_size = %d, queue->size_of_memory = %d, "
"queue->shared_location_name = %s, g_tx_location_name = %s, g_rx_location_name = %s, is_tx = %d",
g_num_of_data_segments,
queue->num_of_data_segments,
queue->read_pos,
queue->write_pos,
g_memory_size,
queue->size_of_memory,
queue->shared_location_name,
g_tx_location_name,
g_rx_location_name,
is_tx
);
if (g_num_of_data_segments == 0) return 0;
if (queue->num_of_data_segments != g_num_of_data_segments) return 1;
if (queue->size_of_memory != g_memory_size) return 1;
if (queue->read_pos > g_num_of_data_segments) return 1;
if (queue->write_pos > g_num_of_data_segments) return 1;
if (strcmp(queue->shared_location_name, is_tx ? g_tx_location_name : g_rx_location_name) != 0) return 1;
return 0;
}

View File

@@ -0,0 +1,75 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __SHARED_RING_QUEUE_H__
#define __SHARED_RING_QUEUE_H__
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
#define SHARED_MEMORY_SEGMENT_ENTRY_SIZE 1024
#define MAX_ONE_WAY_QUEUE_NAME_LENGTH 64
#define CORRUPTED_SHMEM_ERROR -2
typedef struct DataSegment {
char data[SHARED_MEMORY_SEGMENT_ENTRY_SIZE];
} DataSegment;
typedef struct __attribute__((__packed__)) SharedRingQueue {
char shared_location_name[MAX_ONE_WAY_QUEUE_NAME_LENGTH];
int32_t owner_fd;
int32_t user_fd;
int32_t size_of_memory;
uint16_t write_pos;
uint16_t read_pos;
uint16_t num_of_data_segments;
DataSegment mgmt_segment;
DataSegment data_segment[0];
} SharedRingQueue;
SharedRingQueue *
createSharedRingQueue(
const char *shared_location_name,
uint16_t num_of_data_segments,
int is_owner,
int is_tx
);
void destroySharedRingQueue(SharedRingQueue *queue, int is_owner, int is_tx);
int isQueueEmpty(SharedRingQueue *queue);
int isCorruptedQueue(SharedRingQueue *queue, int is_tx);
int peekToQueue(SharedRingQueue *queue, const char **output_buffer, uint16_t *output_buffer_size);
int popFromQueue(SharedRingQueue *queue);
int pushToQueue(SharedRingQueue *queue, const char *input_buffer, const uint16_t input_buffer_size);
void resetRingQueue(SharedRingQueue *queue, uint16_t num_of_data_segments);
void dumpRingQueueShmem(SharedRingQueue *queue);
int
pushBuffersToQueue(
SharedRingQueue *queue,
const char **input_buffers,
const uint16_t *input_buffers_sizes,
const uint8_t num_of_input_buffers
);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // __SHARED_RING_QUEUE_H__

288
core/shmem_ipc/shmem_ipc.c Normal file
View File

@@ -0,0 +1,288 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "shmem_ipc.h"
#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include "shared_ring_queue.h"
#include "shared_ipc_debug.h"
#define UNUSED(x) (void)(x)
const int corrupted_shmem_error = CORRUPTED_SHMEM_ERROR;
static const size_t max_one_way_queue_name_length = MAX_ONE_WAY_QUEUE_NAME_LENGTH;
static const size_t max_shmem_path_length = 72;
struct SharedMemoryIPC {
char shm_name[32];
SharedRingQueue *rx_queue;
SharedRingQueue *tx_queue;
};
void
debugInitial(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...)
{
UNUSED(is_error);
UNUSED(func);
UNUSED(file);
UNUSED(line_num);
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
void (*debug_int)(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...) = debugInitial;
static int
isTowardsOwner(int is_owner, int is_tx)
{
if (is_owner) return !is_tx;
return is_tx;
}
static SharedRingQueue *
createOneWayIPCQueue(
const char *name,
const uint32_t user_id,
const uint32_t group_id,
int is_tx_queue,
int is_owner,
uint16_t num_of_queue_elem
)
{
SharedRingQueue *ring_queue = NULL;
char queue_name[max_one_way_queue_name_length];
char shmem_path[max_shmem_path_length];
const char *direction = isTowardsOwner(is_owner, is_tx_queue) ? "rx" : "tx";
snprintf(queue_name, sizeof(queue_name) - 1, "__cp_nano_%s_shared_memory_%s__", direction, name);
writeDebug(
TraceLevel,
"Creating one way IPC queue. Name: %s, direction: %s, size: %d",
name,
direction,
num_of_queue_elem
);
ring_queue = createSharedRingQueue(queue_name, num_of_queue_elem, is_owner, isTowardsOwner(is_owner, is_tx_queue));
if (ring_queue == NULL) {
writeDebug(
WarningLevel,
"Failed to create %s shared ring queue of size=%d for '%s'\n",
direction,
num_of_queue_elem,
queue_name
);
return NULL;
}
int ret = snprintf(shmem_path, sizeof(shmem_path) - 1, "/dev/shm/%s", queue_name);
if (ret < 0 || (size_t)ret < (strlen(direction) + strlen(name))) {
return NULL;
}
if (is_owner && chmod(shmem_path, 0666) == -1) {
writeDebug(WarningLevel, "Failed to set the permissions");
destroySharedRingQueue(ring_queue, is_owner, isTowardsOwner(is_owner, is_tx_queue));
return NULL;
}
writeDebug(
TraceLevel,
"Successfully created one way IPC queue. "
"Name: %s, user id: %u, group id: %u, is owner: %d, number of queue elements: %u, direction: %s, path: %s",
queue_name,
user_id,
group_id,
is_owner,
num_of_queue_elem,
direction,
shmem_path
);
return ring_queue;
}
SharedMemoryIPC *
initIpc(
const char queue_name[32],
uint32_t user_id,
uint32_t group_id,
int is_owner,
uint16_t num_of_queue_elem,
void (*debug_func)(int is_error, const char *func, const char *file, int line_num, const char *fmt, ...))
{
SharedMemoryIPC *ipc = NULL;
debug_int = debug_func;
writeDebug(
TraceLevel,
"Initializing new IPC. "
"Queue name: %s, user id: %u, group id: %u, is owner: %d, number of queue elements: %u\n",
queue_name,
user_id,
group_id,
is_owner,
num_of_queue_elem
);
ipc = malloc(sizeof(SharedMemoryIPC));
if (ipc == NULL) {
writeDebug(WarningLevel, "Failed to allocate Shared Memory IPC for '%s'\n", queue_name);
debug_int = debugInitial;
return NULL;
}
ipc->rx_queue = NULL;
ipc->tx_queue = NULL;
ipc->rx_queue = createOneWayIPCQueue(queue_name, user_id, group_id, 0, is_owner, num_of_queue_elem);
if (ipc->rx_queue == NULL) {
writeDebug(
WarningLevel,
"Failed to allocate rx queue. "
"Queue name: %s, user id: %u, group id: %u, is owner: %d, number of queue elements: %u",
queue_name,
user_id,
group_id,
is_owner,
num_of_queue_elem
);
destroyIpc(ipc, is_owner);
debug_int = debugInitial;
return NULL;
}
ipc->tx_queue = createOneWayIPCQueue(queue_name, user_id, group_id, 1, is_owner, num_of_queue_elem);
if (ipc->tx_queue == NULL) {
writeDebug(
WarningLevel,
"Failed to allocate rx queue. "
"Queue name: %s, user id: %u, group id: %u, is owner: %d, number of queue elements: %u",
queue_name,
user_id,
group_id,
is_owner,
num_of_queue_elem
);
destroyIpc(ipc, is_owner);
debug_int = debugInitial;
return NULL;
}
writeDebug(TraceLevel, "Successfully allocated IPC");
strncpy(ipc->shm_name, queue_name, sizeof(ipc->shm_name));
return ipc;
}
void
resetIpc(SharedMemoryIPC *ipc, uint16_t num_of_data_segments)
{
writeDebug(TraceLevel, "Reseting IPC queues\n");
resetRingQueue(ipc->rx_queue, num_of_data_segments);
resetRingQueue(ipc->tx_queue, num_of_data_segments);
}
void
destroyIpc(SharedMemoryIPC *shmem, int is_owner)
{
writeDebug(TraceLevel, "Destroying IPC queues\n");
if (shmem->rx_queue != NULL) {
destroySharedRingQueue(shmem->rx_queue, is_owner, isTowardsOwner(is_owner, 0));
shmem->rx_queue = NULL;
}
if (shmem->tx_queue != NULL) {
destroySharedRingQueue(shmem->tx_queue, is_owner, isTowardsOwner(is_owner, 1));
shmem->tx_queue = NULL;
}
debug_int = debugInitial;
free(shmem);
}
void
dumpIpcMemory(SharedMemoryIPC *ipc)
{
writeDebug(WarningLevel, "Ipc memory dump:\n");
writeDebug(WarningLevel, "RX queue:\n");
dumpRingQueueShmem(ipc->rx_queue);
writeDebug(WarningLevel, "TX queue:\n");
dumpRingQueueShmem(ipc->tx_queue);
}
int
sendData(SharedMemoryIPC *ipc, const uint16_t data_to_send_size, const char *data_to_send)
{
writeDebug(TraceLevel, "Sending data of size %u\n", data_to_send_size);
return pushToQueue(ipc->tx_queue, data_to_send, data_to_send_size);
}
int
sendChunkedData(
SharedMemoryIPC *ipc,
const uint16_t *data_to_send_sizes,
const char **data_elem_to_send,
const uint8_t num_of_data_elem
)
{
writeDebug(TraceLevel, "Sending %u chunks of data\n", num_of_data_elem);
return pushBuffersToQueue(ipc->tx_queue, data_elem_to_send, data_to_send_sizes, num_of_data_elem);
}
int
receiveData(SharedMemoryIPC *ipc, uint16_t *received_data_size, const char **received_data)
{
int res = peekToQueue(ipc->rx_queue, received_data, received_data_size);
writeDebug(TraceLevel, "Received data from queue. Res: %d, data size: %u\n", res, *received_data_size);
return res;
}
int
popData(SharedMemoryIPC *ipc)
{
int res = popFromQueue(ipc->rx_queue);
writeDebug(TraceLevel, "Popped data from queue. Res: %d\n", res);
return res;
}
int
isDataAvailable(SharedMemoryIPC *ipc)
{
int res = !isQueueEmpty(ipc->rx_queue);
writeDebug(TraceLevel, "Checking if there is data pending to be read. Res: %d\n", res);
return res;
}
int
isCorruptedShmem(SharedMemoryIPC *ipc, int is_owner)
{
if (isCorruptedQueue(ipc->rx_queue, isTowardsOwner(is_owner, 0)) ||
isCorruptedQueue(ipc->tx_queue, isTowardsOwner(is_owner, 1))
) {
writeDebug(WarningLevel, "Detected corrupted shared memory queue. Shared memory name: %s", ipc->shm_name);
return 1;
}
return 0;
}