change processing logic: scan string for each field.
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
[NAT_FORMAT]
|
||||||
|
magic_num=0x004e4154
|
||||||
|
version=0x0200
|
||||||
|
|
||||||
[NAT_FORMAT_SEND]
|
[NAT_FORMAT_SEND]
|
||||||
batch_size=20
|
batch_size=20
|
||||||
host_ip=127.0.0.1
|
host_ip=127.0.0.1
|
||||||
|
|||||||
@@ -21,17 +21,82 @@
|
|||||||
#define HW_EVENT_ADD "SESSION_BUILT"
|
#define HW_EVENT_ADD "SESSION_BUILT"
|
||||||
#define HW_EVENT_DEL "SESSION_TEARDOWN"
|
#define HW_EVENT_DEL "SESSION_TEARDOWN"
|
||||||
|
|
||||||
|
#define FORMAT_LOG_PROTOCOL_ICMP 0x01
|
||||||
|
#define FORMAT_LOG_PROTOCOL_TCP 0x06
|
||||||
|
#define FORMAT_LOG_PROTOCOL_UDP 0x17
|
||||||
|
|
||||||
struct nat_format_global_info g_nat_format_info;
|
struct nat_format_global_info g_nat_format_info;
|
||||||
|
|
||||||
char *multicast_payload;
|
char *multicast_payload;
|
||||||
int cur_pkt = 0;
|
int cur_pkt = 0;
|
||||||
|
|
||||||
int udp_socket;
|
int udp_socket;
|
||||||
struct sockaddr_in dst_addr;
|
struct sockaddr_in dst_addr = {0};
|
||||||
|
|
||||||
|
// 函数:子串匹配,返回子串结尾+1的位置,即值的起始位置
|
||||||
|
int get_value_offset(char *str, int str_len, const char *key) {
|
||||||
|
int i, j;
|
||||||
|
int key_len = strlen(key);
|
||||||
|
|
||||||
|
for (i = 0; i <= str_len - key_len - 1; i++) {
|
||||||
|
for (j = 0; j < key_len; j++) {
|
||||||
|
if (str[i + j]!= key[j])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j == key_len)
|
||||||
|
return (i + key_len + 1);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 函数:从字符串中提取点分十进制ip并转换为二进制数
|
||||||
|
int extract_ip(char *data, int data_len, const char *key, unsigned int *dst) {
|
||||||
|
int offset;
|
||||||
|
char tmp_ip[20];
|
||||||
|
if ((offset = get_value_offset(data, data_len, key)) == -1) { return -1; }
|
||||||
|
if (sscanf(data+offset, "%19[^,]", tmp_ip) != 1) { return -1; }
|
||||||
|
if (inet_pton(AF_INET, tmp_ip, dst) <= 0) {return -1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 函数:从字符串中提取字符串并转为16位数
|
||||||
|
int extract_port(char *data, int data_len, const char *key, unsigned short *dst) {
|
||||||
|
int offset;
|
||||||
|
if ((offset = get_value_offset(data, data_len, key)) == -1) { return -1; }
|
||||||
|
if (sscanf(data+offset, "%hu", dst) != 1) { return -1; }
|
||||||
|
*dst = htons(*dst);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 函数:从字符串中提取字符串并转为32位数
|
||||||
|
int extract_time(char *data, int data_len, const char *key, unsigned int *dst) {
|
||||||
|
int offset;
|
||||||
|
if ((offset = get_value_offset(data, data_len, key)) == -1) { return -1; }
|
||||||
|
if (sscanf(data+offset, "%u", dst) != 1) { return -1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 函数:从字符串中提取字符串并转为标识
|
||||||
|
int extract_protocol(char *data, int data_len, const char *key, char *dst) {
|
||||||
|
int offset;
|
||||||
|
char protocol_str[20];
|
||||||
|
if ((offset = get_value_offset(data, data_len, key)) == -1) { return -1; }
|
||||||
|
if (sscanf(data+offset, "%[^,]", protocol_str) != 1) { return -1; }
|
||||||
|
if (strcmp(protocol_str, "tcp") == 0) {
|
||||||
|
*dst = FORMAT_LOG_PROTOCOL_TCP;
|
||||||
|
} else if (strcmp(protocol_str, "udp") == 0) {
|
||||||
|
*dst = FORMAT_LOG_PROTOCOL_UDP;
|
||||||
|
} else if (strcmp(protocol_str, "icmp") == 0) {
|
||||||
|
*dst = FORMAT_LOG_PROTOCOL_ICMP;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化函数
|
// 初始化函数
|
||||||
int nat_format_init(void) {
|
int nat_format_init(void) {
|
||||||
// 读取配置文件
|
// 读取配置文件
|
||||||
|
MESA_load_profile_uint_def(NAT_FORMAT_CONFIG_FILE, "NAT_FORMAT", "magic_num", &g_nat_format_info.magic_num, 0x004e4154);
|
||||||
|
MESA_load_profile_uint_def(NAT_FORMAT_CONFIG_FILE, "NAT_FORMAT", "version", &g_nat_format_info.version, 0x0200);
|
||||||
|
|
||||||
MESA_load_profile_uint_def(NAT_FORMAT_CONFIG_FILE, "NAT_FORMAT_SEND", "batch_size", &g_nat_format_info.batch_size, 20);
|
MESA_load_profile_uint_def(NAT_FORMAT_CONFIG_FILE, "NAT_FORMAT_SEND", "batch_size", &g_nat_format_info.batch_size, 20);
|
||||||
MESA_load_profile_string_def(NAT_FORMAT_CONFIG_FILE, "NAT_FORMAT_SEND", "host_ip", g_nat_format_info.host_ip, 16, "127.0.0.1");
|
MESA_load_profile_string_def(NAT_FORMAT_CONFIG_FILE, "NAT_FORMAT_SEND", "host_ip", g_nat_format_info.host_ip, 16, "127.0.0.1");
|
||||||
MESA_load_profile_int_def(NAT_FORMAT_CONFIG_FILE, "NAT_FORMAT_SEND", "host_port", &g_nat_format_info.host_port, 5678);
|
MESA_load_profile_int_def(NAT_FORMAT_CONFIG_FILE, "NAT_FORMAT_SEND", "host_port", &g_nat_format_info.host_port, 5678);
|
||||||
@@ -57,15 +122,20 @@ int nat_format_init(void) {
|
|||||||
multicast_payload = (char *)malloc(PAYLOAD_LEN * g_nat_format_info.batch_size);
|
multicast_payload = (char *)malloc(PAYLOAD_LEN * g_nat_format_info.batch_size);
|
||||||
memset(multicast_payload, 0, PAYLOAD_LEN * g_nat_format_info.batch_size);
|
memset(multicast_payload, 0, PAYLOAD_LEN * g_nat_format_info.batch_size);
|
||||||
|
|
||||||
// 创建socket用于发包,绑定组播地址和发包端口
|
// 创建socket用于发包
|
||||||
udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
|
udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
if (udp_socket == -1) {
|
if (udp_socket == -1) {
|
||||||
printf("UDP multicast socket creation failed:%d, %s\n", errno, strerror(errno));
|
printf("UDP multicast socket creation failed:%d, %s\n", errno, strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
// 绑定组播地址
|
||||||
dst_addr.sin_family = AF_INET;
|
dst_addr.sin_family = AF_INET;
|
||||||
dst_addr.sin_addr.s_addr = inet_addr(g_nat_format_info.multicast_ip);
|
dst_addr.sin_addr.s_addr = inet_addr(g_nat_format_info.multicast_ip);
|
||||||
dst_addr.sin_port = htons(g_nat_format_info.multicast_port);
|
dst_addr.sin_port = htons(g_nat_format_info.multicast_port);
|
||||||
|
struct in_addr multicast_addr;
|
||||||
|
inet_pton(AF_INET, g_nat_format_info.multicast_ip, &multicast_addr.s_addr);
|
||||||
|
setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_IF, &multicast_addr, sizeof(multicast_addr));
|
||||||
|
// 绑定发包端口
|
||||||
struct sockaddr_in src_addr;
|
struct sockaddr_in src_addr;
|
||||||
src_addr.sin_family = AF_INET;
|
src_addr.sin_family = AF_INET;
|
||||||
src_addr.sin_port = htons(g_nat_format_info.host_port);
|
src_addr.sin_port = htons(g_nat_format_info.host_port);
|
||||||
@@ -104,46 +174,92 @@ char nat_format_entry(struct streaminfo *a_udp, void **pme, int thread_seq, void
|
|||||||
|
|
||||||
// 提取udp流信息
|
// 提取udp流信息
|
||||||
char *udp_data = (char *)a_udp->pudpdetail->pdata;
|
char *udp_data = (char *)a_udp->pudpdetail->pdata;
|
||||||
// int32_t upd_data_len = a_udp->pudpdetail->datalen;
|
int32_t udp_data_len = a_udp->pudpdetail->datalen;
|
||||||
unsigned short udp_port = a_udp->addr.tuple4_v4->source;
|
unsigned short udp_port = ntohs(a_udp->addr.tuple4_v4->dest);
|
||||||
|
|
||||||
// 提取nat信息
|
// 提取nat信息
|
||||||
struct nat_payload nat_payload;
|
struct nat_payload nat_payload;
|
||||||
|
nat_payload.magic_num = htonl(g_nat_format_info.magic_num);
|
||||||
|
nat_payload.version = htons((unsigned short)g_nat_format_info.version);
|
||||||
nat_payload.fw_ip = a_udp->addr.tuple4_v4->saddr; // 防火墙ip为源ip,本身为二进制格式
|
nat_payload.fw_ip = a_udp->addr.tuple4_v4->saddr; // 防火墙ip为源ip,本身为二进制格式
|
||||||
// 根据数据来源的端口采取不同的处理策略
|
// 根据数据来源的端口采取不同的处理策略
|
||||||
// 华为syslog格式:防火墙日志生成时间需要转换为时间戳、动作字符串需要转换为序号、点分十进制格式ip需要转换为二进制
|
// 华为syslog格式:防火墙日志生成时间需要转换为时间戳、动作字符串需要转换为序号、点分十进制格式ip需要转换为二进制
|
||||||
if (udp_port == g_nat_format_info.hw_syslog_port) {
|
if (udp_port == g_nat_format_info.hw_syslog_port) {
|
||||||
|
// <190>2024-01-25 01:41:37 JXNC_LTGC_Eudemon_A %%01SECLOG/6/SESSION_TEARDOWN(l):IPVer=4,Protocol=udp,SourceIP=10.119.25.34,DestinationIP=220.248.192.13,SourcePort=35040,DestinationPort=53,SourceNatIP=118.212.209.248,SourceNatPort=6438,BeginTime=1706147236,EndTime=1706147236,SourceVpnID=0,DestinationVpnID=0,SourceZone=pscenet,DestinationZone=untrust,PolicyName=---,CloseReason=aged-out.
|
||||||
|
// sscanf(udp_data, "%*[^>]>%20[0-9: -]%*[^/]/%*[0-9]/%19[A-Z_]%*[^:]:IPVer=%*[^,],Protocol=%*[^,],SourceIP=%19[^,],DestinationIP=%19[^,],SourcePort=%hu,DestinationPort=%hu,SourceNatIP=%19[^,],SourceNatPort=%hu,BeginTime=%u,EndTime=%u", fw_log_time, action_str, src_intra_ip, dst_ip, &nat_payload.src_intra_port, &nat_payload.dst_port, src_extra_ip, &nat_payload.src_extra_port, &nat_payload.stream_start_timestamp, &nat_payload.stream_end_timestamp);
|
||||||
|
|
||||||
|
// 字段前的部分:防火墙日志生成时间、动作
|
||||||
char fw_log_time[21];
|
char fw_log_time[21];
|
||||||
char action_str[20];
|
char action_str[20];
|
||||||
char src_intra_ip[20];
|
if (sscanf(udp_data, "%*[^>]>%20[0-9: -]%*[^/]/%*[0-9]/%19[A-Z_]", fw_log_time, action_str) != 2) {
|
||||||
char dst_ip[20];
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "NAT extraction fw_log_time or action_str failed");
|
||||||
char src_extra_ip[20];
|
return APP_STATE_GIVEME;
|
||||||
int suc_num = sscanf(udp_data, "%*[^>]>%20[0-9: -]%*[^/]/%*[0-9]/%19[A-Z_]%*[^:]:SourceIP=%19[^,],DestinationIP=%19[^,],SourcePort=%hu,DestinationPort=%hu,SourceNatIP=%19[^,],SourceNatPort=%hu,BeginTime=%u,EndTime=%u",
|
|
||||||
fw_log_time, action_str, src_intra_ip, dst_ip, &nat_payload.src_intra_port, &nat_payload.dst_port, src_extra_ip, &nat_payload.src_extra_port, &nat_payload.stream_start_timestamp, &nat_payload.stream_end_timestamp);
|
|
||||||
if (suc_num != 10) {
|
|
||||||
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_FATAL, "nat_format Huawei syslog", "NAT extraction failed: %d/10", suc_num);
|
|
||||||
return APP_STATE_DROPME;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tm fw_time;
|
struct tm fw_time;
|
||||||
if (strptime(fw_log_time, "%Y-%m-%d %H:%M:%S ", &fw_time) == NULL) {
|
if (strptime(fw_log_time, "%Y-%m-%d %H:%M:%S ", &fw_time) == NULL) {
|
||||||
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_FATAL, "nat_format Huawei syslog", "firewall log generation time extraction failed: %s", fw_log_time);
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "firewall log generation time extraction failed: %s", fw_log_time);
|
||||||
return APP_STATE_GIVEME;
|
return APP_STATE_GIVEME;
|
||||||
}
|
}
|
||||||
nat_payload.fw_log_timestamp = (unsigned int)mktime(&fw_time);
|
nat_payload.fw_log_timestamp = (unsigned int)mktime(&fw_time);
|
||||||
|
|
||||||
inet_pton(AF_INET, src_intra_ip, &nat_payload.src_intra_ip);
|
|
||||||
inet_pton(AF_INET, dst_ip, &nat_payload.dst_ip);
|
|
||||||
inet_pton(AF_INET, src_extra_ip, &nat_payload.src_extra_ip);
|
|
||||||
|
|
||||||
if (strcmp(action_str, HW_EVENT_ADD) == 0) {
|
if (strcmp(action_str, HW_EVENT_ADD) == 0) {
|
||||||
nat_payload.action = FORMAT_LOG_ACTION_ADD;
|
nat_payload.action = FORMAT_LOG_ACTION_ADD;
|
||||||
} else if (strcmp(action_str, HW_EVENT_DEL) == 0) {
|
} else if (strcmp(action_str, HW_EVENT_DEL) == 0) {
|
||||||
nat_payload.action = FORMAT_LOG_ACTION_DEL;
|
nat_payload.action = FORMAT_LOG_ACTION_DEL;
|
||||||
} else {
|
} else {
|
||||||
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_FATAL, "nat_format Huawei syslog", "Event field is an undefined value: %s", action_str);
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "Event field is an undefined value: %s", action_str);
|
||||||
return APP_STATE_GIVEME;
|
return APP_STATE_GIVEME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 字段部分(按顺序排列):SourceIP、DestinationIP、SourcePort、DestinationPort、SourceNatIP、SourceNatPort、BeginTime、EndTime
|
||||||
|
// 依次对应nat_payload的:src_intra_ip, dst_ip, src_intra_port, &dst_port, src_extra_ip, src_extra_port, stream_start_timestamp, stream_end_timestamp
|
||||||
|
|
||||||
|
if (extract_ip(udp_data, udp_data_len, "SourceIP", &nat_payload.src_intra_ip) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "SourceIP Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_ip(udp_data, udp_data_len, "DestinationIP", &nat_payload.dst_ip) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "DestinationIP Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_port(udp_data, udp_data_len, "SourcePort", &nat_payload.src_intra_port) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "SourcePort Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_port(udp_data, udp_data_len, "DestinationPort", &nat_payload.dst_port) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "DestinationPort Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_ip(udp_data, udp_data_len, "SourceNatIP", &nat_payload.src_extra_ip) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "SourceNatIP Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_port(udp_data, udp_data_len, "SourceNatPort", &nat_payload.src_extra_port) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "SourceNatPort Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_time(udp_data, udp_data_len, "BeginTime", &nat_payload.stream_start_timestamp) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "BeginTime Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_time(udp_data, udp_data_len, "EndTime", &nat_payload.stream_end_timestamp) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "EndTime Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_protocol(udp_data, udp_data_len, "Protocol", &nat_payload.protocol) < 0) {
|
||||||
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "Protocol Not Found");
|
||||||
|
return APP_STATE_GIVEME;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (udp_port == g_nat_format_info.hw_binary_port) {
|
} else if (udp_port == g_nat_format_info.hw_binary_port) {
|
||||||
return APP_STATE_GIVEME;
|
return APP_STATE_GIVEME;
|
||||||
} else if (udp_port == g_nat_format_info.h3_syslog_port) {
|
} else if (udp_port == g_nat_format_info.h3_syslog_port) {
|
||||||
@@ -162,8 +278,8 @@ char nat_format_entry(struct streaminfo *a_udp, void **pme, int thread_seq, void
|
|||||||
|
|
||||||
// 攒够20个进行发送
|
// 攒够20个进行发送
|
||||||
if (cur_pkt == 20) {
|
if (cur_pkt == 20) {
|
||||||
if (sendto(udp_socket, multicast_payload, strlen(multicast_payload), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr)) < 0) {
|
if (sendto(udp_socket, multicast_payload, PAYLOAD_LEN*g_nat_format_info.batch_size, 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr)) < 0) {
|
||||||
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_FATAL, "nat_format Huawei syslog", "Send multicast failed: %s", strerror(errno));
|
MESA_handle_runtime_log(g_nat_format_info.log, RLOG_LV_INFO, "nat_format Huawei syslog", "Send multicast failed: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
cur_pkt = 0;
|
cur_pkt = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,11 @@
|
|||||||
#include <MESA/MESA_handle_logger.h>
|
#include <MESA/MESA_handle_logger.h>
|
||||||
#include <MESA/MESA_prof_load.h>
|
#include <MESA/MESA_prof_load.h>
|
||||||
|
|
||||||
#define FORMAT_LOG_MAGIC_NUMBER 0x004e4154
|
|
||||||
#define FORMAT_LOG_VERSION 0x0200
|
|
||||||
|
|
||||||
#define FORMAT_LOG_PROTOCOL_ICMP 0x01
|
|
||||||
#define FORMAT_LOG_PROTOCOL_TCP 0x06
|
|
||||||
#define FORMAT_LOG_PROTOCOL_UDP 0x17 // 目前均采用UDP传输
|
|
||||||
|
|
||||||
// 全局配置信息
|
// 全局配置信息
|
||||||
struct nat_format_global_info {
|
struct nat_format_global_info {
|
||||||
|
u_int32_t magic_num;
|
||||||
|
u_int32_t version;
|
||||||
|
|
||||||
u_int32_t batch_size;
|
u_int32_t batch_size;
|
||||||
char host_ip[64];
|
char host_ip[64];
|
||||||
int32_t host_port;
|
int32_t host_port;
|
||||||
@@ -34,8 +30,8 @@ struct nat_format_global_info {
|
|||||||
|
|
||||||
// 格式化NAT报文载荷
|
// 格式化NAT报文载荷
|
||||||
struct nat_payload {
|
struct nat_payload {
|
||||||
unsigned int magic_num = FORMAT_LOG_MAGIC_NUMBER;
|
unsigned int magic_num;
|
||||||
unsigned short version = FORMAT_LOG_VERSION;
|
unsigned short version;
|
||||||
unsigned int fw_log_timestamp;
|
unsigned int fw_log_timestamp;
|
||||||
unsigned int fw_ip;
|
unsigned int fw_ip;
|
||||||
char action;
|
char action;
|
||||||
@@ -47,7 +43,7 @@ struct nat_payload {
|
|||||||
unsigned short src_extra_port;
|
unsigned short src_extra_port;
|
||||||
unsigned int dst_ip;
|
unsigned int dst_ip;
|
||||||
unsigned short dst_port;
|
unsigned short dst_port;
|
||||||
char protocol = FORMAT_LOG_PROTOCOL_UDP;
|
char protocol;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" int nat_format_init(void);
|
extern "C" int nat_format_init(void);
|
||||||
|
|||||||
Reference in New Issue
Block a user