From 7a892ba8c584ab4535b2749611ef498f5289ce3e Mon Sep 17 00:00:00 2001
From: bitcoffee <liuxin350@huawei.com>
Date: Tue, 24 Dec 2024 22:54:46 +0800
Subject: [PATCH 1/4] add sockmap dump function

---
 oncn-mda/cli_src/CMakeLists.txt   |   3 +
 oncn-mda/cli_src/func/dump.c      | 214 ++++++++++++++++++++++++++++++
 oncn-mda/cli_src/func/global.c    |   2 +-
 oncn-mda/cli_src/mdadump.c        |   7 +
 oncn-mda/ebpf_src/sock_redirect.c |  96 +++++++++++++-
 oncn-mda/include/data.h           |   3 +
 oncn-mda/include/dump.h           |   6 +
 oncn-mda/include/macli.h          |  12 ++
 oncn-mda/include/writecap.h       |  80 +++++++++++
 9 files changed, 421 insertions(+), 2 deletions(-)
 create mode 100644 oncn-mda/cli_src/func/dump.c
 create mode 100644 oncn-mda/cli_src/mdadump.c
 create mode 100644 oncn-mda/include/dump.h
 create mode 100644 oncn-mda/include/writecap.h

diff --git a/oncn-mda/cli_src/CMakeLists.txt b/oncn-mda/cli_src/CMakeLists.txt
index 5bf849f..7c710b3 100644
--- a/oncn-mda/cli_src/CMakeLists.txt
+++ b/oncn-mda/cli_src/CMakeLists.txt
@@ -16,4 +16,7 @@ endif($ENV{HS_COVERAGE_ENABLE})
 add_executable(mdacore mdacore.c ${SRC_LIST})
 target_link_libraries(mdacore -lbpf -lelf -lz -lm)
 
+add_executable(mdadump mdadump.c ${SRC_LIST})
+target_link_libraries(mdadump -lbpf -lelf -lz -lm)
+
 MESSAGE("======================Leave cli folder=======================")
diff --git a/oncn-mda/cli_src/func/dump.c b/oncn-mda/cli_src/func/dump.c
new file mode 100644
index 0000000..72a4916
--- /dev/null
+++ b/oncn-mda/cli_src/func/dump.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright Authors of Kmesh */
+
+#include <signal.h>
+#include "macli.h"
+#include "writecap.h"
+volatile sig_atomic_t g_keep_running = TRUE;
+
+static void init_filter_rule(struct sock_param *input)
+{
+    (void)memset(input, 0x0, sizeof(struct sock_param));
+}
+
+void dump_usage(void)
+{
+    printf("Usage: dump {OPTIONS}\n");
+    printf("       OPTIONS: -i|--ip:       filter ip,eg:1.1.1.1/24\n");
+    printf("       OPTIONS: -p|--ports:       filter ports,eg:15001~15006\n");
+    printf("       OPTIONS: -w|--write:       output file\n");
+}
+
+static const struct option g_dump_optionsp[] = {
+    {"ip",          required_argument, NULL, 'i'},
+    {"ports",       required_argument, NULL, 'p'},
+    {"writes",      required_argument, NULL, 'w'},
+    {NULL}
+};
+
+int get_input_dump_ip(const char* src, struct sock_param* filter_rules)
+{
+    if (filter_rules->dump_params.current_cidr_num >= MAX_PARAM_LENGTH) {
+        printf("over the max cidrs set num, max is %d\n", MAX_PARAM_LENGTH);
+        return FAILED;
+    }
+    char tmp_arg[MAX_CIDR_LENGTH] = {0};
+    (void)strncpy(tmp_arg, src, MAX_IP_LENGTH);
+    (void)strncat(tmp_arg, "/32", sizeof(tmp_arg) - strlen(tmp_arg));
+    __u32 ip;
+    __u32 mask;
+    if (check_cidr(tmp_arg, &ip, &mask) != SUCCESS)
+        return FAILED;
+    int current_param_num = filter_rules->dump_params.current_cidr_num++;
+    filter_rules->dump_params.dump_cidr[current_param_num].ip4 = ip;
+    filter_rules->dump_params.dump_cidr[current_param_num].mask = mask;
+    return SUCCESS;
+}
+
+int get_input_dump_port(const char* src, struct sock_param *filter_rules)
+{
+    if (strlen(src) > MAX_PORT_RANGE_LENGTH - 1) {
+        printf("input ports is too long! your input: %s\n", src);
+        return FAILED;
+    }
+    if (filter_rules->dump_params.current_port_num >= MAX_PARAM_LENGTH) {
+        printf("over the max ports set num, max is %d\n", MAX_PARAM_LENGTH);
+        return FAILED;
+    }
+    __u32 begin_port;
+    __u32 end_port;
+    if (check_port(src, &begin_port, &end_port) != SUCCESS) {
+        return FAILED;
+    }
+    int current_param_num = filter_rules->dump_params.current_port_num++;
+    filter_rules->dump_params.dump_port[current_param_num].begin_port = begin_port;
+    filter_rules->dump_params.dump_port[current_param_num].end_port = end_port;
+    return SUCCESS;
+}
+
+int dump_get_opt(int argc, char **argv, struct sock_param *input_dump_filter_rules, char *output_file)
+{
+    int opt;
+    optind = 1;
+    while ((opt = getopt_long(argc, argv, "i:p:w:", g_dump_optionsp, NULL)) >= 0) {
+        switch (opt) {
+            case 'i':
+                if (get_input_dump_ip(optarg, input_dump_filter_rules) != SUCCESS) 
+                    return FAILED;
+                break;
+            case 'p':
+                if (get_input_dump_port(optarg, input_dump_filter_rules) != SUCCESS) 
+                    return FAILED;
+                break;
+            case 'w': {
+                char dirBuff[PATH_MAX] = {0};
+                (void)strncpy(dirBuff, optarg, PATH_MAX - 1);
+                char *baseName = basename(optarg);
+                char *tmpPath = dirname(dirBuff);
+
+                if (realpath(tmpPath, output_file) == NULL) {
+                    printf("input file path %s error! errno:%d\n", tmpPath, errno);
+                    return FAILED;
+                }
+                (void)strncat(output_file, "/", PATH_MAX - strlen(output_file) - 1);
+                (void)strncat(output_file, baseName, PATH_MAX - strlen(output_file) - 1);
+                break;
+            }
+            case '?':
+            default:
+                dump_usage();
+                return FAILED;
+        }
+    }
+    if (optind != argc) {
+        printf("unknown param!\n");
+        dump_usage();
+        return FAILED;
+    }
+    return SUCCESS;
+}
+
+
+void sig_handler(int sig)
+{
+    if (sig == SIGINT) {
+        g_keep_running = FALSE;
+    }
+}
+
+int update_dump_param(struct mesh_map_info *param_map_info, struct sock_param *param_list)
+{
+    int key = 0;
+    if (bpf_map_update_elem(param_map_info->fd, &key, param_list, BPF_EXIST)) {
+        printf("update key ip failed! errno:%d\n", errno);
+        return FAILED;
+    }
+    return SUCCESS;
+}
+
+int init_dump(struct sock_param *param_list, struct mesh_map_info *dump_map_info, struct mesh_map_info *param_map_info)
+{
+    init_filter_rule(param_list);
+    init_mesh_map(dump_map_info, pinmap_file_path[MESH_MAP_OPS_DUMP_I_MAP],
+                  to_str(SOCK_DUMP_MAP_I_NAME), NULL);
+    init_mesh_map(param_map_info, pinmap_file_path[MESH_MAP_OPS_PARAM_MAP],
+                  to_str(SOCK_PARAM_MAP_NAME), NULL);
+
+    if (dump_map_info->fd < 0 || param_map_info->fd < 0) {
+        printf("can not found the pin file %s, %s, is the serviceMesh on?\n",
+                    dump_map_info->pin_file_path, param_map_info->pin_file_path);
+        return FAILED;
+    }
+
+    if (get_map_filter_rule(param_map_info, param_list) != SUCCESS)
+        return FAILED;
+    return SUCCESS;
+}
+
+void get_next_dump_data(int output_file, struct mesh_map_info *dump_map_info)
+{
+    struct dump_data dump_datas = {0};
+    if (bpf_map_lookup_and_delete_elem(dump_map_info->fd, NULL, &dump_datas) == 0) {
+        write_console(&dump_datas);
+    } else if (errno == ENOENT)
+        return;
+    else {
+        printf("get next dump message failed, errno:%d\n", errno);
+        g_keep_running = FALSE;
+    }
+}
+
+void close_dump_fd(struct mesh_map_info *dump_map_info, struct mesh_map_info *param_map_info)
+{
+    if (dump_map_info->fd > 0)
+        close((*dump_map_info).fd);
+    if (param_map_info->fd > 0)
+        close((*param_map_info).fd);
+}
+
+int do_dump(int argc, char **argv)
+{
+    int ret = FAILED;
+    g_keep_running = TRUE;
+    char output_file_name[PATH_MAX] = {0};
+    int output_file = FALSE;
+    if (signal(SIGINT, sig_handler) == SIG_ERR) {
+        printf("create the signal failed!\n");
+        return ERROR;
+    }
+
+    struct sock_param param_list;
+    struct mesh_map_info dump_map_info;
+    struct mesh_map_info param_map_info;
+    if (init_dump(&param_list, &dump_map_info, &param_map_info) != SUCCESS)
+        goto err;
+    
+    if (dump_get_opt(argc, argv, &param_list, output_file_name) != SUCCESS)
+        goto err;
+    
+    if (strcmp(output_file_name, "") != 0) {
+        printf("can not open the cap file\n");
+        goto err;
+    }
+
+    param_list.dump_params.switch_on = TRUE;
+    if (update_dump_param(&param_map_info, &param_list) != SUCCESS) {
+        printf("start dump failed!\n");
+        goto err;
+    }
+
+    while(g_keep_running == TRUE)
+        get_next_dump_data(output_file, &dump_map_info);
+
+    param_list.dump_params.switch_on = FALSE;
+    param_list.dump_params.current_cidr_num = 0;
+    param_list.dump_params.current_port_num = 0;
+    if (update_dump_param(&param_map_info, &param_list)) {
+        printf("stop dump failed!\n");
+        goto err;
+    }
+    ret = SUCCESS;
+err:
+    close_dump_fd(&dump_map_info, &param_map_info);
+    return ret;
+}
\ No newline at end of file
diff --git a/oncn-mda/cli_src/func/global.c b/oncn-mda/cli_src/func/global.c
index fc0a2b7..949a00d 100644
--- a/oncn-mda/cli_src/func/global.c
+++ b/oncn-mda/cli_src/func/global.c
@@ -177,7 +177,7 @@ static int get_map_fd(const char *const map_name, int *const fd)
     return SUCCESS;
 }
 
-static int init_mesh_map(
+int init_mesh_map(
     struct mesh_map_info *const fds_map,
     const char *const pin_file_path,
     const char *const map_name,
diff --git a/oncn-mda/cli_src/mdadump.c b/oncn-mda/cli_src/mdadump.c
new file mode 100644
index 0000000..0b27bf6
--- /dev/null
+++ b/oncn-mda/cli_src/mdadump.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include "dump.h"
+
+int main(int argc, char *argv[])
+{
+	return do_dump(argc, argv);
+}
diff --git a/oncn-mda/ebpf_src/sock_redirect.c b/oncn-mda/ebpf_src/sock_redirect.c
index 456afeb..78af95e 100644
--- a/oncn-mda/ebpf_src/sock_redirect.c
+++ b/oncn-mda/ebpf_src/sock_redirect.c
@@ -3,6 +3,100 @@
 
 #include "mesh_accelerate.h"
 
+static int filter_dump_ip(const struct sk_msg_md *const msg, const struct sock_param *const param)
+{
+    if (param->dump_params.current_cidr_num == 0)
+        return FILTER_PASS;
+
+    bpf_log(DEBUG, "filter param:sip:%u, dip:%u\n", msg->local_ip4, msg->remote_ip4);
+    __u32 local_ip4 = bpf_ntohl(msg->local_ip4);
+    __u32 remote_ip4 = bpf_ntohl(msg->remote_ip4);
+
+    for (int i = 0; i < MAX_PARAM_LENGTH; ++i) {
+        if (i >= param->dump_params.current_cidr_num)
+            break;
+        if (param->dump_params.dump_cidr[i].ip4 == local_ip4 ||
+            param->dump_params.dump_cidr[i].ip4 == remote_ip4)
+            return FILTER_PASS; 
+    }
+    return FILTER_RETURN;
+}
+
+static int adj_filter_dump_port(const struct port_range *const param, __u32 msg_sport, __u32 msg_dport)
+{
+    __u32 begin_port = param->begin_port;
+    __u32 end_port = param->end_port;
+    bpf_log(DEBUG, "filter param need:begin ports: %u, end ports:%u\n", begin_port, end_port);
+    if ((msg_sport >= begin_port && msg_sport <= end_port) ||
+        (msg_dport >= begin_port && msg_dport <= end_port))
+        return FILTER_PASS;
+    return FILTER_RETURN;
+}
+
+static int filter_dump_port(const struct sk_msg_md *const msg, const struct sock_param *const param)
+{
+    if (param->dump_params.current_port_num == 0)
+        return FILTER_PASS;
+
+    __u32 msg_sport = msg->local_port;
+    __u32 msg_dport = bpf_ntohl(msg->remote_port);
+
+    for (int i = 0; i < MAX_PARAM_LENGTH; ++i) {
+        if (i >= param->dump_params.current_port_num)
+            break;
+        if (adj_filter_dump_port(&(param->dump_params.dump_port[i]), msg_sport, msg_dport) == FILTER_PASS)
+            return FILTER_PASS;
+    }
+    return FILTER_RETURN;
+}
+
+static void get_dump_data(const struct sk_msg_md *const msg, struct dump_data *const data)
+{
+    __u64 current_timestamp = bpf_ktime_get_ns();
+    data->timestamp = current_timestamp;
+    data->sip = msg->local_ip4;
+    data->sport = (bpf_htonl(msg->local_port) >> FORMAT_IP_LENGTH);
+    data->dip = msg->remote_ip4;
+    data->dport = (force_read(msg->remote_port) >> FORMAT_IP_LENGTH);
+    data->data_length = msg->size;
+    bpf_probe_read_kernel(data->data, 4096, (void *)(long)msg->data);
+}
+
+static void dump_msg(const struct sk_msg_md *const msg)
+{
+    bpf_log(DEBUG, "begin dump\n");
+    int index = 0;
+    struct sock_param *param = bpf_map_lookup_elem(&SOCK_PARAM_MAP_NAME, &index);
+    if (param == NULL)
+        return;
+
+    if (param->dump_params.switch_on == FALSE)
+        return;
+
+    bpf_log(DEBUG, "dump");
+
+    if (filter_dump_ip(msg, param) != FILTER_PASS) {
+        bpf_log(DEBUG, "ip:%u, ports:%u filtered by ip\n", msg->local_ip4, msg->local_port);
+        return;
+    }
+
+    if (filter_dump_port(msg, param) != FILTER_PASS) {
+        bpf_log(DEBUG, "ip:%u, ports:%u filtered by port\n", msg->local_ip4, msg->local_port);
+        return;
+    }
+
+    struct dump_data *data = NULL;
+    data = bpf_map_lookup_elem(&SOCK_DUMP_CPU_ARRAY_NAME, &index);
+    if (data == NULL)
+        return;
+
+    get_dump_data(msg, data);
+
+    if (bpf_map_push_elem(&SOCK_DUMP_MAP_I_NAME, data, BPF_EXIST))
+        bpf_log(ERROR, "push the element failed!\n");
+    bpf_log(DEBUG, "dump success\n");
+}
+
 SEC("sk_msg")
 int SOCK_REDIRECT_NAME(struct sk_msg_md *const msg)
 {
@@ -23,7 +117,7 @@ int SOCK_REDIRECT_NAME(struct sk_msg_md *const msg)
         ret = bpf_msg_redirect_hash(msg, &SOCK_OPS_MAP_NAME, redir_key, BPF_F_INGRESS);
         if (ret != SK_DROP) {
             bpf_log(DEBUG, "redirect the sk success\n");
-
+            dump_msg(msg);
         } else {
             // If you connect to the peer machine, you do end up in this branch
             bpf_log(INFO, "no such socket, may be peer socket on another machine\n");
diff --git a/oncn-mda/include/data.h b/oncn-mda/include/data.h
index 951a3ab..7098ef5 100644
--- a/oncn-mda/include/data.h
+++ b/oncn-mda/include/data.h
@@ -15,6 +15,8 @@
 #define SK_BPF_GID_UID      18000
 #define BPF_SO_ORIGINAL_DST 800
 
+#define MAX_DUMP_DATA_LENGTH 4096
+
 #define SUCCESS 0
 #define FAILED  1
 
@@ -139,6 +141,7 @@ struct dump_data {
     __u32 dip;
     __u32 dport;
     __u32 data_length;
+    __u8  data[MAX_DUMP_DATA_LENGTH];
 };
 
 #pragma pack()
diff --git a/oncn-mda/include/dump.h b/oncn-mda/include/dump.h
new file mode 100644
index 0000000..b76f474
--- /dev/null
+++ b/oncn-mda/include/dump.h
@@ -0,0 +1,6 @@
+#ifndef __DUMP_H__
+#define __DUMP_H__
+
+int do_dump(int argc, char *argv[]);
+
+#endif
\ No newline at end of file
diff --git a/oncn-mda/include/macli.h b/oncn-mda/include/macli.h
index 1db7e7a..cd0dcd7 100644
--- a/oncn-mda/include/macli.h
+++ b/oncn-mda/include/macli.h
@@ -101,6 +101,9 @@ struct mesh_service_info {
     struct mesh_prog_info prog_fds[MESH_PROG_NUM];
 };
 
+extern const char pinmap_file_path[][PATH_MAX];
+extern const char pinprog_file_path[][PATH_MAX];
+
 // global.c
 int check_cidr(const char *const src, __u32 *const ip, __u32 *const mask);
 int check_port(const char *const src, __u32 *const begin_port, __u32 *const end_port);
@@ -120,4 +123,13 @@ void close_fds(int cgroup_fd, const struct mesh_service_info *const fds);
 
 // chain.c
 int do_chain(int argc, char *const *argv, struct sock_param *const filter_rules);
+int init_mesh_map(
+        struct mesh_map_info *const fds_map,
+        const char *const pin_file_path,
+        const char *const map_name,
+#if LIBBPF_HIGHER_0_6_0_VERSION
+        struct create_map_attr *const map_attr);
+#else
+        struct bpf_create_map_attr *const map_attr);
+#endif
 #endif
diff --git a/oncn-mda/include/writecap.h b/oncn-mda/include/writecap.h
new file mode 100644
index 0000000..2aabc82
--- /dev/null
+++ b/oncn-mda/include/writecap.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/* Copyright Authors of Kmesh */
+
+#include "data.h"
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <libgen.h>
+
+#include <securec.h>
+#include <arpa/inet.h>
+#include <sys/sysinfo.h>
+#include <stdio.h>
+#include <time.h>
+
+#define MAC_LENGTH 6
+#define MAX_IP_PORT_LENGTH 32
+#define MAX_IP_LENGTH 16
+const int NANO_2_SECOND_RATE = 1000000000;
+
+int get_sys_time(__u64 ktime, time_t *dataTime)
+{
+    struct sysinfo info;
+    time_t boot_time;
+    if (sysinfo(&info)) {
+        return FAILED;
+    }
+    time_t cur_time = time(NULL);
+    if (cur_time > info.uptime)
+        boot_time = cur_time - info.uptime;
+    else
+        boot_time = info.uptime - cur_time;
+    *dataTime = boot_time + ktime / NANO_2_SECOND_RATE;
+    return SUCCESS; 
+}
+
+int format_ip(__u32 ip4, char *ip, int ipSize)
+{
+    struct in_addr src;
+    src.s_addr = ip4;
+    if (inet_ntop(AF_INET, &src, ip, ipSize) == NULL) {
+        printf("can not convert the ip:%d\n", ip4);
+        return FAILED;
+    }
+    return SUCCESS;
+}
+
+int format_ip_port(__u32 ip4, __u32 port, char *ip, int ipSize)
+{
+    char tmp_ip[MAX_IP_LENGTH] = {0};
+
+    if (format_ip(ip4, tmp_ip, MAX_IP_LENGTH) != SUCCESS)
+        return FAILED;
+    (void)snprintf(ip, ipSize - 1, "%s:%u", tmp_ip, port);
+    return SUCCESS;
+}
+
+void write_console(const struct dump_data *data)
+{
+    time_t print_time;
+
+    char srcIP[MAX_IP_PORT_LENGTH] = {0};
+    char dstIP[MAX_IP_PORT_LENGTH] = {0};
+    format_ip_port(data->sip, ntohl(data->sport << 16), srcIP, sizeof(srcIP));
+    format_ip_port(data->dip, ntohl(data->dport << 16), dstIP, sizeof(dstIP));
+    if (get_sys_time(data->timestamp, &print_time) == FAILED)
+        printf("[%llu],ip:%s > %s\n", data->timestamp, srcIP, dstIP);
+    else {
+        struct tm print_tm;
+        localtime_r(&print_time, &print_tm);
+        printf("[%d-%d-%d %d:%d:%d],ip:%s > %s, data length:%d\n",
+               print_tm.tm_year + 1900, print_tm.tm_mon + 1, print_tm.tm_mday,
+               print_tm.tm_hour, print_tm.tm_min, print_tm.tm_sec,
+               srcIP, dstIP, data->data_length);
+        int print_length = data->data_length <= MAX_DUMP_DATA_LENGTH ? data->data_length : MAX_DUMP_DATA_LENGTH;
+        for (int i = 0; i < print_length; i++) {
+            printf("%c", data->data[i]);
+        }
+        printf("\n");
+    }
+}
-- 
2.33.0

