diff --git a/MAINTAINERS b/MAINTAINERS index f329b6ac04cb3b910c18d0d922c46e7a1aba42d1..b2cad7532459b7cd4680e541018c75913aa7a22d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14778,6 +14778,13 @@ S: Supported F: Documentation/networking/device_drivers/ethernet/neterion/s2io.rst F: drivers/net/ethernet/neterion/ +NETTRACE +M: Peilin He +M: xu xin +L: netdev@vger.kernel.org +S: Maintained +F: net/nettrace/ + NETFILTER M: Pablo Neira Ayuso M: Jozsef Kadlecsik diff --git a/net/Kconfig b/net/Kconfig index 092a1c0902ac9205ad25831cf09be5064786be6e..e9600faa440432a05e071606c9813c97639ba2ea 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -422,6 +422,7 @@ source "net/ceph/Kconfig" source "net/nfc/Kconfig" source "net/psample/Kconfig" source "net/ife/Kconfig" +source "net/nettrace/Kconfig" config LWTUNNEL bool "Network light weight tunnels" diff --git a/net/Makefile b/net/Makefile index 45f3fbaae644e167fde0e2b63b935a21745f0fe7..0c8990098eb97484ab0feef3e4548807b3c8af2b 100644 --- a/net/Makefile +++ b/net/Makefile @@ -80,3 +80,4 @@ obj-$(CONFIG_XDP_SOCKETS) += xdp/ obj-$(CONFIG_MPTCP) += mptcp/ obj-$(CONFIG_MCTP) += mctp/ obj-$(CONFIG_NET_HANDSHAKE) += handshake/ +obj-$(CONFIG_NET_TRACE) += nettrace/ diff --git a/net/nettrace/Kconfig b/net/nettrace/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..802e8caa316986bae4654265dbfa16ed09a2336b --- /dev/null +++ b/net/nettrace/Kconfig @@ -0,0 +1,11 @@ +config NET_TRACE + tristate "Net trace" + depends on KPROBES + default n + help + Trace net package from the kernel network proto stack.Nettrace is + a new kernel module for network packet capture and tracking.It can + capture packets from common protocol stacks and various points within + the kernel, detect abnormal packets, and print kernel and user-mode + stacks at tracepoints. It is designed to be scalable and to have + minimal impact on system performance. diff --git a/net/nettrace/Makefile b/net/nettrace/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..66c103b20591543e23c53cfa5aef0ede9e9ee350 --- /dev/null +++ b/net/nettrace/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for nettrace. +# +nettrace-objs := core.o kprobe.o parser.o dump.o help.o group.o handler.o procfs.o utils.o mm.o +obj-$(CONFIG_NET_TRACE) += nettrace.o diff --git a/net/nettrace/Makefile.alone b/net/nettrace/Makefile.alone new file mode 100644 index 0000000000000000000000000000000000000000..5f9a0aae28bfefd5aa764ec01be0ce6769429cdf --- /dev/null +++ b/net/nettrace/Makefile.alone @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile. +# + +nettrace-objs := core.o kprobe.o parser.o dump.o help.o group.o handler.o procfs.o utils.o +obj-m := ntrace.o + +all: clean build + +build: + make -C $(KERNEL_DIR) M=$(shell pwd) modules + +clean: + make -C $(KERNEL_DIR) M=$(shell pwd) clean + +.PHONY: build diff --git a/net/nettrace/core.c b/net/nettrace/core.c new file mode 100644 index 0000000000000000000000000000000000000000..8540463bdd3b38bcdd9536c6c8a0fc85e3c63753 --- /dev/null +++ b/net/nettrace/core.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "core.h" +#include "help.h" +#include "group.h" +#include "procfs.h" + +enum nettrace_status nt_status = NT_INIT; + +#define MAX_DUMP_QUEUE_MEM_DEFAULT (SK_RMEM_MAX * 200) +int max_dump_queue_mem = MAX_DUMP_QUEUE_MEM_DEFAULT; +#define MAX_DUMP_SKB_CNT_DEFAULT (100 * 1000) +unsigned int max_dump_skb_cnt = MAX_DUMP_SKB_CNT_DEFAULT; +#define MAX_DUMP_FILE_SIZE_DEFAULT (100 * 1024 * 1024) +unsigned int max_dump_file_size = MAX_DUMP_FILE_SIZE_DEFAULT; + +MODULE_DESCRIPTION("Network debugging tools."); +/*module init.*/ +KPROBE_INIT { + int err = -EINVAL; + + init_group(); + err = init_args(); + if (err) + goto on_err; + + err = trace_register(); + if (err) + goto on_init_err; + + err = ntrace_proc_init(); + if (err) + goto on_init_err; + + WRITE_ONCE(nt_status, NT_RUNNING); + return 0; + +on_init_err: + free_all_group(); +on_err: + return err; +} + +/*module exit*/ +KPROBE_EXIT { + WRITE_ONCE(nt_status, NT_EXITING); + /* Now start to free all tracepoints */ + free_all_group(); + free_rules(); + ntrace_proc_exit(); +} diff --git a/net/nettrace/core.h b/net/nettrace/core.h new file mode 100644 index 0000000000000000000000000000000000000000..c28330d55da67a466b6c59fd1e6ceac475350252 --- /dev/null +++ b/net/nettrace/core.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * Nettrace support. + * Author: + * Menglong Dong + * Migrator: + * xu xin + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETDUMP_CORE_H +#define NETDUMP_CORE_H + +#include "kprobe.h" +#include "parser.h" + +/* Internal status of nettrace */ +enum nettrace_status { + /* nettrace is initializing related tracepoints and its dump files */ + NT_INIT, + /* nettrace is ready and tracing */ + NT_RUNNING, + /* somebody is rmmoving the nettrace.ko */ + NT_EXITING +}; + +extern enum nettrace_status nt_status; + +#endif //NETDUMP_CORE_H diff --git a/net/nettrace/dump.c b/net/nettrace/dump.c new file mode 100644 index 0000000000000000000000000000000000000000..efc687bcb636bb7dce1c9b245487d0f2d289abd3 --- /dev/null +++ b/net/nettrace/dump.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "dump.h" +#include "utils.h" +#include "group.h" +#include "core.h" +#include "mm.h" + +/* The number of packets which is not dumped into pcap due to insufficient memory. */ +unsigned int dump_loss_due_to_no_memory; + +/* The number of packets which is not dumped into pcap due to the limit of max_dump_skb_cnt. */ +unsigned int dump_skb_over_cnt; + +/* The number of packets which is not dumped into pcap due to the limit of max_dump_file_size. */ +unsigned int dump_skb_over_size; + +static int dump_skb(struct sk_buff_nettrace *skb, struct file *to) +{ + struct pcap_pkthdr phdr; + struct timespec64 tv; + struct sk_buff *frag, *tmp; + + ktime_get_real_ts64(&tv); + phdr.ts.tv_sec = tv.tv_sec; + phdr.ts.tv_usec = tv.tv_nsec / 1000; + + phdr.len = skb->total_len; + phdr.caplen = skb->total_len; + + file_append(to, &phdr, sizeof(phdr)); + file_append(to, skb->data, skb->len); + + if (!skb_queue_empty(&skb->frag_list)) { + skb_queue_walk_safe(&skb->frag_list, frag, tmp) + file_append(to, ((struct sk_buff_nettrace *)frag)->data, + ((struct sk_buff_nettrace *)frag)->len); + } + + return 0; +} + +int init_pcap(struct file *f) +{ + struct pcap_file_header hdr; + + hdr.magic = PCAP_MAGIC; + hdr.version_major = PCAP_VERSION_MAJOR; + hdr.version_minor = PCAP_VERSION_MINOR; + hdr.thiszone = sys_tz.tz_dsttime; + hdr.sigfigs = 0; + hdr.snaplen = DEFAULT_SNAPLEN; + hdr.linktype = LINKTYPE_ETHERNET; + + file_append(f, &hdr, sizeof(hdr)); + + return 0; +} + +static __always_inline void dump_queue_lock(struct sk_buff_head *dump_queue, unsigned long flag) +{ + spin_lock_irqsave(&dump_queue->lock, flag); +} + +static __always_inline void dump_queue_unlock(struct sk_buff_head *dump_queue, unsigned long flag) +{ + spin_unlock_irqrestore(&dump_queue->lock, flag); +} + +static void dump_skb_work(struct work_struct *work) +{ + struct trace_point *tp = container_of(work, struct trace_point, dump_work); + + struct sk_buff_nettrace *skb; + struct sk_buff_head list; + unsigned long flag = 0; + + __skb_queue_head_init(&list); + + dump_queue_lock(&tp->dump_queue, flag); + skb_queue_splice_tail_init(&tp->dump_queue, &list); + dump_queue_unlock(&tp->dump_queue, flag); + + while ((skb = (struct sk_buff_nettrace *) __skb_dequeue(&list))) { + + /* Don't waste time on writing dump_file when exit */ + if (likely(READ_ONCE(nt_status) != NT_EXITING)) { + dump_skb(skb, tp->dump_file); + tp->dump_cnt++; + } + + release_skb_nettrace(skb, tp); + } +} + +void try_dump_skb(struct sk_buff *skb, struct trace_point *tp) +{ + struct sk_buff_nettrace *new_skb; + unsigned long flag; + + /* if dump_skb_cnt exceed the uppper limit, drop it */ + if (tp->dump_cnt > max_dump_skb_cnt) { + dump_skb_over_cnt++; + return; + } + + /* if dump_file_size exceed the uppper limit, drop it */ + if (tp->dump_file->f_pos + skb->truesize + + sizeof(struct pcap_pkthdr) >= max_dump_file_size) { + dump_skb_over_size++; + return; + } + + new_skb = skb_copy_nettrace(skb, tp); + + if (!new_skb) { + dump_loss_due_to_no_memory++; + return; + } + + dump_queue_lock(&tp->dump_queue, flag); + __skb_queue_tail(&tp->dump_queue, (struct sk_buff *)new_skb); + dump_queue_unlock(&tp->dump_queue, flag); + + schedule_work(&tp->dump_work); +} + +void init_dump_work(struct trace_point *tp) +{ + INIT_WORK(&tp->dump_work, dump_skb_work); + skb_queue_head_init(&tp->dump_queue); +} diff --git a/net/nettrace/dump.h b/net/nettrace/dump.h new file mode 100644 index 0000000000000000000000000000000000000000..69ee718ee3ee9425d6bf86059d33469d858fd21c --- /dev/null +++ b/net/nettrace/dump.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETDUMP_DUMP_H +#define NETDUMP_DUMP_H + +#include +#include "group.h" + +#define PCAP_MAGIC 0xa1b2c3d4 +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 + +#define DEFAULT_SNAPLEN 0x40000 + +#define LINKTYPE_NULL 0 +#define LINKTYPE_ETHERNET 1 /* also for 100Mb and up */ +#define LINKTYPE_EXP_ETHERNET 2 /* 3Mb experimental Ethernet */ +#define LINKTYPE_AX25 3 +#define LINKTYPE_PRONET 4 +#define LINKTYPE_CHAOS 5 +#define LINKTYPE_TOKEN_RING 6 /* DLT_IEEE802 is used for Token Ring */ +#define LINKTYPE_ARCNET 7 +#define LINKTYPE_SLIP 8 +#define LINKTYPE_PPP 9 +#define LINKTYPE_FDDI 10 +#define LINKTYPE_PPP_HDLC 50 /* PPP in HDLC-like framing */ +#define LINKTYPE_PPP_ETHER 51 /* NetBSD PPP-over-Ethernet */ +#define LINKTYPE_ATM_RFC1483 100 /* LLC/SNAP-encapsulated ATM */ +#define LINKTYPE_RAW 101 /* raw IP */ +#define LINKTYPE_SLIP_BSDOS 102 /* BSD/OS SLIP BPF header */ +#define LINKTYPE_PPP_BSDOS 103 /* BSD/OS PPP BPF header */ +#define LINKTYPE_C_HDLC 104 /* Cisco HDLC */ +#define LINKTYPE_IEEE802_11 105 /* IEEE 802.11 (wireless) */ +#define LINKTYPE_ATM_CLIP 106 /* Linux Classical IP over ATM */ +#define LINKTYPE_LOOP 108 /* OpenBSD loopback */ +#define LINKTYPE_LINUX_SLL 113 /* Linux cooked socket capture */ +#define LINKTYPE_LTALK 114 /* Apple LocalTalk hardware */ +#define LINKTYPE_ECONET 115 /* Acorn Econet */ +#define LINKTYPE_CISCO_IOS 118 /* For Cisco-internal use */ +#define LINKTYPE_PRISM_HEADER 119 /* 802.11+Prism II monitor mode */ +#define LINKTYPE_AIRONET_HEADER 120 /* FreeBSD Aironet driver stuff */ + +struct pcap_file_header { + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + int32_t thiszone; /* gmt to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length saved portion of each pkt */ + uint32_t linktype; /* data link type (LINKTYPE_*) */ +}; + +struct timeval_compat { + uint32_t tv_sec; /* seconds */ + uint32_t tv_usec; /* microseconds */ +}; + +struct pcap_pkthdr { + struct timeval_compat ts; /* time stamp using 32 bits fields */ + uint32_t caplen; /* length of portion present */ + uint32_t len; /* length this packet (off wire) */ +}; + +extern unsigned int dump_loss_due_to_no_memory; +extern unsigned int max_dump_skb_cnt, max_dump_file_size, dump_skb_over_cnt, dump_skb_over_size; + +extern void try_dump_skb(struct sk_buff *skb, struct trace_point *tp); + +extern int init_pcap(struct file *f); + +extern void init_dump_work(struct trace_point *tp); + +#endif //NETDUMP_DUMP_H diff --git a/net/nettrace/group.c b/net/nettrace/group.c new file mode 100644 index 0000000000000000000000000000000000000000..2dc1b70f8239e218ead9616e0b5b8c57cc77595f --- /dev/null +++ b/net/nettrace/group.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "group.h" +#include "utils.h" +#include "kprobe.h" +#include "help.h" +#include "handler.h" +#include "dump.h" +#include "mm.h" + +/* The kernel function that we interest in. Normally, they can be divided into + * four part: ethernet II, IP layer, udp layer, tcp layer and some common + * function, such as kfree_skb(). + */ +struct trace_point all_tp[] = { + +#define SKB_TP(i, g, n) {.skb_index = i, .groups = g, .name = n} +#define PSKB_TP(i, g, n) {.pskb_index = i, .groups = g, .name = n} +#define SK_TP(i, g, n) {.sock_index = i, .groups = g, .name = n} +#define SS_TP(skb, sk, g, n) \ + {.skb_index = skb, .sock_index = sk, .groups = g, .name = n} + + /* net link layout trace points. */ + PSKB_TP(1, "link_input", "__netif_receive_skb_core"), + SKB_TP(2, "link_input", "napi_gro_receive"), + SKB_TP(1, "link_input", "netif_receive_skb_internal"), + SKB_TP(1, "link_input", "__netif_receive_skb"), + SKB_TP(1, "link_input", "netif_rx"), + SKB_TP(1, "link_input", "enqueue_to_backlog"), + SKB_TP(1, "link_output", "__dev_queue_xmit"), + SKB_TP(1, "link_output", "dev_hard_start_xmit"), + SKB_TP(1, "link_output", "dev_queue_xmit_accel"), + SKB_TP(2, "link_output", "dev_forward_skb"), + SKB_TP(1, "link_output", "skb_do_redirect"), + //tc_classify is discarded in Linux 4.19 and later version. + //{.skb_index = 1, .groups = "link_input", .name = "tc_classify", .is_ret = true}, + + /* ip layout trace points. */ + SKB_TP(1, "ip_input", "ip_rcv"), + SKB_TP(3, "ip_input", "ip_rcv_finish"), + SKB_TP(1, "ip_input", "ip_route_input_noref"), + {.skb_index = 1, .groups = "ip_input", .name = "fib_validate_source", .is_ret = true}, + SKB_TP(2, "ip_input", "ip_rcv_finish_core"), + SKB_TP(1, "ip_input", "ip_local_deliver"), + SKB_TP(3, "ip_input", "ip_local_deliver_finish"), + SKB_TP(1, "ip_input", "ip_forward"), + SKB_TP(3, "ip_input", "ip_forward_finish"), + SKB_TP(2, "ip_input", "ip_send_skb"), + SS_TP(3, 2, "ip_output", "__ip_local_out"), + SKB_TP(3, "ip_output", "ip_output"), + SKB_TP(3, "ip_output", "ip_finish_output"), + SKB_TP(3, "ip_output", "ip_finish_output2"), + + /* udp layout trace points. */ + SKB_TP(1, "udp_input", "__udp4_lib_rcv"), + SKB_TP(2, "udp_input", "udp_queue_rcv_skb"), + SKB_TP(2, "udp_input", "__udp_enqueue_schedule_skb"), + SK_TP(1, "udp_input", "__skb_recv_udp"), + SK_TP(1, "udp_input", "udp_recvmsg"), + + /* tcp layout trace points. */ + SKB_TP(1, "tcp_input", "tcp_v4_rcv"), + SS_TP(2, 1, "tcp_input", "tcp_v4_do_rcv"), + SS_TP(2, 1, "tcp_input", "tcp_rcv_established"), + SS_TP(2, 1, "tcp_input", "tcp_rcv_state_process"), + SS_TP(2, 1, "tcp_input", "tcp_data_queue"), + SS_TP(2, 1, "tcp_input", "tcp_queue_rcv"), + SK_TP(1, "tcp_input", "tcp_recvmsg"), + + SK_TP(1, "tcp_output", "tcp_sendmsg"), + SK_TP(1, "tcp_output", "tcp_push"), + SK_TP(1, "tcp_output", "tcp_write_xmit"), + SK_TP(1, "tcp_output", "tcp_set_state"), + SS_TP(2, 1, "tcp_output", "__tcp_transmit_skb"), + + + /* common skb trace points. */ + SKB_TP(1, "error", "kfree_skb_reason"), + SKB_TP(1, "normal", "consume_skb"), + + /* Compile when VNet MACVLAN=y */ + /* macvlan drop stat. */ + SKB_TP(1, "macvlan", "macvlan_start_xmit"), + SKB_TP(1, "macvlan", "macvlan_handle_frame"), + +#undef SKB_TP +#undef SK_TP +#undef SS_TP +}; +const int all_tp_len = sizeof(all_tp) / sizeof(struct trace_point); +struct trace_group *all_group; + +static struct trace_group *query_group(char *name, struct trace_group *tp); + +/******************************************************************************************* + * + * This is the part for trace point and group query. + * + *******************************************************************************************/ + +static struct trace_group +*parent_group(struct trace_group *tg, struct trace_group *parent) +{ + struct trace_group *tmp, *tmp2; + + list_for_each_entry(tmp, &parent->groups, list) { + if (tmp == tg) + return parent; + tmp2 = parent_group(tg, tmp); + if (tmp2 != NULL) + return tmp2; + } + return NULL; +} + +static inline +struct trace_group *parent_group_all(struct trace_group *tg) +{ + return parent_group(tg, all_group); +} + +static struct trace_point *query_tp(char *name) +{ + int i = 0; + + for (; i < all_tp_len; i++) { + if (streq(name, all_tp[i].name)) + return &all_tp[i]; + } + return NULL; +} + +static void *query_handler(struct trace_group *tg) +{ + if (tg == NULL) + return all_group->handler; + + if (tg->handler) + return tg->handler; + + return query_handler(parent_group_all(tg)); +} + +static void *query_ret_handler(struct trace_group *tg) +{ + if (tg == NULL) + return all_group->ret_handler; + + if (tg->ret_handler) + return tg->ret_handler; + + return query_ret_handler(parent_group_all(tg)); +} + +static struct trace_group +*query_group(char *name, struct trace_group *tp) +{ + struct trace_group *pos, *tmp; + + if (streq(tp->name, name)) + return tp; + + list_for_each_entry(pos, &tp->groups, list) { + tmp = query_group(name, pos); + if (tmp != NULL) + return tmp; + } + return NULL; +} + +/************************************************************************************** + * + * This is the part for trace point register. + * + **************************************************************************************/ + +static int +trace_point_register(struct trace_point *tp, struct trace_group *tg) +{ + char path[MAX_FILE_NAME] = {}; + struct kprobe *p; + struct file *dump_file; + + if (tp->kprobe) + return 0; + + if (tp->skb_index <= 0 && tp->sock_index <= 0 && tp->pskb_index <= 0) { + log_err("kprobe %s has no index!\n", tp->name); + goto out_err; + } + + if (tp->is_ret) { + p = (struct kprobe *) kretprobe_declare(tp->name, + query_ret_handler(tg), + entry_handler_general); + if (!p) + goto out_err; + ((struct kretprobe *)p)->data_size = sizeof(struct ret_data); + } else + p = kprobe_declare(tp->name, query_handler(tg)); + + if (!p) { + log_err("kprobe declare failed: %s\n", tp->name); + goto out_err; + } + + /* NOTE: + * When register kprobe, the given SYMBOL NAME may be not found in kallsyms + * for example, suppose we want to trace the kernel func: + * + * "__netif_receive_skb_core", + * + * HOWEVER, that symbol name might be changed by compilers into + * + * "__netif_receive_skb_core.constprop.0" + * + * So, we finally trace __netif_receive_skb_core.constprop.0 and use it as + * tp->name instead of __netif_receive_skb_core! + */ + if ((tp->is_ret && !c_register_kretprobe((struct kretprobe *) p)) || + (!tp->is_ret && !c_register_kprobe(p))) + tp->kprobe = p; + else { + if (strcmp(tp->name, "macvlan_start_xmit") == 0 || + strcmp(tp->name, "macvlan_handle_frame") == 0) { + log_err("Please confirm if the macvlan module is inserted, no %s\n", + tp->name); + } else { + log_err("kprobe register failed: %s\n", tp->name); + } + goto out_free_err; + } + + if (print_dump) { + snprintf(path, sizeof(path), "%s/%s.pcap", print_dump, tp->name); + dump_file = file_create(path); + if (!dump_file) { + log_err("failed to create dump file: %s\n", path); + goto out_err; + } + init_pcap(dump_file); + init_dump_work(tp); + init_dump_mm(tp); + /* Add write barrier to avoid the compiler instruction recombination. + * We must guarantee that tp->dump_file is init after init_dump_work. + */ + wmb(); + tp->dump_file = dump_file; + } + + return 0; +out_free_err: + kfree(p); +out_err: + return -1; +} + +static void trace_reg_group(struct trace_group *tg) +{ + struct trace_group *tmp_tg; + struct tp_list *tpl; + + log_info("begin register group: %s\n", tg->name); + + list_for_each_entry(tpl, &tg->traces, list) trace_point_register(tpl->tp, tg); + + list_for_each_entry(tmp_tg, &tg->groups, list) trace_reg_group(tmp_tg); + + log_info("end register group: %s\n", tg->name); +} + +/*register the kprobe that defined in 'global_kprobe_list'.*/ +int trace_register(void) +{ + int t = 0; + + for (; t < filter_trace_len; t++) { + char *ft = filter_trace[t]; + + struct trace_group *tg = query_group(ft, all_group); + + if (!tg) { + log_err("trace: %s not founded!\n", ft); + return -EINVAL; + } + trace_reg_group(tg); + } + + for (t = 0; t < filter_probe_len; t++) { + char *name = filter_probe[t]; + + struct trace_point *tp = query_tp(name); + + if (!tp) { + log_err("probe: %s not founded!\n", name); + return -EINVAL; + } + trace_point_register(tp, all_group); + } + + return 0; +} + +/******************************************************************************************* + * + * This is the part for trace group functions. + * + * In fact, trace group is organized in form of tree. + * + *******************************************************************************************/ + +/* add trace point to group. + */ +static int add2group(struct trace_point *tp, char *groups) +{ + char *group; + struct tp_list *tpl; + struct trace_group *g; + + while ((group = strsep(&groups, ",")) != NULL) { + g = query_group(group, all_group); + if (!g) { + log_err("group: %s not exits!", group); + continue; + } + tpl = kmalloc(sizeof(struct tp_list), GFP_KERNEL); + if (!tpl) + return -ENOMEM; + memset(tpl, 1, sizeof(struct tp_list)); + + tpl->tp = tp; + INIT_LIST_HEAD(&tpl->list); + list_add_tail(&tpl->list, &g->traces); + } + + return 0; +} + +static void init_tp(void) +{ + struct trace_point *tp; + int i = 0; + + for_each_tp(i, tp) add2group(tp, tp->groups); +} + +static struct trace_group +*add_trace_group(char *name, char *desc, enum trace_group_level lev, + struct trace_group *parent) +{ + struct trace_group *tmp_tp = kmalloc(sizeof(struct trace_group), GFP_KERNEL); + + if (!tmp_tp) + return NULL; + memset(tmp_tp, 0, sizeof(struct trace_group)); + + INIT_LIST_HEAD(&tmp_tp->list); + INIT_LIST_HEAD(&tmp_tp->groups); + INIT_LIST_HEAD(&tmp_tp->traces); + + strscpy(tmp_tp->name, name, MAX_TP_NAME); + strscpy(tmp_tp->desc, desc, MAX_TP_DESC); + tmp_tp->level = lev; + + if (parent != NULL) + list_add_tail(&tmp_tp->list, &parent->groups); + else + all_group = tmp_tp; + + return tmp_tp; +} + +#define ADD_TRACE_GROUP(name, lev, parent, desc) \ + (name = add_trace_group(#name, desc, lev, parent)) +#define ADD_ANNOY_TRACE_GROUP(name, lev, parent, desc) \ + add_trace_group(#name, desc, lev, parent) + +void init_group(void) +{ + /* define all trace groups. */ + struct trace_group *all, *link, *ip, *tcp, *udp, *error, *macvlan; + + ADD_TRACE_GROUP(all, BASIC, NULL, "the root trace"); + ADD_TRACE_GROUP(link, BASIC, all, "the link layer."); + ADD_TRACE_GROUP(ip, BASIC, all, "the ip layer"); + ADD_TRACE_GROUP(tcp, BASIC, all, "the tcp layer"); + ADD_TRACE_GROUP(udp, BASIC, all, "the udp layer"); + ADD_TRACE_GROUP(error, BASIC, all, "the scene that free error package."); + ADD_TRACE_GROUP(macvlan, BASIC, all, "macvlan receive and send skb."); + + /* for kw scan. */ + if (!all) + return; + all->handler = post_handler_general; + all->ret_handler = ret_handler_general; + + /* general network protocol stack. */ + ADD_ANNOY_TRACE_GROUP(link_input, BASIC, link, "the link layer that receive package"); + ADD_ANNOY_TRACE_GROUP(link_output, BASIC, link, "the link layer that send package"); + + ADD_ANNOY_TRACE_GROUP(ip_input, BASIC, ip, "the ip layer that receive package"); + ADD_ANNOY_TRACE_GROUP(ip_output, BASIC, ip, "the ip layer that send package"); + + ADD_ANNOY_TRACE_GROUP(tcp_input, BASIC, tcp, "the tcp layer that receive package"); + ADD_ANNOY_TRACE_GROUP(tcp_output, BASIC, tcp, "the tcp layer that send package"); + + ADD_ANNOY_TRACE_GROUP(udp_input, BASIC, udp, "the udp layer that receive package"); + + ADD_ANNOY_TRACE_GROUP(normal, BASIC, all, "the scene that free normal package."); + + init_tp(); +} + +static void free_group(struct trace_group *tg) +{ + struct tp_list *tp_pos, *tp_next; + struct trace_group *pos, *next; + struct trace_point *tp; + + list_for_each_entry_safe(pos, next, &tg->groups, list) free_group(pos); + + list_for_each_entry_safe(tp_pos, tp_next, &tg->traces, list) { + tp = tp_pos->tp; + kfree(tp_pos); + + if (tp->kprobe) { + if (tp->is_ret) + c_unregister_kretprobe((struct kretprobe *) tp->kprobe); + else + c_unregister_kprobe(tp->kprobe); + kfree(tp->kprobe); + tp->kprobe = NULL; + } + + /* Dump skb work must be done before tp is removed */ + if (print_dump && tp->dump_work.func) + flush_work(&tp->dump_work); + + if (tp->dump_file) { + file_close(tp->dump_file); + tp->dump_file = NULL; + release_dump_mm(tp); + } + } + kfree(tg); +} + +void free_all_group(void) +{ + free_group(all_group); +} diff --git a/net/nettrace/group.h b/net/nettrace/group.h new file mode 100644 index 0000000000000000000000000000000000000000..dc5c80e74d8d829921d60fb01d749c40ed63866b --- /dev/null +++ b/net/nettrace/group.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETDUMP_GROUP_H +#define NETDUMP_GROUP_H + +#include +#include +#include "mm.h" + +#define MAX_TP_NAME KSYM_NAME_LEN +/* The length of group name */ +#define MAX_TP_GROUP 128 +/* The length of TP description */ +#define MAX_TP_DESC 256 + +#define for_each_tp(i, p) \ + for (i = 0; i < all_tp_len && ({ p = &all_tp[i]; 1; }); i++) + +/*Definition of kprobe point that we predefined*/ +struct trace_point { + /* The traced function name */ + char name[MAX_TP_NAME]; + /* To define the position index of struct sk_buff *skb in a certain + * trace_point function + */ + int skb_index; + /* To define the position index of struct sk_buff **pskb in a certain + * trace_point function + */ + int pskb_index; + + int sock_index; + /* Count the number of skb when dumpping in the current TP. */ + unsigned int dump_cnt; + char desc[MAX_TP_DESC]; + char groups[MAX_TP_GROUP]; + bool is_ret; + struct kprobe *kprobe; + struct file *dump_file; + struct work_struct dump_work; + struct sk_buff_head dump_queue; + struct llist_head skbcache; + int nr_skb_objs; + struct llist_head datacache; + int nr_data_objs; +}; + +struct tp_list { + struct trace_point *tp; + struct list_head list; +}; + +enum trace_group_level { + BASIC, + MOD +}; + +struct kretprobe_instance; + +struct ret_data { + struct sk_buff *skb; + struct sock *sk; +}; + +struct trace_group { + char name[MAX_TP_NAME]; + char desc[MAX_TP_DESC]; + enum trace_group_level level; + struct list_head list; + struct list_head groups; + struct list_head traces; + bool activated; + + void (*handler)(struct kprobe *p, + struct pt_regs *regs, + unsigned long flags); + int (*ret_handler)(struct kretprobe_instance *ri, + struct pt_regs *regs); +}; + +extern const int all_tp_len; +extern struct trace_point all_tp[]; + +extern struct trace_group *all_group; + +extern void init_group(void); + +extern int trace_register(void); + +extern void free_all_group(void); + +#endif //NETDUMP_GROUP_H diff --git a/net/nettrace/handler.c b/net/nettrace/handler.c new file mode 100644 index 0000000000000000000000000000000000000000..499c902ffae3b06195e95c211a64608e77e0bcf4 --- /dev/null +++ b/net/nettrace/handler.c @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "handler.h" +#include "kprobe.h" +#include "group.h" +#include "parser.h" +#include "help.h" +#include "dump.h" +#include "utils.h" +#include "core.h" + +/******************************************************************************************* + * + * This is the part for filter, by skb or sock + * + *******************************************************************************************/ + +static int filter_skb(struct sk_buff *skb, struct sock *sk, + struct trace_point *tp, struct skb_output *skb_output); + +static int filter_sock(struct sock *sk, struct trace_point *tp, struct skb_output *output); + +/* This is the general skb info print function. + * + * The info in 'output' will be printed by 'log_data' log level. + */ +static void general_print(struct skb_output *output, struct trace_point *tp) +{ + char saddr[IP_ADDR_LEN] = {}, daddr[IP_ADDR_LEN] = {}, + output_str[MAX_OUTPUT_LEN] = {}; + COMMON_RULE *rule = &output->rule; + struct icmphdr *icmp; + struct sk_buff *skb = output->skb; + struct net_device *dev = skb ? skb->dev : NULL; + char *type = ""; + + str_append(output_str, "[%d]", dev ? dev->ifindex : 0); + if (tp->is_ret) + str_append(output_str, "[%s,ret:%d]:", output->sym_name, output->ret_val); + else + str_append(output_str, "[%s]:", output->sym_name); + if (rule->proto_3 != ETH_P_IP) { + str_append(output_str, "proto: %s", proto3tostr(rule->proto_3)); + goto begin_print; + } + + if (i2ip(rule->saddr, saddr) || i2ip(rule->daddr, daddr)) { + log_err("parse ip addr error!"); + return; + } + str_append(output_str, "IP %s>%s", saddr, daddr); + + switch (rule->proto_4) { + case IPPROTO_TCP: + str_append(output_str, + " // TCP %d>%d,%s", + ntohs(rule->sport), ntohs(rule->dport), output->flags); + break; + case IPPROTO_UDP: + str_append(output_str, + " // UDP %d>%d", + ntohs(rule->sport), ntohs(rule->dport)); + break; + case IPPROTO_ICMP: + if (!output->skb) { + str_append(output_str, " // ICMP"); + break; + } + + if (!skb_transport_header_was_set(skb)) + icmp = (struct icmphdr *) (skb_network_header(skb) + sizeof(struct iphdr)); + else + icmp = icmp_hdr(skb); + + if (icmp->code == 0 && icmp->type == 8) + type = "request"; + if (icmp->code == 0 && icmp->type == 0) + type = "response"; + + str_append(output_str, + " // ICMP %s %u", + type, + ntohs(icmp->un.echo.sequence)); + break; + default: + str_append(output_str, + " // %s", + proto4tostr(rule->proto_4)); + break; + } + +begin_print: + log_data("%s\n", output_str); + +#if defined(CONFIG_BACKTRACE_USRSTACK_ARM64) || defined(CONFIG_BACKTRACE_USRSTACK_X86_64) + if (print_ustack) + backtrace_usrstack(); +#endif + + if (output->skb && tp->dump_file) + try_dump_skb(output->skb, tp); + + if (print_stack) + dump_stack(); +} + +/* Print the network information by print the skb. + * + * Note that this is most about the kernel function that receive skb, + * as the package header in skb is not completed in skb send function. + */ +static int filter_skb(struct sk_buff *skb, struct sock *sk, + struct trace_point *tp, struct skb_output *skb_output) +{ + int proto_3, proto_4, sport, dport; + struct ethhdr *eth; + struct tcphdr *tcp; + struct udphdr *udp; + struct iphdr *ip; + + COMMON_RULE *rule = &skb_output->rule; + + skb_output->sym_name = tp->name; + rule->s_mask = 0xffffffff; + rule->d_mask = 0xffffffff; + eth = eth_hdr(skb); + if (!sk) + sk = skb->sk; + + if (!skb_mac_header_was_set(skb)) { + proto_3 = ntohs(skb->protocol); + if (proto_3) + goto parse_network; + + if (!sk) + goto error; + + if (sk->sk_family == PF_INET && skb->network_header) { + proto_3 = ETH_P_IP; + goto parse_network; + } + + return filter_sock(sk, tp, skb_output); + } + skb_output->skb = skb; + proto_3 = ntohs(eth->h_proto); + +parse_network: + SET_RULE_FLAGS(rule, proto_3, proto_3); + if (proto_3 != ETH_P_IP) + goto do_match; + + ip = ip_hdr(skb); + if (likely((u8 *)ip >= skb->head && + (u8 *)ip + sizeof(struct iphdr) <= skb_tail_pointer(skb))) { + proto_4 = ip->protocol; + SET_RULE_FLAGS(rule, proto_4, proto_4); + SET_RULE_FLAGS(rule, saddr, ip->saddr); + SET_RULE_FLAGS(rule, daddr, ip->daddr); + } else { + proto_4 = 0; + } + + switch (proto_4) { + case IPPROTO_TCP: + tcp = tcp_hdr(skb); + if (likely((u8 *)tcp >= skb->head && + (u8 *)tcp + sizeof(struct tcphdr) <= skb_tail_pointer(skb))) { + sport = tcp->source; + dport = tcp->dest; + flag2str(tcp, skb_output->flags); + } else { + sport = 0; + dport = 0; + } + goto flag_port; + + case IPPROTO_UDP: + udp = udp_hdr(skb); + if (likely((u8 *)udp >= skb->head && + (u8 *)udp + sizeof(struct udphdr) <= skb_tail_pointer(skb))) { + sport = udp->source; + dport = udp->dest; + } else { + sport = 0; + dport = 0; + } + goto flag_port; + default: + break; + } + +do_match: + if (match_all_rule(rule)) + return 0; + return -1; + +flag_port: + SET_RULE_FLAGS(rule, sport, sport); + SET_RULE_FLAGS(rule, dport, dport); + goto do_match; + +error: + return -1; +} + +/* Print the network information by print the sock. + * + * Note this is most about the process that send skb, during which + * skb headers is not ready and we can not get information from it, + * such ip addr or tcp port. + */ +static int filter_sock(struct sock *sk, struct trace_point *tp, struct skb_output *output) +{ + const struct inet_sock *inet; + int sport = 0, dport = 0; + + COMMON_RULE *rule = &output->rule; + + rule->s_mask = 0xffffffff; + rule->d_mask = 0xffffffff; + output->sym_name = tp->name; + + SET_RULE_FLAGS(rule, proto_4, sk->sk_protocol); + + if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) + goto do_filter; + + inet = inet_sk(sk); + + SET_RULE_FLAGS(rule, proto_3, ETH_P_IP); + SET_RULE_FLAGS(rule, saddr, inet->inet_saddr); + SET_RULE_FLAGS(rule, daddr, inet->inet_daddr); + + sport = inet->inet_sport; + dport = inet->inet_dport; + + SET_RULE_FLAGS(rule, sport, sport); + SET_RULE_FLAGS(rule, dport, dport); + +do_filter: + if (match_all_rule(rule)) + return 0; + return -1; +} + +/******************************************************************************************** + * + * This is the part for all kind of handlers. + * + ********************************************************************************************/ + +/*the function that handle skb and sock.*/ +void __post_handler_general(struct kprobe *p, struct pt_regs *regs, unsigned long flags) +{ + + struct sk_buff *skb = NULL; + struct sock *sk = NULL; + struct skb_output skb_output = {}; + struct trace_point *tp = (struct trace_point *) p->symbol_name; + + if (tp->sock_index > 0) + sk = (struct sock *) kprobe_parm(regs, tp->sock_index); + + if (tp->skb_index > 0) { + skb = (struct sk_buff *) kprobe_parm(regs, tp->skb_index); + goto do_print_skb; + } + + if (tp->pskb_index > 0) { + skb = *((struct sk_buff **) kprobe_parm(regs, tp->pskb_index)); + goto do_print_skb; + } + + if (sk && !filter_sock(sk, tp, &skb_output)) + general_print(&skb_output, tp); + return; + +do_print_skb: + if (skb && !filter_skb(skb, sk, tp, &skb_output)) + general_print(&skb_output, tp); +} + +void post_handler_general(struct kprobe *p, struct pt_regs *regs, unsigned long flags) +{ + /* If being removed, nettrace should stop tracing as soon as possible */ + if (unlikely(READ_ONCE(nt_status) == NT_EXITING)) + return; + + /* If insmoding nettrace is not completed, don't start to parse */ + if (unlikely(READ_ONCE(nt_status) == NT_INIT)) + return; + + __post_handler_general(p, regs, flags); +} + +int entry_handler_general(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + struct sk_buff *skb = NULL; + struct sock *sk = NULL; + struct trace_point *tp = NULL; + struct ret_data *data = (struct ret_data *) ri->data; + +#ifdef CONFIG_KRETPROBE_ON_RETHOOK + struct kretprobe *rp = get_kretprobe(ri); + + if (unlikely(!rp)) + return 1; + tp = (struct trace_point *) rp->kp.symbol_name; +#else + tp = (struct trace_point *) ri->rph->rp->kp.symbol_name; +#endif + if (tp->sock_index > 0) + sk = (struct sock *) kprobe_parm(regs, tp->sock_index); + + if (tp->skb_index > 0) + skb = (struct sk_buff *) kprobe_parm(regs, tp->skb_index); + + if (tp->pskb_index > 0) + skb = *((struct sk_buff **) kprobe_parm(regs, tp->pskb_index)); + + data->sk = sk; + data->skb = skb; + + if (sk == NULL && skb == NULL) + return 1; + return 0; +} + +int __ret_handler_general(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + struct sk_buff *skb = NULL; + struct sock *sk = NULL; + struct skb_output skb_output = {}; + struct trace_point *tp = NULL; + struct ret_data *data = (struct ret_data *) ri->data; + +#ifdef CONFIG_KRETPROBE_ON_RETHOOK + struct kretprobe *rp = get_kretprobe(ri); + + if (unlikely(!rp)) + return 1; + tp = (struct trace_point *) rp->kp.symbol_name; +#else + tp = (struct trace_point *) ri->rph->rp->kp.symbol_name; +#endif + sk = data->sk; + skb_output.ret_val = KPROBE_RET_PARM; + + skb = data->skb; + if (skb != NULL) + goto do_print_skb; + + if (sk && !filter_sock(sk, tp, &skb_output)) + general_print(&skb_output, tp); + return 0; + +do_print_skb: + if (skb && !filter_skb(skb, sk, tp, &skb_output)) + general_print(&skb_output, tp); + return 0; +} + +int ret_handler_general(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + /* If being removed, nettrace should stop tracing as soon as possible */ + if (unlikely(READ_ONCE(nt_status) == NT_EXITING)) + return 0; + + /* If insmoding nettrace is not completed, don't start to parse */ + if (unlikely(READ_ONCE(nt_status) == NT_INIT)) + return 0; + + return __ret_handler_general(ri, regs); +} + +/*the function that handle skb and sock.*/ +void post_handler_udp_tracer(struct kprobe *p, struct pt_regs *regs, unsigned long flags) +{ + + struct sk_buff *skb = NULL; + struct sock *sk = NULL; + struct skb_output skb_output = {}; + + struct trace_point *tp = (struct trace_point *) p->symbol_name; + + if (tp->sock_index > 0) + sk = (struct sock *) kprobe_parm(regs, tp->sock_index); + + if (tp->skb_index > 0) { + skb = (struct sk_buff *) kprobe_parm(regs, tp->skb_index); + goto do_print_skb; + } + + if (tp->pskb_index > 0) { + skb = *((struct sk_buff **) kprobe_parm(regs, tp->pskb_index)); + goto do_print_skb; + } + + if (sk && !filter_sock(sk, tp, &skb_output)) + goto do_print; + return; + +do_print_skb: + if (skb && !filter_skb(skb, sk, tp, &skb_output)) + goto do_print; + return; + +do_print: + general_print(&skb_output, tp); + + if (!print_stack && streq(tp->name, "kfree_skb_reason")) + dump_stack(); +} diff --git a/net/nettrace/handler.h b/net/nettrace/handler.h new file mode 100644 index 0000000000000000000000000000000000000000..9e76a6394048f4bc40e4fab4e6a678d7a4467a15 --- /dev/null +++ b/net/nettrace/handler.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETDUMP_HANDLER_H +#define NETDUMP_HANDLER_H + +#include "group.h" +#include "parser.h" + +#define MAX_OUTPUT_LEN 300 + +#if defined(CONFIG_BACKTRACE_USRSTACK_ARM64) || defined(CONFIG_BACKTRACE_USRSTACK_X86_64) +extern void backtrace_usrstack(void); +#endif + +struct kretprobe_instance; + +struct skb_output { + COMMON_RULE rule; + struct sk_buff *skb; + char *sym_name; + char flags[TCP_FLAG_LEN]; + int ret_val; +}; + +extern struct trace_group *all_group; + +extern void +post_handler_general(struct kprobe *p, struct pt_regs *regs, unsigned long flags); + +extern void +post_handler_udp_tracer(struct kprobe *p, struct pt_regs *regs, unsigned long flags); + +extern int +ret_handler_general(struct kretprobe_instance *ri, struct pt_regs *regs); + +extern int +entry_handler_general(struct kretprobe_instance *ri, struct pt_regs *regs); + +#endif //NETDUMP_HANDLER_H diff --git a/net/nettrace/help.c b/net/nettrace/help.c new file mode 100644 index 0000000000000000000000000000000000000000..f838fdd579b76e6d5c347234340fb6600ea74a93 --- /dev/null +++ b/net/nettrace/help.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "help.h" + +#include "utils.h" +#include "kprobe.h" +#include "parser.h" +#include "group.h" + +/*param for package filter*/ +MODULE_PARM_DESC(saddr, "filter source ip address,e.g. insmod nettrace.ko saddr=172.16.6.62"); +PARAM_STRING_NAMED(filter_saddr, saddr, NULL); +MODULE_PARM_DESC(daddr, "filter destination ip address,e.g. insmod nettrace.ko daddr=172.16.6.74"); +PARAM_STRING_NAMED(filter_daddr, daddr, NULL); +MODULE_PARM_DESC(addr, "filter source or destination ip address,e.g. insmod nettrace.ko addr=192.168.2.11"); +PARAM_STRING_NAMED(filter_addr, addr, NULL); +MODULE_PARM_DESC(proto, "filter 3 layer or 4 layer net protocol,e.g. insmod nettrace.ko proto=arp"); +PARAM_STRING_NAMED(filter_proto, proto, NULL); +MODULE_PARM_DESC(port, "filter source port or destination port, e.g. insmod nettrace.ko port=1234"); +PARAM_INT_NAMED(filter_port, port, -1); +MODULE_PARM_DESC(sport, "filter source port,e.g. insmod nettrace.ko port=1234"); +PARAM_INT_NAMED(filter_sport, sport, -1); +MODULE_PARM_DESC(dport, "filter destination port,e.g. insmod nettrace.ko port=1234"); +PARAM_INT_NAMED(filter_dport, dport, -1); + +MODULE_PARM_DESC(trace, "a trace is a serial of kernel tracing for special scene, e.g. insmod nettrace.ko trace=link"); +PARAM_STRING_ARRAY(filter_trace, trace, 10); +MODULE_PARM_DESC(probe, "this is a list of kernel function where you want to dump network"); +PARAM_STRING_ARRAY(filter_probe, probe, 10); + +MODULE_PARM_DESC(stack, "print the kernel function call stack,e.g. insmod nettrace.ko stack=1"); +PARAM_INT_NAMED(print_stack, stack, 0); +MODULE_PARM_DESC(ustack, "print the user space call stack,e.g. insmod nettrace.ko ustack=1"); +PARAM_INT_NAMED(print_ustack, ustack, 0); + +MODULE_PARM_DESC(output, "three kind of output supported: ftrace, kernel and file, e.g. insmod nettrace.ko output=ftrace"); +PARAM_STRING_NAMED(print_output, output, NULL); +MODULE_PARM_DESC(dump, "the directory where you want to put pcap file in. e.g. insmod nettrace.ko trace=error dump=./output"); +PARAM_STRING_NAMED(print_dump, dump, NULL); + +MODULE_PARM_DESC(flag, "addition flags supported values v: print addition info, e.g. insmod nettrace.ko flag=v"); +PARAM_STRING_NAMED(param_flag, flag, NULL); +MODULE_PARM_DESC(mm, "the number of pages reserved for each TP during the initialization phase"); +PARAM_INT_NAMED(param_mm, mm, 100); + +enum output_type op_type; +int if_print_info; + +struct print_entry { + struct list_head list; + char *msg; +}; + +static struct work_struct print_work; +static LIST_HEAD(print_list); +static spinlock_t print_lock; + +static struct file *output_file; +static char output_index[64]; + +static void print_process(struct work_struct *work) +{ + struct print_entry *file, *next; + unsigned long flag; + LIST_HEAD(head); + + spin_lock_irqsave(&print_lock, flag); + list_splice_init(&print_list, &head); + spin_unlock_irqrestore(&print_lock, flag); + + list_for_each_entry_safe(file, next, &head, list) { + file_append(output_file, file->msg, (unsigned int) strlen(file->msg)); + + kfree(file->msg); + kfree(file); + } +} + +int init_output_file(char *path) +{ + output_file = file_create(path); + + if (output_file == NULL) + return -1; + + INIT_WORK(&print_work, print_process); + spin_lock_init(&print_lock); + + return 0; +} + +static void print_enqueue(char *fmt, va_list ap) +{ + char buf[MAX_LOG_BUF]; + char *print_buf; + struct print_entry *print_file; + unsigned long flag; + + vsnprintf(buf, sizeof(buf), fmt, ap); + print_buf = kcalloc(1, strlen(buf) + 1, GFP_KERNEL); + if (!print_buf) + return; + + strscpy(print_buf, buf, strlen(buf) + 1); + + print_file = kcalloc(1, sizeof(struct print_entry), GFP_KERNEL); + if (!print_file) { + kfree(print_buf); + return; + } + + print_file->msg = print_buf; + + spin_lock_irqsave(&print_lock, flag); + list_add_tail(&print_file->list, &print_list); + spin_unlock_irqrestore(&print_lock, flag); + + schedule_work(&print_work); +} + +void log_base(char *fmt, ...) +{ + + va_list argptr; + + va_start(argptr, fmt); + + switch (op_type) { + default: + vprintk(fmt, argptr); + break; + case OUTPUT_FTRACE: + vprintk(fmt, argptr); + break; + case OUTPUT_FILE: + print_enqueue(fmt, argptr); + break; + } + + va_end(argptr); +} + +static void print_help(void) +{ + log_data("============================Netdump========================\n"); + log_data("\n"); + log_data("Welcome to use netdump! This is a tool based on kprobe for\n"); + log_data("network bag grab in kernel.\n"); + log_data("\n"); + log_data("Basic usage:\n"); + log_data("\n"); + log_data(" insmod netdump.ko [probe=]\n"); + log_data(" [output=] [flag=]\n"); + log_data(" [dump=]\n"); + log_data(" [trace=] [proto=]\n"); + log_data(" [saddr=] [daddr=] [addr=]\n"); + log_data(" [sport=] [dport=] [port=]\n"); + log_data(" [stack=<0 or 1>] [ustack=<0 or 1>]\n"); + log_data("\n"); + log_data("trace: a trace is a serial of kernel tracing for special scene.\n"); + log_data(" We support various of trace, such ip, tcp, macvlan, etc.\n"); + log_data(" Trace can have children trace, use 'trace=?' to see all supported\n"); + log_data(" trace.\n"); + log_data("\n"); + log_data("probe: this is a list of kernel function where you want\n"); + log_data(" to dump network package info. Use 'probe=?' to see all\n"); + log_data(" supported kernel functions.\n"); + log_data("\n"); + log_data("dump: the directory where you want to put pcap file in. Once this\n"); + log_data(" option is set, all package filtered will be saved.\n"); + log_data("\n"); + log_data("output: three kind of output supported: ftrace, kernel and file.\n"); + log_data(" When comes up with file, it should be a file path, such as /ntrace.log.\n"); + log_data("\n"); + log_data("flag: addition flags. Supported values:\n"); + log_data(" v: print addition info.\n"); + log_data("\n"); + log_data("mm: the number of pages reserved for each TP during the initialization phase,\n"); + log_data(" which is used for temporarily caching messages.\n"); + log_data(" [mm=100] means that each TP initialization will reserve 2 * 100 pages,\n"); + log_data(" 100 for storing SKBs, and 100 for storing data."); + log_data("\n"); + + log_data("stack: print the kernel function call stack.\n"); + log_data("ustack: print the user spack call stack.\n"); + log_data("\n"); + log_data("-----------------------package filter-------------------\n"); + log_data("saddr: source ip addr\n"); + log_data("daddr: dest ip addr\n"); + log_data("addr: source or dest ip addr\n"); + log_data("\n"); + log_data("sport: source udp or tcp port\n"); + log_data("dport: dest udp or tcp port\n"); + log_data("port: source or dest udp or tcp port\n"); + log_data("proto: the network protocol\n"); + + log_data("\n"); + log_data("\n"); + log_data("============================Netdump========================\n"); +} + +static void print_group(struct trace_group *tg) +{ + struct trace_group *tmp_tg; + char tab[] = " "; + ulong cur_len = strlen(output_index); + + log_data("%s%s: %s\n", output_index, tg->name, tg->desc); + if (cur_len + sizeof(tab) > sizeof(output_index)) + return; + strscpy(output_index + cur_len, tab, strlen(tab) + 1); + list_for_each_entry(tmp_tg, &tg->groups, list) { + print_group(tmp_tg); + } + output_index[cur_len] = '\0'; +} + +static void print_trace(void) +{ + memset(output_index, 0, sizeof(output_index)); + print_group(all_group); +} + +static void print_probe(void) +{ + struct trace_point *tp; + int i = 0; + + log_data("all supported kprobe:\n\n"); + for_each_tp(i, tp) { + log_data("\t%s\n", tp->name); + } +} + +static int init_rule(void) +{ + COMMON_RULE *rule; + u32 addr_i = 0; + int proto3 = 0; + int proto4 = 0; + + rule = kcalloc(1, sizeof(COMMON_RULE), GFP_KERNEL); + if (!rule) + return -ENOMEM; + + INIT_LIST_HEAD(&rule->list); + rule->s_mask = rule->d_mask = 0xffffffff; + + if (filter_addr) { + if (ip2i(filter_addr, &addr_i)) { + log_err("ip addr format error!\n"); + goto error; + } + addr_i = htonl(addr_i); + rule->saddr = addr_i; + rule->__flags |= FLAG_addr; + } + + if (filter_saddr) { + if (ip2i(filter_saddr, &addr_i)) { + log_err("ip addr format error!\n"); + goto error; + } + addr_i = htonl(addr_i); + SET_RULE_FLAGS(rule, saddr, addr_i); + } + + if (filter_daddr) { + if (ip2i(filter_daddr, &addr_i)) { + log_err("ip addr format error!\n"); + goto error; + } + addr_i = htonl(addr_i); + SET_RULE_FLAGS(rule, daddr, addr_i); + } + + if (filter_sport > 0xffff || filter_dport > 0xffff || filter_port > 0xffff) { + log_err("port range error!\n"); + goto error; + } + + if (filter_port > 0) { + filter_port = (int) htons((u16) filter_port); + rule->sport = filter_port; + rule->__flags |= FLAG_port; + } + + if (filter_sport > 0) { + filter_sport = (int) htons((u16) filter_sport); + SET_RULE_FLAGS(rule, sport, filter_sport); + } + + if (filter_dport > 0) { + filter_dport = (int) htons((u16) filter_dport); + SET_RULE_FLAGS(rule, dport, filter_dport); + } + + if (filter_proto) { + proto3 = str2proto3(filter_proto); + proto4 = str2proto4(filter_proto); + if (proto3 >= 0) { + SET_RULE_FLAGS(rule, proto_3, proto3); + } else if (proto4 >= 0) { + SET_RULE_FLAGS(rule, proto_4, proto4); + } else { + log_err("proto not found!\n"); + goto error; + } + } + + if (add_rule(rule)) + kfree(rule); + + return 0; +error: + kfree(rule); + return -EINVAL; +} + +int init_args(void) +{ + int err = -EINVAL; + + if (print_output != NULL) { + if (streq(print_output, "ftrace")) + op_type = OUTPUT_FTRACE; + else if (streq(print_output, "kernel")) + op_type = OUTPUT_KERNEL; + else if (!init_output_file(print_output)) + op_type = OUTPUT_FILE; + else { + log_err("output type error!\n"); + goto error; + } + } + + if (print_dump != NULL) { + err = access_path(print_dump); + if (err) { + log_err("[nettrace] access_path err:%d\n", err); + log_err("[nettrace] failed to access the dump output directory: %s\n", + print_dump); + goto error; + } + } + + if (filter_trace_len == 0 && filter_probe_len == 0) { + pr_alert("Don't worry. execute 'dmesg' and see usage of nettrace.ko\n"); + print_help(); + goto error; + } + + if (filter_trace_len == 1 && streq(filter_trace[0], "?")) { + pr_alert("Execute 'dmesg' and see available parameters of 'trace='\n"); + print_trace(); + goto error; + } + + if (filter_probe_len == 1 && streq(filter_probe[0], "?")) { + pr_alert("Execute 'dmesg' and see available parameters of 'probe='\n"); + print_probe(); + goto error; + } + + if (param_flag) { + char *tmp_flag; + + while ((tmp_flag = strsep(¶m_flag, ",")) != NULL) { + switch (*tmp_flag) { + case 'v': + if_print_info = 1; + break; + default: + log_err("flags:%c not supported\n", *tmp_flag); + goto error; + } + } + } + + if (init_rule()) + goto error; + + return 0; +error: + return err; +} diff --git a/net/nettrace/help.h b/net/nettrace/help.h new file mode 100644 index 0000000000000000000000000000000000000000..32cc99780a6e8578c55dc7c6abee0202b0bcb753 --- /dev/null +++ b/net/nettrace/help.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETDUMP_HELP_H +#define NETDUMP_HELP_H + +#include +#include + +#define MAX_LOG_BUF 200 + +enum output_type { + OUTPUT_KERNEL, + OUTPUT_FTRACE, + OUTPUT_FILE +}; + +extern char *output_file_path; +extern enum output_type op_type; +extern int if_print_info; + +extern char *filter_saddr; +extern char *filter_daddr; +extern char *filter_addr; +extern char *filter_proto; + +extern int filter_port; +extern int filter_sport; +extern int filter_dport; + +extern char *filter_trace[]; +extern char *filter_probe[]; +extern int filter_trace_len; +extern int filter_probe_len; + +extern int print_stack; +extern int print_ustack; + +extern char *print_output; +extern char *print_dump; +extern char *param_flag; +extern int param_mm; + +extern int init_output_file(char *path); + +extern void log_base(char *fmt, ...); + +extern int init_args(void); + +#define log_info(fmt, args...) {if (if_print_info)\ + log_base(fmt, ##args); } +#define log_data(fmt, args...) log_base(fmt, ##args) +#define log_err(fmt, args...) log_base(fmt, ##args) +#define log_debug(fmt, args...) log_base(fmt, ##args) + +static inline void print_leave(void) +{ + log_info("you just exited netdump, welcome back~\n"); +} + +#endif //NETDUMP_HELP_H diff --git a/net/nettrace/kprobe.c b/net/nettrace/kprobe.c new file mode 100644 index 0000000000000000000000000000000000000000..9fa61b009824c64bdfee4ce1adf99a74375fb02d --- /dev/null +++ b/net/nettrace/kprobe.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "kprobe.h" +#include "help.h" +#include "group.h" + +static kprobe_opcode_t *query_kallsym_addr; + +/* Get the function arg with index of 'index' from 'regs'. + * + * This is just another version of the 'KPROBE_PARM'. + */ +unsigned long kprobe_parm(struct pt_regs *regs, int index) +{ + switch (index) { + case 1: + return PT_REGS_PARM1(regs); + case 2: + return PT_REGS_PARM2(regs); + case 3: + return PT_REGS_PARM3(regs); + case 4: + return PT_REGS_PARM4(regs); + case 5: + return PT_REGS_PARM5(regs); + default: + return 0; + } +} + +/* Declare the kprobe with function call. + */ +struct kprobe *kprobe_declare(const char *sym, void *handler) +{ + struct kprobe *p = kcalloc(1, sizeof(struct kprobe), GFP_KERNEL); + + if (!p) + return NULL; + + p->symbol_name = sym; + p->post_handler = handler; + + return p; +} + +static int skip_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + return 0; +} + +/* Declare the kretprobe with function call. + */ +struct kretprobe *kretprobe_declare(const char *sym, void *handler, + void *entry_handler) +{ + struct kretprobe *p = kcalloc(1, sizeof(struct kretprobe), GFP_KERNEL); + + if (!p) + return NULL; + + if (entry_handler == NULL) + entry_handler = skip_entry_handler; + + p->entry_handler = entry_handler; + p->kp.symbol_name = sym; + p->handler = handler; + + return p; +} + +int filter_syms(void *data, const char *name_buf, unsigned long address) +{ + char *name = data; + + if (strstarts(name_buf, name)) { + if (strlen(name_buf) > MAX_TP_NAME) { + log_err("[nettrace: %s] func name is too long: %s\n", __func__, name_buf); + return -1; + } + strscpy(name, name_buf, MAX_TP_NAME); + query_kallsym_addr = (kprobe_opcode_t *) address; + return -1; + } + return 0; +} + +/* query the address of syms in kallsyms. + */ +int query_kallsyms(char *name) +{ + if (strlen(name) + 1 >= MAX_TP_NAME) { + log_err("[nettrace: %s] func name is too long: %s\n", __func__, name); + return -1; + } + strncat(name, ".", 1); + return kallsyms_on_each_symbol(filter_syms, name); +} + +/* register the kprobe with function call. + */ +int c_register_kprobe(struct kprobe *p) +{ + if (register_kprobe(p) < 0) { + char name[MAX_TP_NAME] = {}; + + strscpy(name, p->symbol_name, sizeof(name)); + if (query_kallsyms(name)) { + pr_alert("[nettrace] Note: The function we want to trace (%s) has become %s.\n", + (char *) p->symbol_name, name); + pr_alert("bacause the kernel compiler had added a suffix in its symbol!\n"); + pr_alert("There may be several versions of %s, please check /proc/kallsyms.\n", + (char *) p->symbol_name); + strscpy((char *) p->symbol_name, name, sizeof(name)); + if (register_kprobe(p) < 0) + goto on_err; + } else { + goto on_err; + } + } + + log_info(" planted kprobe at %p, name %s\n", p->addr, p->symbol_name); + return 0; + +on_err: + return -1; +} + +/* register the kprobe with function call. + */ +int c_register_kretprobe(struct kretprobe *p) +{ + if (register_kretprobe(p) < 0) { + char name[MAX_TP_NAME] = {}; + + strscpy(name, p->kp.symbol_name, sizeof(name)); + if (query_kallsyms(name)) { + pr_alert("[nettrace] Note: The function we want to trace (%s) has become %s.\n", + (char *) p->kp.symbol_name, name); + pr_alert("bacause the kernel compiler had added a suffix in its symbol!\n"); + pr_alert("There may be several versions of %s, please check /proc/kallsyms\n", + (char *) p->kp.symbol_name); + strscpy((char *) p->kp.symbol_name, name, sizeof(name)); + if (register_kretprobe(p) < 0) + goto on_err; + } else { + goto on_err; + } + } + + log_info(" planted kretprobe at %p, name %s\n", p->kp.addr, p->kp.symbol_name); + return 0; + +on_err: + log_err(" register kretprobe failed: %s\n", p->kp.symbol_name); + return -1; +} + +/* unregister the krpobe with function call. + */ +void c_unregister_kprobe(struct kprobe *p) +{ + unregister_kprobe(p); + log_info("kprobe at %s unregistered\n", p->symbol_name); +} + +/* unregister the kretprobe with function call. + */ +void c_unregister_kretprobe(struct kretprobe *p) +{ + unregister_kretprobe(p); + log_info("kretprobe at %s unregistered\n", p->kp.symbol_name); +} diff --git a/net/nettrace/kprobe.h b/net/nettrace/kprobe.h new file mode 100644 index 0000000000000000000000000000000000000000..373568452c8322384a8f5ce46662242bb22b0a4c --- /dev/null +++ b/net/nettrace/kprobe.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KPROBE_COMMON_H +#define KPROBE_COMMON_H + +#include +#include +#include +#include + +#define DECLARE_HANDLER_POST(func) \ + static void post_handler_##func( \ + struct kprobe *p, \ + struct pt_regs *regs, \ + unsigned long flags) +#define DECLARE_HANDLER_RET(func) \ + static int ret_handler##func( \ + struct kretprobe_instance *ri, \ + struct pt_regs *regs) +#define DECLARE_HANDLER_ENTRY(func) \ + static int entry_handler##func( \ + struct kretprobe_instance *ri, \ + struct pt_regs *regs) + +#define DECLARE_KPROBE(func) \ + DECLARE_HANDLER_POST(func); \ + static struct kprobe kprobe_##func = { \ + .symbol_name = #func, \ + .post_handler = post_handler_##func}; \ + DECLARE_HANDLER_POST(func) + +#define DECLARE_RETKPROBE(func) \ + DECLARE_HANDLER_ENTRY(func); \ + DECLARE_HANDLER_RET(func); \ + static struct kretprobe kretprobe_##func = { \ + .handler = ret_handler##func, \ + .entry_handler = entry_handler##func, \ + .kp = {.symbol_name = #func} }; \ + DECLARE_HANDLER_RET(func) + +#define REGISTER_KPROBE(func) \ + do { \ + if (register_kprobe(&kprobe_##func) < 0) { \ + pr_info("register_kprobe failed:" #func "\n"); \ + return 0; \ + } \ + pr_info("Planted kprobe at %p, handler addr %p, name %s\n", \ + kprobe_##func.addr, kprobe_##func.post_handler, #func); \ + } while (0) + +#define UNREGISTER_KPROBE(func) \ + do { \ + unregister_kprobe(&kprobe_##func); \ + pr_info("kprobe at %p unregistered\n", kprobe_##func.addr); \ + } while (0) + +#define PARAM_STRING(name, def) \ + char *name = def; \ + module_param(name, charp, 0) + +#define PARAM_STRING_ARRAY(name, cmdname, length) \ + char *name[length]; \ + int name##_len = 0; \ + module_param_array_named(cmdname, name, charp, &name##_len, 0) + +#define PARAM_STRING_RW(name, default) \ + char *name = default; \ + module_param(name, charp, 0644) + +#define PARAM_STRING_NAMED(name, cmdname, default) \ + char *name = default; \ + module_param_named(cmdname, name, charp, 0644) + +#define PARAM_INT(name, default) \ + int name = default; \ + module_param(name, int, 0) + +#define PARAM_INT_RW(name, default) \ + int name = default; \ + module_param(name, int, 0644) + +#define PARAM_INT_NAMED(name, cmdname, default) \ + int name = default; \ + module_param_named(cmdname, name, int, 0644) + +#define KPROBE_INIT \ + static int __init kprobe_init(void);\ + module_init(kprobe_init); \ + static int __init kprobe_init(void) + +#define KPROBE_EXIT \ + static void __exit kprobe_exit(void); \ + module_exit(kprobe_exit); \ + static void __exit kprobe_exit(void) + +#if defined(__x86_64__) + +#define PT_REGS_PARM1(x) ((x)->di) +#define PT_REGS_PARM2(x) ((x)->si) +#define PT_REGS_PARM3(x) ((x)->dx) +#define PT_REGS_PARM4(x) ((x)->cx) +#define PT_REGS_PARM5(x) ((x)->r8) +#define PT_REGS_RET(x) ((x)->sp) +#define PT_REGS_FP(x) ((x)->bp) +#define PT_REGS_RC(x) ((x)->ax) +#define PT_REGS_SP(x) ((x)->sp) + +#elif defined(__s390x__) + +#define PT_REGS_PARM1(x) ((x)->gprs[2]) +#define PT_REGS_PARM2(x) ((x)->gprs[3]) +#define PT_REGS_PARM3(x) ((x)->gprs[4]) +#define PT_REGS_PARM4(x) ((x)->gprs[5]) +#define PT_REGS_PARM5(x) ((x)->gprs[6]) +#define PT_REGS_RET(x) ((x)->gprs[14]) +#define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */ +#define PT_REGS_RC(x) ((x)->gprs[2]) +#define PT_REGS_SP(x) ((x)->gprs[15]) + +#elif defined(__aarch64__) + +#define PT_REGS_PARM1(x) ((x)->regs[0]) +#define PT_REGS_PARM2(x) ((x)->regs[1]) +#define PT_REGS_PARM3(x) ((x)->regs[2]) +#define PT_REGS_PARM4(x) ((x)->regs[3]) +#define PT_REGS_PARM5(x) ((x)->regs[4]) +#define PT_REGS_RET(x) ((x)->regs[30]) +#define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */ +#define PT_REGS_RC(x) ((x)->regs[0]) +#define PT_REGS_SP(x) ((x)->sp) + +#elif defined(__arm__) + +#define PT_REGS_PARM1(x) ((x)->uregs[0]) +#define PT_REGS_PARM2(x) ((x)->uregs[1]) +#define PT_REGS_PARM3(x) ((x)->uregs[2]) +#define PT_REGS_PARM4(x) ((x)->uregs[3]) +#define PT_REGS_PARM5(x) ((x)->uregs[4]) +#define PT_REGS_RET(x) ((x)->uregs[14]) +#define PT_REGS_FP(x) ((x)->uregs[11]) /* Works only with CONFIG_FRAME_POINTER */ +#define PT_REGS_RC(x) ((x)->uregs[0]) +#define PT_REGS_SP(x) ((x)->uregs[13]) +#define PT_REGS_IP(x) ((x)->uregs[12]) + +#elif defined(__mips__) + +#define PT_REGS_PARM1(x) ((x)->regs[4]) +#define PT_REGS_PARM2(x) ((x)->regs[5]) +#define PT_REGS_PARM3(x) ((x)->regs[6]) +#define PT_REGS_PARM4(x) ((x)->regs[7]) +#define PT_REGS_PARM5(x) ((x)->regs[8]) +#define PT_REGS_RET(x) ((x)->regs[31]) +#define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */ +#define PT_REGS_RC(x) ((x)->regs[2]) +#define PT_REGS_SP(x) ((x)->regs[29]) +#define PT_REGS_IP(x) ((x)->cp0_epc) + +#elif defined(__powerpc__) + +#define PT_REGS_PARM1(x) ((x)->gpr[3]) +#define PT_REGS_PARM2(x) ((x)->gpr[4]) +#define PT_REGS_PARM3(x) ((x)->gpr[5]) +#define PT_REGS_PARM4(x) ((x)->gpr[6]) +#define PT_REGS_PARM5(x) ((x)->gpr[7]) +#define PT_REGS_RC(x) ((x)->gpr[3]) +#define PT_REGS_SP(x) ((x)->sp) +#define PT_REGS_IP(x) ((x)->nip) + +#endif + +#define KPROBE_PARM(type, name, index) (type name = (type)PT_REGS_PARM##index(regs)) +#define KPROBE_RET_PARM regs_return_value(regs) + +//Below is the function encapsulation + +extern unsigned long kprobe_parm(struct pt_regs *regs, int index); + +extern struct kprobe *kprobe_declare(const char *sym, void *handler); + +extern struct kretprobe *kretprobe_declare(const char *sym, void *handler, void *entry_handler); + +extern int c_register_kprobe(struct kprobe *p); + +extern int c_register_kretprobe(struct kretprobe *p); + +extern void c_unregister_kprobe(struct kprobe *p); + +extern void c_unregister_kretprobe(struct kretprobe *p); + +MODULE_LICENSE("GPL"); +#endif diff --git a/net/nettrace/mm.c b/net/nettrace/mm.c new file mode 100644 index 0000000000000000000000000000000000000000..9ea6da9066b1c069179b642497adbbaea96c6ae2 --- /dev/null +++ b/net/nettrace/mm.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2024 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "group.h" +#include "help.h" +#include "mm.h" + +#define MIN_CACHED_SKB_OBJS 100 + +struct sk_buff_nettrace *get_cached_skb(struct trace_point *tp) +{ + if (!tp->nr_skb_objs) + return NULL; + + WRITE_ONCE(tp->nr_skb_objs, tp->nr_skb_objs - 1); + return (struct sk_buff_nettrace *)llist_del_first(&tp->skbcache); +} + +bool put_cached_skb(struct trace_point *tp, struct sk_buff_nettrace *skb) +{ + if (tp->nr_skb_objs >= param_mm) + return false; + + llist_add((struct llist_node *) skb, &tp->skbcache); + WRITE_ONCE(tp->nr_skb_objs, tp->nr_skb_objs + 1); + return true; +} + +u8 *get_cached_data(struct trace_point *tp) +{ + if (!tp->nr_data_objs) + return NULL; + + WRITE_ONCE(tp->nr_data_objs, tp->nr_data_objs - 1); + return (u8 *)llist_del_first(&tp->datacache); +} + +bool put_cached_data(struct trace_point *tp, u8 *data) +{ + if (tp->nr_data_objs >= param_mm) + return false; + + llist_add((struct llist_node *) data, &tp->datacache); + WRITE_ONCE(tp->nr_data_objs, tp->nr_data_objs + 1); + return true; +} + +void init_dump_mm(struct trace_point *tp) +{ + int i; + struct sk_buff_nettrace *skb; + u8 *data; + + if (param_mm < MIN_CACHED_SKB_OBJS) + param_mm = MIN_CACHED_SKB_OBJS; + + for (i = 0; i < param_mm; i++) { + skb = (struct sk_buff_nettrace *) + __get_free_page(GFP_NOWAIT | __GFP_NOWARN); + + if (skb) + put_cached_skb(tp, skb); + else + pr_err("Failed to preallocate for nettrace dump skb! number:%d\n", i); + } + + for (i = 0; i < param_mm; i++) { + data = (u8 *) + __get_free_page(GFP_NOWAIT | __GFP_NOWARN); + + if (data) + put_cached_data(tp, data); + else + pr_err("Failed to preallocate for nettrace dump data! number:%d\n", i); + } + + /* Partial failure does not affect usage. */ +} + +void release_dump_mm(struct trace_point *tp) +{ + int i; + struct sk_buff_nettrace *skb; + u8 *data; + + for (i = 0; i < param_mm; i++) { + skb = get_cached_skb(tp); + if (skb) + free_page((unsigned long)skb); + } + + for (i = 0; i < param_mm; i++) { + data = get_cached_data(tp); + free_page((unsigned long)data); + } +} + +static struct sk_buff_nettrace *__alloc_skb_nettrace(struct trace_point *tp) +{ + struct sk_buff_nettrace *skb; + u8 *data; + + skb = get_cached_skb(tp); + if (!skb) + goto out; + prefetchw(skb); + memset(skb, 0, sizeof(struct sk_buff_nettrace)); + + data = get_cached_data(tp); + if (!data) + goto nodata; + + skb->data = data; + memset(data, 0, PAGE_SIZE); + + skb_queue_head_init(&skb->frag_list); +out: + return skb; +nodata: + put_cached_skb(tp, skb); + skb = NULL; + goto out; +} + +void release_skb_nettrace(struct sk_buff_nettrace *skb, struct trace_point *tp) +{ + struct sk_buff *frag, *tmp; + + if (!skb_queue_empty(&skb->frag_list)) { + skb_queue_walk_safe(&skb->frag_list, frag, tmp) { + put_cached_data(tp, ((struct sk_buff_nettrace *)frag)->data); + put_cached_skb(tp, (struct sk_buff_nettrace *)frag); + } + } + put_cached_data(tp, skb->data); + put_cached_skb(tp, skb); +} + +struct sk_buff_nettrace *skb_copy_nettrace(const struct sk_buff *skb, struct trace_point *tp) +{ + int headerlen = skb_headroom(skb) - skb->mac_header; + unsigned int size = skb->len + headerlen; + unsigned int frag_num = size / PAGE_SIZE + 1; + unsigned int i; + struct sk_buff_nettrace *n; + unsigned int dump_size = 0; + + /* Alloc skb. */ + for (i = 0; i < frag_num; i++) { + if (!i) { + n = __alloc_skb_nettrace(tp); + if (!n) + return NULL; + } else { + struct sk_buff_nettrace *frag = __alloc_skb_nettrace(tp); + + if (!frag) + goto nofrag; + __skb_queue_tail(&n->frag_list, (struct sk_buff *)frag); + } + } + + /* Copy first page. */ + n->len = size > PAGE_SIZE ? PAGE_SIZE : headerlen + skb->len; + if (n->data) { + if (skb_copy_bits(skb, -headerlen, n->data, n->len)) + pr_warn("[nettrace]: Copy SKB failed.\n"); + } + dump_size += n->len; + /* Only one. */ + if (size <= PAGE_SIZE) { + n->total_len = dump_size; + return n; + } + + /* Copy frag pages. */ + if (!skb_queue_empty(&n->frag_list)) { + struct sk_buff *frag, *tmp; + unsigned int i = 0; + + skb_queue_walk_safe(&n->frag_list, frag, tmp) { + i++; + ((struct sk_buff_nettrace *)frag)->len = size - PAGE_SIZE * i > PAGE_SIZE ? + PAGE_SIZE : size - PAGE_SIZE * i; + if (((struct sk_buff_nettrace *)frag)->data) { + if (skb_copy_bits(skb, -headerlen + PAGE_SIZE * i, + ((struct sk_buff_nettrace *)frag)->data, + ((struct sk_buff_nettrace *)frag)->len)) + pr_warn("[nettrace]: Copy SKB failed.\n"); + } + dump_size += ((struct sk_buff_nettrace *)frag)->len; + } + } + if (n) + n->total_len = dump_size; + return n; + +nofrag: + release_skb_nettrace(n, tp); + return NULL; +} diff --git a/net/nettrace/mm.h b/net/nettrace/mm.h new file mode 100644 index 0000000000000000000000000000000000000000..f4ef13486dc10a417c3a76ad41ad973fa286c0ef --- /dev/null +++ b/net/nettrace/mm.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2024 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETDUMP_MM_H +#define NETDUMP_MM_H + +#include "group.h" + +struct sk_buff_nettrace { + struct sk_buff_nettrace *next; + struct sk_buff_nettrace *prev; + struct sk_buff_head frag_list; + unsigned int len; + unsigned int total_len; + unsigned char *data; +}; + +struct trace_point; +struct sk_buff_nettrace *skb_copy_nettrace(const struct sk_buff *skb, struct trace_point *tp); +void init_dump_mm(struct trace_point *tp); +void release_dump_mm(struct trace_point *tp); +void release_skb_nettrace(struct sk_buff_nettrace *skb, struct trace_point *tp); + +struct sk_buff_nettrace *get_cached_skb(struct trace_point *tp); +bool put_cached_skb(struct trace_point *tp, struct sk_buff_nettrace *skb); +u8 *get_cached_data(struct trace_point *tp); +bool put_cached_data(struct trace_point *tp, u8 *data); + +#endif //NETDUMP_MM_H diff --git a/net/nettrace/parser.c b/net/nettrace/parser.c new file mode 100644 index 0000000000000000000000000000000000000000..c337e95f187f09fba730b169c27f61c7d5eb23a0 --- /dev/null +++ b/net/nettrace/parser.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "parser.h" +#include "help.h" +#include "utils.h" + +LIST_HEAD(rule_list); + +struct inet_proto { + int number; + char *name; +}; + +const struct inet_proto proto4[] = { + {.number = 0, .name = "ip"}, + {.number = 1, .name = "icmp"}, + {.number = 2, .name = "igmp"}, + {.number = 4, .name = "ipip"}, + {.number = 6, .name = "tcp"}, + {.number = 8, .name = "egp"}, + {.number = 12, .name = "pup"}, + {.number = 17, .name = "udp"}, + {.number = 22, .name = "idp"}, + {.number = 29, .name = "tp"}, + {.number = 33, .name = "dccp"}, + {.number = 41, .name = "ipv6"}, + {.number = 46, .name = "rsvp"}, + {.number = 47, .name = "gre"}, + {.number = 50, .name = "esp"}, + {.number = 51, .name = "ah"}, + {.number = 92, .name = "mtp"}, + {.number = 94, .name = "beetph"}, + {.number = 98, .name = "encap"}, + {.number = 103, .name = "pim"}, + {.number = 108, .name = "comp"}, + {.number = 132, .name = "sctp"}, + {.number = 136, .name = "udplite"}, + {.number = 137, .name = "mpls"}, + {.number = 255, .name = "raw"} +}; +const int proto4_len = sizeof(proto4) / sizeof(struct inet_proto); + +const struct inet_proto proto3[] = { + {.number = 0x0060, .name = "loop"}, + {.number = 0x0200, .name = "pup"}, + {.number = 0x0201, .name = "pupat"}, + {.number = 0x22F0, .name = "tsn"}, + {.number = 0x22EB, .name = "erspan2"}, + {.number = 0x0800, .name = "ip"}, + {.number = 0x0805, .name = "x25"}, + {.number = 0x0806, .name = "arp"}, + {.number = 0x08FF, .name = "bpq"}, + {.number = 0x0a00, .name = "ieeepup"}, + {.number = 0x0a01, .name = "ieeepupat"}, + {.number = 0x4305, .name = "batman"}, + {.number = 0x6000, .name = "dec"}, + {.number = 0x6001, .name = "dna_dl"}, + {.number = 0x6002, .name = "dna_rc"}, + {.number = 0x6003, .name = "dna_rt"}, + {.number = 0x6004, .name = "lat"}, + {.number = 0x6005, .name = "diag"}, + {.number = 0x6006, .name = "cust"}, + {.number = 0x6007, .name = "sca"}, + {.number = 0x6558, .name = "teb"}, + {.number = 0x8035, .name = "rarp"}, + {.number = 0x809B, .name = "atalk"}, + {.number = 0x80F3, .name = "aarp"}, + {.number = 0x8100, .name = "8021q"}, + {.number = 0x88BE, .name = "erspan"}, + {.number = 0x8137, .name = "ipx"}, + {.number = 0x86DD, .name = "ipv6"}, + {.number = 0x8808, .name = "pause"}, + {.number = 0x8809, .name = "slow"}, + {.number = 0x883E, .name = "wccp"}, + {.number = 0x8847, .name = "mpls_uc"}, + {.number = 0x8848, .name = "mpls_mc"}, + {.number = 0x884c, .name = "atmmpoa"}, + {.number = 0x8863, .name = "ppp_disc"}, + {.number = 0x8864, .name = "ppp_ses"}, + {.number = 0x886c, .name = "link_ctl"}, + {.number = 0x8884, .name = "atmfate"}, + {.number = 0x888E, .name = "pae"}, + {.number = 0x88A2, .name = "aoe"}, + {.number = 0x88A8, .name = "8021ad"}, + {.number = 0x88B5, .name = "802_ex1"}, + {.number = 0x88C7, .name = "preauth"}, + {.number = 0x88CA, .name = "tipc"}, + {.number = 0x88CC, .name = "lldp"}, + {.number = 0x88E5, .name = "macsec"}, + {.number = 0x88E7, .name = "8021ah"}, + {.number = 0x88F5, .name = "mvrp"}, + {.number = 0x88F7, .name = "1588"}, + {.number = 0x88F8, .name = "ncsi"}, + {.number = 0x88FB, .name = "prp"}, + {.number = 0x8906, .name = "fcoe"}, + {.number = 0x8915, .name = "iboe"}, + {.number = 0x890D, .name = "tdls"}, + {.number = 0x8914, .name = "fip"}, + {.number = 0x8917, .name = "80221"}, + {.number = 0x892F, .name = "hsr"}, + {.number = 0x894F, .name = "nsh"}, + {.number = 0x9000, .name = "loopback"}, + {.number = 0x9100, .name = "qinq1"}, + {.number = 0x9200, .name = "qinq2"}, + {.number = 0x9300, .name = "qinq3"}, + {.number = 0xDADA, .name = "edsa"}, + {.number = 0xDADB, .name = "dsa_8021q"}, + {.number = 0xED3E, .name = "ife"}, + {.number = 0xFBFB, .name = "af_iucv"}, + {.number = 0x0600, .name = "802_3_min"} +}; +const int proto3_len = sizeof(proto3) / sizeof(struct inet_proto); + + +/****************************************************************************************** + * + * This is the part for skb bag parse + * + ******************************************************************************************/ + +/*parse u32 to ip addr string.*/ +int i2ip(__be32 ip, char *dest) +{ + u8 *tmp = (u8 *) &ip; + + return sprintf(dest, "%u.%u.%u.%u", + *(tmp), + *(tmp + 1), + *(tmp + 2), + *(tmp + 3)) < 0; +} + +/*parse ip addr string to u32.*/ +int ip2i(char *ip_str, u32 *ip) +{ + int ip_v[4]; + int i = 0; + u32 ip_tmp = 0; + int tmp; + + if (sscanf(ip_str, "%d.%d.%d.%d", + &ip_v[0], + &ip_v[1], + &ip_v[2], + &ip_v[3]) < 4) + return -1; + + for (; i < 4; i++) { + tmp = ip_v[i]; + if (tmp < 0 || tmp > 255) + return -1; + ip_tmp += (((u32)tmp) << ((3 - i) * 8)); + } + *ip = ip_tmp; + return 0; +} + +static +int str2proto(char *proto_str, struct inet_proto proto[], int len) +{ + int i = 0; + + for (; i < len; i++) { + if (streq(proto[i].name, proto_str)) + return proto[i].number; + } + return -1; +} + +static +char *proto2str(int proto, struct inet_proto protos[], int len) +{ + int i = 0; + + for (; i < len; i++) { + if (protos[i].number == proto) + return protos[i].name; + } + return NULL; +} + +int str2proto3(char *proto_str) +{ + return str2proto(proto_str, (struct inet_proto *) proto3, proto3_len); +} + +int str2proto4(char *proto_str) +{ + return str2proto(proto_str, (struct inet_proto *) proto4, proto4_len); +} + +char *proto3tostr(int p) +{ + return proto2str(p, (struct inet_proto *) proto3, proto3_len); +} + +char *proto4tostr(int p) +{ + return proto2str(p, (struct inet_proto *) proto4, proto4_len); +} + +void flag2str(struct tcphdr *tcp, char *str) +{ + if (strlen(str) + 1 > TCP_FLAG_LEN) { + log_err("%s: string length(%s) exceeds TCP_FLAG_LEN\n", __func__, str); + return; + } + + if (tcp->psh) + strncat(str, "P", 1); + if (tcp->rst) + strncat(str, "R", 1); + if (tcp->syn) + strncat(str, "S", 1); + if (tcp->fin) + strncat(str, "F", 1); +} + +/******************************************************************************************* + * + * This is the part for ip bag match + * + *******************************************************************************************/ + +/* 'remote' is the match rule that generated form ip package, 'local' is the + * match rule that user define. + * + * The return value is 0 if matched, and -1 otherwise. + */ +bool match_rule(COMMON_RULE *remote, COMMON_RULE *local) +{ + + if ((local->__flags & FLAG_proto_3) && local->proto_3 != remote->proto_3) + return false; + if ((local->__flags & FLAG_proto_4) && local->proto_4 != remote->proto_4) + return false; + + if (local->__flags & FLAG_port && + local->sport != remote->sport && + local->sport != remote->dport) + return false; + + if ((local->__flags & FLAG_dport) && local->dport != remote->dport) + return false; + if ((local->__flags & FLAG_sport) && local->sport != remote->sport) + return false; + + if (local->__flags & FLAG_addr && ( + local->saddr != (remote->saddr & local->s_mask) && + local->saddr != (remote->daddr & local->s_mask))) + return false; + + if ((local->__flags & FLAG_saddr) && + local->saddr != (remote->saddr & local->s_mask)) + return false; + if ((local->__flags & FLAG_daddr) && + local->daddr != (remote->daddr & local->d_mask)) + return false; + + return true; +} + +bool match_all_rule(COMMON_RULE *remote) +{ + bool is_empty = true; + COMMON_RULE *rule; + + list_for_each_entry(rule, &rule_list, list) { + is_empty = false; + if (match_rule(remote, rule)) + return true; + } + + if (is_empty) + return true; + return false; +} + +int add_rule(COMMON_RULE *rule) +{ + if (rule->__flags) { + list_add_tail(&rule->list, &rule_list); + return 0; + } + return -1; +} + +void free_rules(void) +{ + COMMON_RULE *rule, *pre; + + list_for_each_entry_safe(rule, pre, &rule_list, list) { + kfree(rule); + } +} diff --git a/net/nettrace/parser.h b/net/nettrace/parser.h new file mode 100644 index 0000000000000000000000000000000000000000..0b63df304becd2d1ff37e06319f46e71a343ca4e --- /dev/null +++ b/net/nettrace/parser.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef IP_PARSER_H +#define IP_PARSER_H + +#include +#include + +#define IP_ADDR_LEN 16 + +#define TCP_FLAG_LEN 6 + +/* flags for network package. */ +#define FLAG_proto_3 (1UL << 0) +#define FLAG_proto_4 (1UL << 1) +#define FLAG_sport (1UL << 2) +#define FLAG_dport (1UL << 3) +#define FLAG_saddr (1UL << 4) +#define FLAG_daddr (1UL << 5) + +/* flags for match. */ +#define FLAG_addr (1UL << 6) +#define FLAG_port (1UL << 7) + +typedef +struct common_rule { + struct list_head list; + __u16 __flags; + __u16 proto_3; + __u8 proto_4; + __u16 sport; + __u16 dport; + __u32 saddr; + __u32 s_mask; + __u32 daddr; + __u32 d_mask; + __u8 flags; +} COMMON_RULE; + +#define SET_RULE_FLAGS(rule, flags, value) \ +{\ + (rule)->flags = value;\ + (rule)->__flags |= FLAG_##flags;\ +} + +extern int i2ip(u32 ip, char *dest); + +extern int ip2i(char *ip_str, u32 *ip); + +extern int str2proto4(char *proto_str); + +extern int str2proto3(char *proto_str); + +extern char *proto3tostr(int p); + +extern char *proto4tostr(int p); + +extern bool match_rule(COMMON_RULE *remote, COMMON_RULE *local); + +extern bool match_all_rule(COMMON_RULE *remote); + +extern int add_rule(COMMON_RULE *rule); + +extern void free_rules(void); + +extern void flag2str(struct tcphdr *tcp, char *str); + +#endif //IP_PARSER_H diff --git a/net/nettrace/procfs.c b/net/nettrace/procfs.c new file mode 100644 index 0000000000000000000000000000000000000000..8a09bb00e9275e2b518649eb4fafaa26c154c2cc --- /dev/null +++ b/net/nettrace/procfs.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "procfs.h" +#include "group.h" +#include "dump.h" + +static struct proc_dir_entry *ntrace_proc; + +static int kprobe_tp_info_seq_show(struct seq_file *seq, void *v) +{ + struct trace_point *tp; + int i = 0; + + seq_puts(seq, "current registered kernel function:\n"); + for_each_tp(i, tp) { + if (!tp->kprobe) + continue; + seq_printf(seq, "%30s:%p\n", tp->name, tp->kprobe->addr); + } + + return 0; +} + +static int kprobe_tp_info_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, kprobe_tp_info_seq_show, NULL); +} + +static const struct proc_ops kprobe_tp_info_ops = { + .proc_open = kprobe_tp_info_seq_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +static int nettrace_statistics_seq_show(struct seq_file *seq, void *v) +{ + seq_printf(seq, "The total dump_loss: %u\n", dump_loss_due_to_no_memory + + dump_skb_over_cnt + dump_skb_over_size); + seq_printf(seq, "dump_queue_no_memory: %u\n", dump_loss_due_to_no_memory); + seq_printf(seq, "dump_file_over_cnt: %u\n", dump_skb_over_cnt); + seq_printf(seq, "dump_file_over_size:%u\n", dump_skb_over_size); + + return 0; +} + +static int nettrace_statistics_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, nettrace_statistics_seq_show, NULL); +} + +static const struct proc_ops nettrace_statistics_ops = { + .proc_open = nettrace_statistics_seq_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +static int max_dump_skb_cnt_seq_show(struct seq_file *seq, void *v) +{ + seq_printf(seq, "%d\n", max_dump_skb_cnt); + return 0; +} + +static int max_dump_skb_cnt_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, max_dump_skb_cnt_seq_show, NULL); +} + +static ssize_t max_dump_skb_cnt_proc_write(struct file *file, + const char __user *buf, size_t count, loff_t *pos) +{ + char buffer[32]; + int temp_value; + int err = 0; + + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count)) { + err = -EFAULT; + goto out; + } + + err = kstrtoint(strstrip(buffer), 0, &temp_value); + if (err) + goto out; + if (temp_value < 0) { + err = -EINVAL; + goto out; + } + + max_dump_skb_cnt = temp_value; +out: + return err < 0 ? err : count; +} + +static const struct proc_ops max_dump_skb_cnt_ops = { + .proc_open = max_dump_skb_cnt_proc_open, + .proc_read = seq_read, + .proc_write = max_dump_skb_cnt_proc_write, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +static int max_dump_file_size_seq_show(struct seq_file *seq, void *v) +{ + seq_printf(seq, "%d\n", max_dump_file_size); + return 0; +} + +static int max_dump_file_size_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, max_dump_file_size_seq_show, NULL); +} + +static ssize_t max_dump_file_size_proc_write(struct file *file, + const char __user *buf, size_t count, loff_t *pos) +{ + char buffer[32]; + int temp_value; + int err = 0; + + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count)) { + err = -EFAULT; + goto out; + } + + err = kstrtoint(strstrip(buffer), 0, &temp_value); + if (err) + goto out; + if (temp_value < 0) { + err = -EINVAL; + goto out; + } + + max_dump_file_size = temp_value; +out: + return err < 0 ? err : count; +} + +static const struct proc_ops max_dump_file_size_ops = { + .proc_open = max_dump_file_size_proc_open, + .proc_read = seq_read, + .proc_write = max_dump_file_size_proc_write, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +int __net_init ntrace_proc_init(void) +{ + + ntrace_proc = proc_mkdir("ntrace", init_net.proc_net); + + if (ntrace_proc == NULL) + return -ENOMEM; + + if (!proc_create("kprobe", 0444, ntrace_proc, &kprobe_tp_info_ops)) + goto err_rmdir_ntrace; + if (!proc_create("max_dump_skb_cnt", 0644, ntrace_proc, &max_dump_skb_cnt_ops)) + goto err_rmdir_ntrace; + if (!proc_create("max_dump_file_size", 0644, ntrace_proc, &max_dump_file_size_ops)) + goto err_rmdir_ntrace; + if (!proc_create("statistics", 0444, ntrace_proc, &nettrace_statistics_ops)) + goto err_rmdir_ntrace; + + return 0; + +err_rmdir_ntrace: + remove_proc_subtree("ntrace", init_net.proc_net); + return -ENOMEM; +} + +void __net_exit ntrace_proc_exit(void) +{ + remove_proc_entry("kprobe", ntrace_proc); + proc_remove(ntrace_proc); +} diff --git a/net/nettrace/procfs.h b/net/nettrace/procfs.h new file mode 100644 index 0000000000000000000000000000000000000000..11c71657a94194921a11a2c0f10229b49a4cfa38 --- /dev/null +++ b/net/nettrace/procfs.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETDUMP_PROCFS_H +#define NETDUMP_PROCFS_H + +extern int __net_init ntrace_proc_init(void); + +extern void __net_exit ntrace_proc_exit(void); + +#endif //NETDUMP_PROCFS_H diff --git a/net/nettrace/utils.c b/net/nettrace/utils.c new file mode 100644 index 0000000000000000000000000000000000000000..a61dfa70313f4e75766df2dc44174b854b05a111 --- /dev/null +++ b/net/nettrace/utils.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + + +int file_append(struct file *file, void *data, unsigned int size) +{ + loff_t pos = file->f_pos; + int n = 1; + + while (size > 0 && n > 0) { + n = __kernel_write(file, data, size, &pos); + size -= n; + } + file->f_pos = pos; + return 0; +} + +struct file *file_create(const char *path) +{ + struct file *file = NULL; + + file = filp_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(file)) + return NULL; + + return file; +} + +int access_path(const char *path) +{ + struct file *file = NULL; + + file = filp_open(path, O_DIRECTORY, 0644); + if (IS_ERR(file)) + return PTR_ERR(file); + filp_close(file, NULL); + return 0; +} + +void file_close(struct file *file) +{ + filp_close(file, NULL); +} diff --git a/net/nettrace/utils.h b/net/nettrace/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..59bfd16e8005a725adeaa2ed1806e4479e4ab745 --- /dev/null +++ b/net/nettrace/utils.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Nettrace support. + * + * Copyright (C) 2022 ZTE Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETDUMP_UTILS_H +#define NETDUMP_UTILS_H + +#include +#include + +#define MAX_FILE_NAME 256 + +#define str_append(dest, fmt, args...) \ + sprintf(dest + strlen(dest), fmt, ##args) + +extern int file_append(struct file *file, void *data, unsigned int size); + +extern struct file *file_create(const char *path); + +extern void file_close(struct file *file); + +static inline int streq(char *a, char *b) +{ + return strcmp(a, b) == 0; +} + +extern int access_path(const char *path); + +#endif //NETDUMP_UTILS_H