luckfox-pico-sdk/sysdrv/source/mcu/rt-thread/third_party/netutils/tcpdump/tcpdump.c
luckfox-eng29 8f34c2760d project:build.sh: Added fastboot support; custom modifications to U-Boot and kernel implemented using patches.
project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between
the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door
for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more
usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp:
Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for
compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem.
sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device
tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately.
sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick
boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades.
sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for
fastboot.

Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
2024-10-14 09:47:04 +08:00

734 lines
21 KiB
C

/*
* File : tcpdump.c
* This is file that captures the IP message based on the RT-Thread
* and saves in the file system.
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-07-13 never the first version
*/
#include <rtthread.h>
#include <dfs_posix.h>
#include "netif/ethernetif.h"
#include "optparse.h"
#ifdef PKG_NETUTILS_TCPDUMP_DBG
#define DBG_ENABLE
#define DBG_SECTION_NAME "TCPDUMP"
#define DBG_LEVEL DBG_INFO
#define DBG_COLOR
#else
#undef DBG_ENABLE
#endif
#include <rtdbg.h>
#define TCPDUMP_PIPE_DEVICE ("urdbd") /* rdb pipe */
#define TCPDUMP_DEFAULT_NANE ("sample.pcap")
#define TCPDUMP_MAX_MSG (10)
#define PCAP_FILE_HEADER_SIZE (24)
#define PCAP_PKTHDR_SIZE (16)
#define PCAP_FILE_ID (0xA1B2C3D4)
#define PCAP_VERSION_MAJOR (0x200)
#define PCAP_VERSION_MINOR (0x400)
#define GREENWICH_MEAN_TIME (0)
#define PRECISION_OF_TIME_STAMP (0)
#define MAX_LENTH_OF_CAPTURE_PKG (0xFFFF)
#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 */
#define MSH_CMD ("phi::m::w::") /* [-p] [-h] [-i] [-m] [-w] */
#define STRCMP(a, R, b) (rt_strcmp((a), (b)) R 0)
#define PACP_FILE_HEADER_CREATE(_head) \
do { \
(_head)->magic = PCAP_FILE_ID; \
(_head)->version_major = PCAP_VERSION_MAJOR; \
(_head)->version_minor = PCAP_VERSION_MINOR; \
(_head)->thiszone = GREENWICH_MEAN_TIME; \
(_head)->sigfigs = PRECISION_OF_TIME_STAMP; \
(_head)->snaplen = MAX_LENTH_OF_CAPTURE_PKG; \
(_head)->linktype = LINKTYPE_ETHERNET; \
} while (0)
#define PACP_PKTHDR_CREATE(_head, _p) \
do{ \
(_head)->ts.tv_sec = rt_tick_get() / RT_TICK_PER_SECOND; \
(_head)->ts.tv_msec = rt_tick_get() % RT_TICK_PER_SECOND; \
(_head)->caplen = _p->tot_len; \
(_head)->len = _p->tot_len; \
} while (0)
struct rt_pcap_file_header
{
rt_uint32_t magic;
rt_uint16_t version_major;
rt_uint16_t version_minor;
rt_int32_t thiszone;
rt_uint32_t sigfigs;
rt_uint32_t snaplen;
rt_uint32_t linktype;
};
struct rt_timeval
{
rt_uint32_t tv_sec;
rt_uint32_t tv_msec;
};
struct rt_pcap_pkthdr
{
struct rt_timeval ts;
rt_uint32_t caplen;
rt_uint32_t len;
};
enum rt_tcpdump_return_param
{
STOP = -2,
HELP = -3,
};
static struct rt_device *tcpdump_pipe;
static struct rt_mailbox *tcpdump_mb;
static struct netif *netif;
static netif_linkoutput_fn link_output;
static netif_input_fn input;
static const char *name;
static char *filename;
static const char *eth;
static char *ethname;
static const char *mode;
static int fd = -1;
static void rt_tcpdump_filename_del(void);
static void rt_tcpdump_ethname_del(void);
static void rt_tcpdump_error_info_deal(void);
static void rt_tcpdump_init_indicate(void);
static rt_err_t rt_tcpdump_pcap_file_save(const void *buf, int len);
static rt_err_t (*tcpdump_write)(const void *buf, int len);
#ifdef PKG_NETUTILS_TCPDUMP_PRINT
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
static void hex_dump(const rt_uint8_t *ptr, rt_size_t buflen)
{
unsigned char *buf = (unsigned char *)ptr;
int i, j;
RT_ASSERT(ptr != RT_NULL);
for (i = 0; i < buflen; i += 16)
{
rt_kprintf("%08X: ", i);
for (j = 0; j < 16; j++)
if (i + j < buflen)
rt_kprintf("%02X ", buf[i + j]);
else
rt_kprintf(" ");
rt_kprintf(" ");
for (j = 0; j < 16; j++)
if (i + j < buflen)
rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
rt_kprintf("\n");
}
}
#endif
/* get tx data */
static err_t _netif_linkoutput(struct netif *netif, struct pbuf *p)
{
RT_ASSERT(netif != RT_NULL);
if (p != RT_NULL)
{
pbuf_ref(p);
if (rt_mb_send(tcpdump_mb, (rt_uint32_t)p) != RT_EOK)
{
pbuf_free(p);
}
}
return link_output(netif, p);
}
/* get rx data */
static err_t _netif_input(struct pbuf *p, struct netif *inp)
{
RT_ASSERT(inp != RT_NULL);
if (p != RT_NULL)
{
pbuf_ref(p);
if (rt_mb_send(tcpdump_mb, (rt_uint32_t)p) != RT_EOK)
{
pbuf_free(p);
}
}
return input(p, inp);
}
/* import pcap file into your PC through file-system */
static rt_err_t rt_tcpdump_pcap_file_write(const void *buf, int len)
{
int length;
if (filename == RT_NULL)
{
dbg_log(DBG_ERROR, "file name is null!\n");
return -RT_ERROR;
}
if ((len == 0) && (fd > 0))
{
dbg_log(DBG_ERROR, "ip mess error and close file!\n");
close(fd);
fd = -1;
return -RT_ERROR;
}
if (fd < 0)
{
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0);
if (fd < 0)
{
dbg_log(DBG_ERROR, "open file failed!\n");
return -RT_ERROR;
}
}
length = write(fd, buf, len);
if (length != len)
{
dbg_log(DBG_ERROR, "write data failed, length: %d\n", length);
close(fd);
return -RT_ERROR;
}
return RT_EOK;
}
/* Import pcap file into your PC through rdb tools */
static rt_err_t rt_tcpdump_pcap_file_save(const void *buf, int len)
{
rt_device_write(tcpdump_pipe, 0, buf, len);
return RT_EOK;
}
/* write ip mess and print */
static void rt_tcpdump_ip_mess_write(struct pbuf *p)
{
rt_uint8_t *buf = (rt_uint8_t *)rt_malloc(p->tot_len);
RT_ASSERT(buf != RT_NULL);
pbuf_copy_partial(p, buf, p->tot_len, 0);
#ifdef PKG_NETUTILS_TCPDUMP_PRINT
hex_dump(buf, p->tot_len);
#endif
/* write ip mess */
if (tcpdump_write != RT_NULL)
tcpdump_write(buf, p->tot_len);
rt_free(buf);
}
/* write pcap file header */
static rt_err_t rt_tcpdump_pcap_file_init(void)
{
struct rt_pcap_file_header file_header;
int res = -1;
if (tcpdump_pipe != RT_NULL)
{
if (rt_device_open(tcpdump_pipe, RT_DEVICE_OFLAG_WRONLY) != RT_EOK)
{
dbg_log(DBG_LOG, "not found pipe device!\n");
return -RT_ERROR;
}
}
/* in rdb mode does not need to write pcap file header */
if ((tcpdump_write != RT_NULL) && (tcpdump_write == rt_tcpdump_pcap_file_write))
{
PACP_FILE_HEADER_CREATE(&file_header);
res = tcpdump_write(&file_header, sizeof(file_header));
}
#ifdef PKG_NETUTILS_TCPDUMP_PRINT
hex_dump((rt_uint8_t *)&file_header, PCAP_FILE_HEADER_SIZE);
#endif
if (res != RT_EOK)
return -RT_ERROR;
return RT_EOK;
}
static void rt_tcpdump_thread_entry(void *param)
{
struct pbuf *pbuf = RT_NULL;
struct rt_pcap_pkthdr pkthdr;
rt_uint32_t mbval;
while (1)
{
if (rt_mb_recv(tcpdump_mb, &mbval, RT_WAITING_FOREVER) == RT_EOK)
{
pbuf = (struct pbuf *)mbval;
RT_ASSERT(pbuf != RT_NULL);
/* write pkthdr */
if ((tcpdump_write != RT_NULL) && (tcpdump_write == rt_tcpdump_pcap_file_write))
{
PACP_PKTHDR_CREATE(&pkthdr, pbuf);
tcpdump_write(&pkthdr, sizeof(pkthdr));
}
#ifdef PKG_NETUTILS_TCPDUMP_PRINT
hex_dump((rt_uint8_t *)&pkthdr, PCAP_PKTHDR_SIZE);
#endif
rt_tcpdump_ip_mess_write(pbuf);
pbuf_free(pbuf);
pbuf = RT_NULL;
}
/* tcpdump deinit, the mailbox does not receive the data, exits the thread*/
else
{
dbg_log(DBG_INFO, "tcpdump stop and tcpdump thread exit!\n");
close(fd);
fd = -1;
if (tcpdump_pipe != RT_NULL)
rt_device_close((rt_device_t)tcpdump_pipe);
tcpdump_write = RT_NULL;
rt_tcpdump_filename_del();
rt_tcpdump_ethname_del();
return;
}
}
}
/* set file name */
static void rt_tcpdump_filename_set(const char *name)
{
filename = rt_strdup(name);
}
/* delete file name */
static void rt_tcpdump_filename_del(void)
{
name = RT_NULL;
if (filename != RT_NULL)
rt_free(filename);
filename = RT_NULL;
}
/* set network interface name */
static void rt_tcpdump_ethname_set(const char *eth)
{
ethname = rt_strdup(eth);
}
/* delete network interface name */
static void rt_tcpdump_ethname_del(void)
{
eth = RT_NULL;
if (ethname != RT_NULL)
rt_free(ethname);
}
static int rt_tcpdump_init(void)
{
struct eth_device *device;
rt_thread_t tid;
rt_base_t level;
if (netif != RT_NULL)
{
dbg_log(DBG_ERROR, "This command is running, please stop before you use the [tcpdump -p] command!\n");
return -RT_ERROR;
}
/* print and set default state */
rt_tcpdump_init_indicate();
tcpdump_pipe = rt_device_find(TCPDUMP_PIPE_DEVICE);
/* file-system mode does not judge pipe */
if (tcpdump_write != rt_tcpdump_pcap_file_write)
{
if (tcpdump_pipe == RT_NULL)
{
dbg_log(DBG_ERROR, "pipe is error!\n");
return -RT_ERROR;
}
}
device = (struct eth_device *)rt_device_find(eth);
if (device == RT_NULL)
{
dbg_log(DBG_ERROR, "network interface card [%s] device not find!\n", eth);
dbg_log(DBG_ERROR, "tcpdump thread startup failed and enter the correct network interface please!\n");
return -RT_ERROR;
}
if ((device->netif == RT_NULL) || (device->netif->linkoutput == RT_NULL))
{
dbg_log(DBG_ERROR, "this device not e0!\n");
return -RT_ERROR;
}
tcpdump_mb = rt_mb_create("tdrmb", TCPDUMP_MAX_MSG, RT_IPC_FLAG_FIFO);
if (tcpdump_mb == RT_NULL)
{
dbg_log(DBG_ERROR, "tcp dump mp create fail!\n");
return -RT_ERROR;
}
tid = rt_thread_create("tcpdump", rt_tcpdump_thread_entry, RT_NULL, 2048, 12, 10);
if (tid == RT_NULL)
{
rt_mb_delete(tcpdump_mb);
tcpdump_mb = RT_NULL;
dbg_log(DBG_ERROR, "tcpdump thread create fail!\n");
return -RT_ERROR;
}
rt_tcpdump_filename_set(name);
rt_tcpdump_ethname_set(eth);
netif = device->netif;
/* linkoutput and input init */
level = rt_hw_interrupt_disable();
link_output = netif->linkoutput;
netif->linkoutput = _netif_linkoutput;
input = netif->input;
netif->input = _netif_input;
rt_hw_interrupt_enable(level);
/* linkoutput and input init */
/* write pcap file header */
rt_tcpdump_pcap_file_init();
rt_thread_startup(tid);
dbg_log(DBG_INFO, "tcpdump start!\n");
return RT_EOK;
}
static void rt_tcpdump_deinit(void)
{
rt_base_t level;
if (netif == RT_NULL)
{
dbg_log(DBG_ERROR, "capture packet stopped, no repeat input required!\n");
return;
}
/* linkoutput and input deinit */
level = rt_hw_interrupt_disable();
netif->linkoutput = link_output;
netif->input = input;
netif = RT_NULL;
rt_hw_interrupt_enable(level);
/* linkoutput and input deinit */
rt_mb_delete(tcpdump_mb);
tcpdump_mb = RT_NULL;
}
static void rt_tcpdump_help_info_print(void)
{
rt_kprintf("\n");
rt_kprintf("|>------------------------- help -------------------------<|\n");
rt_kprintf("| tcpdump [-p] [-h] [-i interface] [-m mode] [-w file] |\n");
rt_kprintf("| |\n");
rt_kprintf("| -h: help |\n");
rt_kprintf("| -i: specify the network interface for listening |\n");
rt_kprintf("| -m: choose what mode(file-system or rdb) to save the file|\n");
rt_kprintf("| -w: write the captured packets into an xx.pcap file |\n");
rt_kprintf("| -p: stop capturing packets |\n");
rt_kprintf("| |\n");
rt_kprintf("| e.g.: |\n");
rt_kprintf("| specify network interface and select save mode \\ |\n");
rt_kprintf("| and specify filename |\n");
rt_kprintf("| tcpdump -ie0 -mfile -wtext.pcap |\n");
rt_kprintf("| tcpdump -ie0 -mrdb -wtext.pcap |\n");
rt_kprintf("| |\n");
rt_kprintf("| -m: file-system mode |\n");
rt_kprintf("| tcpdump -mfile |\n");
rt_kprintf("| |\n");
rt_kprintf("| -m: rdb mode |\n");
rt_kprintf("| tcpdump -mrdb |\n");
rt_kprintf("| |\n");
rt_kprintf("| -w: file |\n");
rt_kprintf("| tcpdump -wtext.pcap |\n");
rt_kprintf("| |\n");
rt_kprintf("| -p: stop |\n");
rt_kprintf("| tcpdump -p |\n");
rt_kprintf("| |\n");
rt_kprintf("| -h: help |\n");
rt_kprintf("| tcpdump -h |\n");
rt_kprintf("| |\n");
rt_kprintf("| write commands but no arguments are illegal!! |\n");
rt_kprintf("| e.g.: tcpdump -i / -i -mfile / -i -mfile -wtext.pcap |\n");
rt_kprintf("|>------------------------- help -------------------------<|\n");
rt_kprintf("\n");
}
static void rt_tcpdump_error_info_deal(void)
{
dbg_log(DBG_ERROR, "tcpdump command is incorrect, please refer to the help information!\n");
rt_tcpdump_help_info_print();
}
/* print and set default state */
static void rt_tcpdump_init_indicate(void)
{
int name_flag = 0, eth_flag = 0, mode_flag = 0;
if (eth == RT_NULL)
{
rt_kprintf("[TCPDUMP]default selection [e0] network interface\n");
eth = "e0";
eth_flag = 1;
}
if (tcpdump_write == RT_NULL)
{
rt_kprintf("[TCPDUMP]default selection [file-system] mode\n");
tcpdump_write = rt_tcpdump_pcap_file_write;
mode_flag = 1;
}
if (name == RT_NULL)
{
rt_kprintf("[TCPDUMP]default save in [sample.pcap]\n");
name = TCPDUMP_DEFAULT_NANE;
name_flag = 1;
}
if (eth_flag == 0)
rt_kprintf("[TCPDUMP]select [%s] network interface\n", eth);
if (mode_flag == 0)
{
if (STRCMP(mode, ==, "file"))
rt_kprintf("[TCPDUMP]select [file-system] mode\n");
if (STRCMP(mode, ==, "rdb"))
rt_kprintf("[TCPDUMP]select [rdb] mode\n");
}
if (name_flag == 0)
rt_kprintf("[TCPDUMP]save in [%s]\n", name);
}
/* msh command-line deal */
static int rt_tcpdump_cmd_deal(struct optparse *options)
{
switch (options->optopt)
{
case 'p':
rt_tcpdump_deinit();
return STOP;
case 'h':
rt_tcpdump_help_info_print();
return HELP;
case 'i':
/* it's illegal without parameters. */
if (options->optarg == RT_NULL)
return -RT_ERROR;
eth = options->optarg;
return RT_EOK;
case 'm':
if (options->optarg == RT_NULL)
return -RT_ERROR;
if (STRCMP(options->optarg, ==, "file"))
{
mode = options->optarg;
tcpdump_write = rt_tcpdump_pcap_file_write;
return RT_EOK;
}
if (STRCMP(options->optarg, ==, "rdb"))
{
mode = options->optarg;
tcpdump_write = rt_tcpdump_pcap_file_save;
return RT_EOK;
}
/* User input Error */
return -RT_ERROR;
case 'w':
if (options->optarg == RT_NULL)
return -RT_ERROR;
name = options->optarg;
break;
default:
return -RT_ERROR;
}
return RT_EOK;
}
/* msh command-line parsing */
static int rt_tcpdump_cmd_parse(char *argv[], const char *cmd)
{
int ch, res, invalid_argv = 0;
struct optparse options;
optparse_init(&options, argv);
while ((ch = optparse(&options, cmd)) != -1)
{
ch = ch;
invalid_argv = 1;
switch (options.optopt)
{
case 'p':
return rt_tcpdump_cmd_deal(&options);
case 'h':
return rt_tcpdump_cmd_deal(&options);
case 'i':
res = rt_tcpdump_cmd_deal(&options);
break;
case 'm':
res = rt_tcpdump_cmd_deal(&options);
break;
case 'w':
res = rt_tcpdump_cmd_deal(&options);
break;
default:
rt_tcpdump_error_info_deal();
return -RT_ERROR;
}
if (res == -RT_ERROR)
{
rt_tcpdump_error_info_deal();
return res;
}
}
/* judge invalid command */
if (invalid_argv == 0)
{
rt_tcpdump_error_info_deal();
return -RT_ERROR;
}
return RT_EOK;
}
static void rt_tcpdump_cmd_argv_deinit(void)
{
eth = RT_NULL;
tcpdump_write = RT_NULL;
name = RT_NULL;
}
static int tcpdump_test(int argc, char *argv[])
{
int res = 0;
if (argc == 1)
{
rt_tcpdump_cmd_argv_deinit();
rt_tcpdump_init();
return RT_EOK;
}
rt_tcpdump_cmd_argv_deinit();
res = rt_tcpdump_cmd_parse(argv, MSH_CMD);
if (res == STOP)
return RT_EOK;
if (res == HELP)
return RT_EOK;
if (res == -RT_ERROR)
return -RT_ERROR;
rt_tcpdump_init();
return RT_EOK;
}
#ifdef RT_USING_FINSH
#include <finsh.h>
MSH_CMD_EXPORT_ALIAS(tcpdump_test, tcpdump, test optparse_short cmd.);
#endif