summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--CMakeLists.txt2
-rw-r--r--conf/stellar.toml12
-rw-r--r--deps/CMakeLists.txt5
-rw-r--r--deps/linenoise/CMakeLists.txt2
-rw-r--r--deps/linenoise/linenoise.c1225
-rw-r--r--deps/linenoise/linenoise.h77
-rw-r--r--deps/ringbuf/CMakeLists.txt2
-rw-r--r--deps/ringbuf/ringbuf.c430
-rw-r--r--deps/ringbuf/ringbuf.h29
-rw-r--r--deps/ringbuf/utils.h115
-rw-r--r--deps/sds/CMakeLists.txt2
-rw-r--r--deps/sds/sds.c1335
-rw-r--r--deps/sds/sds.h285
-rw-r--r--deps/sds/sdsalloc.h42
-rw-r--r--deps/sds/testhelp.h57
-rw-r--r--include/stellar/monitor.h54
-rw-r--r--infra/CMakeLists.txt4
-rw-r--r--infra/monitor/CMakeLists.txt22
-rw-r--r--infra/monitor/enforcer/CMakeLists.txt11
-rw-r--r--infra/monitor/enforcer/show_session_enforcer.c818
-rw-r--r--infra/monitor/monitor_cmd_assistant.c537
-rw-r--r--infra/monitor/monitor_cmd_assistant.h60
-rw-r--r--infra/monitor/monitor_private.h331
-rw-r--r--infra/monitor/monitor_ringbuf.c137
-rw-r--r--infra/monitor/monitor_ringbuf.h30
-rw-r--r--infra/monitor/monitor_rpc.c115
-rw-r--r--infra/monitor/monitor_rpc.h10
-rw-r--r--infra/monitor/monitor_server.c591
-rw-r--r--infra/monitor/monitor_spinlock.c72
-rw-r--r--infra/monitor/monitor_spinlock.h8
-rw-r--r--infra/monitor/monitor_stat.c76
-rw-r--r--infra/monitor/monitor_transaction.c83
-rw-r--r--infra/monitor/monitor_utils.c672
-rw-r--r--infra/monitor/monitor_utils.h30
-rw-r--r--infra/monitor/version.map10
-rw-r--r--infra/packet_manager/packet_internal.h2
-rw-r--r--infra/session_manager/session_manager.c10
-rw-r--r--infra/session_manager/session_manager_rte.h1
-rw-r--r--infra/version.map2
-rw-r--r--test/CMakeLists.txt3
-rw-r--r--test/decoders/http/http_gtest_llhttp.cpp507
-rw-r--r--test/monitor/CMakeLists.txt78
-rw-r--r--test/monitor/gtest_cmd_assistant.cpp252
-rw-r--r--test/monitor/gtest_monitor_server.cpp172
-rw-r--r--test/monitor/gtest_monitor_util.cpp453
-rw-r--r--test/monitor/gtest_packet_dump.cpp276
-rw-r--r--test/monitor/gtest_packet_dump_tunnel.cpp140
-rw-r--r--test/monitor/gtest_packet_dump_unit.cpp229
-rw-r--r--test/monitor/gtest_ringbuf.cpp194
-rw-r--r--test/monitor/gtest_rpc.cpp68
-rw-r--r--test/monitor/gtest_sds.cpp152
-rw-r--r--test/monitor/gtest_seek_layer.cpp142
-rw-r--r--test/monitor/gtest_spinlock.cpp49
-rw-r--r--test/monitor/gtest_topk.cpp535
-rw-r--r--test/monitor/monitor_main.cpp74
-rw-r--r--test/monitor/pcap/monitor_benchmark.pcapbin0 -> 3118 bytes
-rw-r--r--test/monitor/pcap/monitor_packet_dump.pcapbin0 -> 4917478 bytes
-rw-r--r--test/monitor/pcap/monitor_tunnel.pcapbin0 -> 1056 bytes
-rw-r--r--tools/CMakeLists.txt1
-rw-r--r--tools/monitor/CMakeLists.txt19
-rw-r--r--tools/monitor/monitor_cli.c557
-rwxr-xr-xtools/monitor/stellar-dump-update.sh12
-rw-r--r--tools/monitor/stellar-dump.patch11400
-rw-r--r--vendors/CMakeLists.txt2
-rw-r--r--vendors/tcpdump-4.99.4.tar.gzbin0 -> 1912404 bytes
66 files changed, 22616 insertions, 6 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 17c428b..f09520f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -52,6 +52,7 @@ stages:
--suppress=unreachableCode
--suppress=internalAstError
--suppress=integerOverflow
+ --suppress=*:${CI_PROJECT_DIR}/infra/monitor/stellar-dump/*
tags:
- share
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ff43ca..9d28378 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,6 +45,7 @@ if (CMAKE_CXX_CPPCHECK)
"--suppress=unreachableCode"
"--suppress=internalAstError"
"--suppress=integerOverflow"
+ "--suppress=*:${CMAKE_SOURCE_DIR}/infra/monitor/stellar-dump/*"
)
set(CMAKE_C_CPPCHECK ${CMAKE_CXX_CPPCHECK})
else()
@@ -84,6 +85,7 @@ add_subdirectory(infra)
add_subdirectory(decoders)
add_subdirectory(scripts)
add_subdirectory(include)
+add_subdirectory(tools)
add_subdirectory(test)
install(DIRECTORY DESTINATION log COMPONENT PROGRAM)
diff --git a/conf/stellar.toml b/conf/stellar.toml
index 808de63..8303360 100644
--- a/conf/stellar.toml
+++ b/conf/stellar.toml
@@ -65,6 +65,13 @@
file = "log/stellar.log"
level = "INFO" # TRACE, DEBUG, INFO, WARN, ERROR, FATAL
+[monitor]
+ listen_port = 80
+ data_link_bind_port = 37008
+ connection_idle_timeout = 60 # second
+ cli_request_timeout = 3 # second
+ pktdump_task_max_num = 3
+
[[module]]
path = ""
init = "packet_manager_on_init"
@@ -78,3 +85,8 @@
exit = "session_manager_on_exit"
thread_init = "session_manager_on_thread_init"
thread_exit = "session_manager_on_thread_exit"
+
+[[module]]
+ path=""
+ init="monitor_on_init"
+ exit="monitor_on_exit"
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 69ffeec..a00ff10 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -5,4 +5,7 @@ add_subdirectory(rbtree)
add_subdirectory(interval_tree)
add_subdirectory(bitmap)
add_subdirectory(nmx_pool)
-add_subdirectory(logger) \ No newline at end of file
+add_subdirectory(logger)
+add_subdirectory(sds)
+add_subdirectory(linenoise)
+add_subdirectory(ringbuf) \ No newline at end of file
diff --git a/deps/linenoise/CMakeLists.txt b/deps/linenoise/CMakeLists.txt
new file mode 100644
index 0000000..b0cad66
--- /dev/null
+++ b/deps/linenoise/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_library(linenoise STATIC linenoise.c)
+target_include_directories(linenoise PUBLIC ${CMAKE_CURRENT_LIST_DIR}) \ No newline at end of file
diff --git a/deps/linenoise/linenoise.c b/deps/linenoise/linenoise.c
new file mode 100644
index 0000000..cfe51e7
--- /dev/null
+++ b/deps/linenoise/linenoise.c
@@ -0,0 +1,1225 @@
+/* linenoise.c -- guerrilla line editing library against the idea that a
+ * line editing lib needs to be 20,000 lines of C code.
+ *
+ * You can find the latest source code at:
+ *
+ * http://github.com/antirez/linenoise
+ *
+ * Does a number of crazy assumptions that happen to be true in 99.9999% of
+ * the 2010 UNIX computers around.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * References:
+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+ *
+ * Todo list:
+ * - Filter bogus Ctrl+<char> combinations.
+ * - Win32 support
+ *
+ * Bloat:
+ * - History search like Ctrl+r in readline?
+ *
+ * List of escape sequences used by this program, we do everything just
+ * with three sequences. In order to be so cheap we may have some
+ * flickering effect with some slow terminal, but the lesser sequences
+ * the more compatible.
+ *
+ * EL (Erase Line)
+ * Sequence: ESC [ n K
+ * Effect: if n is 0 or missing, clear from cursor to end of line
+ * Effect: if n is 1, clear from beginning of line to cursor
+ * Effect: if n is 2, clear entire line
+ *
+ * CUF (CUrsor Forward)
+ * Sequence: ESC [ n C
+ * Effect: moves cursor forward n chars
+ *
+ * CUB (CUrsor Backward)
+ * Sequence: ESC [ n D
+ * Effect: moves cursor backward n chars
+ *
+ * The following is used to get the terminal width if getting
+ * the width with the TIOCGWINSZ ioctl fails
+ *
+ * DSR (Device Status Report)
+ * Sequence: ESC [ 6 n
+ * Effect: reports the current cusor position as ESC [ n ; m R
+ * where n is the row and m is the column
+ *
+ * When multi line mode is enabled, we also use an additional escape
+ * sequence. However multi line editing is disabled by default.
+ *
+ * CUU (Cursor Up)
+ * Sequence: ESC [ n A
+ * Effect: moves cursor up of n chars.
+ *
+ * CUD (Cursor Down)
+ * Sequence: ESC [ n B
+ * Effect: moves cursor down of n chars.
+ *
+ * When linenoiseClearScreen() is called, two additional escape sequences
+ * are used in order to clear the screen and position the cursor at home
+ * position.
+ *
+ * CUP (Cursor position)
+ * Sequence: ESC [ H
+ * Effect: moves the cursor to upper left corner
+ *
+ * ED (Erase display)
+ * Sequence: ESC [ 2 J
+ * Effect: clear the whole screen
+ *
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include "linenoise.h"
+
+#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
+#define LINENOISE_MAX_LINE 4096
+static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
+static linenoiseCompletionCallback *completionCallback = NULL;
+static linenoiseHintsCallback *hintsCallback = NULL;
+static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
+
+static struct termios orig_termios; /* In order to restore at exit.*/
+static int maskmode = 0; /* Show "***" instead of input. For passwords. */
+static int rawmode = 0; /* For atexit() function to check if restore is needed*/
+static int mlmode = 0; /* Multi line mode. Default is single line. */
+static int atexit_registered = 0; /* Register atexit just 1 time. */
+static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
+static int history_len = 0;
+static char **history = NULL;
+
+/* The linenoiseState structure represents the state during line editing.
+ * We pass this state to functions implementing specific editing
+ * functionalities. */
+struct linenoiseState {
+ int ifd; /* Terminal stdin file descriptor. */
+ int ofd; /* Terminal stdout file descriptor. */
+ char *buf; /* Edited line buffer. */
+ size_t buflen; /* Edited line buffer size. */
+ const char *prompt; /* Prompt to display. */
+ size_t plen; /* Prompt length. */
+ size_t pos; /* Current cursor position. */
+ size_t oldpos; /* Previous refresh cursor position. */
+ size_t len; /* Current edited line length. */
+ size_t cols; /* Number of columns in terminal. */
+ size_t maxrows; /* Maximum num of rows used so far (multiline mode) */
+ int history_index; /* The history index we are currently editing. */
+};
+
+enum KEY_ACTION{
+ KEY_NULL = 0, /* NULL */
+ CTRL_A = 1, /* Ctrl+a */
+ CTRL_B = 2, /* Ctrl-b */
+ CTRL_C = 3, /* Ctrl-c */
+ CTRL_D = 4, /* Ctrl-d */
+ CTRL_E = 5, /* Ctrl-e */
+ CTRL_F = 6, /* Ctrl-f */
+ CTRL_H = 8, /* Ctrl-h */
+ TAB = 9, /* Tab */
+ CTRL_K = 11, /* Ctrl+k */
+ CTRL_L = 12, /* Ctrl+l */
+ ENTER = 13, /* Enter */
+ CTRL_N = 14, /* Ctrl-n */
+ CTRL_P = 16, /* Ctrl-p */
+ CTRL_T = 20, /* Ctrl-t */
+ CTRL_U = 21, /* Ctrl+u */
+ CTRL_W = 23, /* Ctrl+w */
+ ESC = 27, /* Escape */
+ BACKSPACE = 127 /* Backspace */
+};
+
+static void linenoiseAtExit(void);
+int linenoiseHistoryAdd(const char *line);
+static void refreshLine(struct linenoiseState *l);
+
+/* Debugging macro. */
+#if 0
+FILE *lndebug_fp = NULL;
+#define lndebug(...) \
+ do { \
+ if (lndebug_fp == NULL) { \
+ lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
+ fprintf(lndebug_fp, \
+ "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
+ (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
+ (int)l->maxrows,old_rows); \
+ } \
+ fprintf(lndebug_fp, ", " __VA_ARGS__); \
+ fflush(lndebug_fp); \
+ } while (0)
+#else
+#define lndebug(fmt, ...)
+#endif
+
+/* ======================= Low level terminal handling ====================== */
+
+/* Enable "mask mode". When it is enabled, instead of the input that
+ * the user is typing, the terminal will just display a corresponding
+ * number of asterisks, like "****". This is useful for passwords and other
+ * secrets that should not be displayed. */
+void linenoiseMaskModeEnable(void) {
+ maskmode = 1;
+}
+
+/* Disable mask mode. */
+void linenoiseMaskModeDisable(void) {
+ maskmode = 0;
+}
+
+/* Set if to use or not the multi line mode. */
+void linenoiseSetMultiLine(int ml) {
+ mlmode = ml;
+}
+
+/* Return true if the terminal name is in the list of terminals we know are
+ * not able to understand basic escape sequences. */
+static int isUnsupportedTerm(void) {
+ char *term = getenv("TERM");
+ int j;
+
+ if (term == NULL) return 0;
+ for (j = 0; unsupported_term[j]; j++)
+ if (!strcasecmp(term,unsupported_term[j])) return 1;
+ return 0;
+}
+
+/* Raw mode: 1960 magic shit. */
+static int enableRawMode(int fd) {
+ struct termios raw;
+
+ if (!isatty(STDIN_FILENO)) goto fatal;
+ if (!atexit_registered) {
+ atexit(linenoiseAtExit);
+ atexit_registered = 1;
+ }
+ if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
+
+ raw = orig_termios; /* modify the original mode */
+ /* input modes: no break, no CR to NL, no parity check, no strip char,
+ * no start/stop output control. */
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ /* output modes - disable post processing */
+ raw.c_oflag &= ~(OPOST);
+ /* control modes - set 8 bit chars */
+ raw.c_cflag |= (CS8);
+ /* local modes - choing off, canonical off, no extended functions,
+ * no signal chars (^Z,^C) */
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ /* control chars - set return condition: min number of bytes and timer.
+ * We want read to return every single byte, without timeout. */
+ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+
+ /* put terminal in raw mode after flushing */
+ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
+ rawmode = 1;
+ return 0;
+
+fatal:
+ errno = ENOTTY;
+ return -1;
+}
+
+static void disableRawMode(int fd) {
+ /* Don't even check the return value as it's too late. */
+ if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
+ rawmode = 0;
+}
+
+/* Use the ESC [6n escape sequence to query the horizontal cursor position
+ * and return it. On error -1 is returned, on success the position of the
+ * cursor. */
+static int getCursorPosition(int ifd, int ofd) {
+ char buf[32];
+ int cols, rows;
+ unsigned int i = 0;
+
+ /* Report cursor location */
+ if (write(ofd, "\x1b[6n", 4) != 4) return -1;
+
+ /* Read the response: ESC [ rows ; cols R */
+ while (i < sizeof(buf)-1) {
+ if (read(ifd,buf+i,1) != 1) break;
+ if (buf[i] == 'R') break;
+ i++;
+ }
+ buf[i] = '\0';
+
+ /* Parse it. */
+ if (buf[0] != ESC || buf[1] != '[') return -1;
+ if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
+ return cols;
+}
+
+/* Try to get the number of columns in the current terminal, or assume 80
+ * if it fails. */
+static int getColumns(int ifd, int ofd) {
+ struct winsize ws;
+
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+ /* ioctl() failed. Try to query the terminal itself. */
+ int start, cols;
+
+ /* Get the initial position so we can restore it later. */
+ start = getCursorPosition(ifd,ofd);
+ if (start == -1) goto failed;
+
+ /* Go to right margin and get position. */
+ if (write(ofd,"\x1b[999C",6) != 6) goto failed;
+ cols = getCursorPosition(ifd,ofd);
+ if (cols == -1) goto failed;
+
+ /* Restore position. */
+ if (cols > start) {
+ char seq[32];
+ snprintf(seq,32,"\x1b[%dD",cols-start);
+ if (write(ofd,seq,strlen(seq)) == -1) {
+ /* Can't recover... */
+ }
+ }
+ return cols;
+ } else {
+ return ws.ws_col;
+ }
+
+failed:
+ return 80;
+}
+
+/* Clear the screen. Used to handle ctrl+l */
+void linenoiseClearScreen(void) {
+ if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
+ /* nothing to do, just to avoid warning. */
+ }
+}
+
+/* Beep, used for completion when there is nothing to complete or when all
+ * the choices were already shown. */
+static void linenoiseBeep(void) {
+ fprintf(stderr, "\x7");
+ fflush(stderr);
+}
+
+/* ============================== Completion ================================ */
+
+/* Free a list of completion option populated by linenoiseAddCompletion(). */
+static void freeCompletions(linenoiseCompletions *lc) {
+ size_t i;
+ for (i = 0; i < lc->len; i++)
+ free(lc->cvec[i]);
+ if (lc->cvec != NULL)
+ free(lc->cvec);
+}
+
+/* This is an helper function for linenoiseEdit() and is called when the
+ * user types the <tab> key in order to complete the string currently in the
+ * input.
+ *
+ * The state of the editing is encapsulated into the pointed linenoiseState
+ * structure as described in the structure definition. */
+static int completeLine(struct linenoiseState *ls) {
+ linenoiseCompletions lc = { 0, NULL };
+ int nread, nwritten;
+ char c = 0;
+
+ completionCallback(ls->buf,&lc);
+ if (lc.len == 0) {
+ linenoiseBeep();
+ } else {
+ size_t stop = 0, i = 0;
+
+ while(!stop) {
+ /* Show completion or original buffer */
+ if (i < lc.len) {
+ struct linenoiseState saved = *ls;
+
+ ls->len = ls->pos = strlen(lc.cvec[i]);
+ ls->buf = lc.cvec[i];
+ refreshLine(ls);
+ ls->len = saved.len;
+ ls->pos = saved.pos;
+ ls->buf = saved.buf;
+ } else {
+ refreshLine(ls);
+ }
+
+ nread = read(ls->ifd,&c,1);
+ if (nread <= 0) {
+ freeCompletions(&lc);
+ return -1;
+ }
+
+ switch(c) {
+ case 9: /* tab */
+ i = (i+1) % (lc.len+1);
+ if (i == lc.len) linenoiseBeep();
+ break;
+ case 27: /* escape */
+ /* Re-show original buffer */
+ if (i < lc.len) refreshLine(ls);
+ stop = 1;
+ break;
+ default:
+ /* Update buffer and return */
+ if (i < lc.len) {
+ nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]);
+ ls->len = ls->pos = nwritten;
+ }
+ stop = 1;
+ break;
+ }
+ }
+ }
+
+ freeCompletions(&lc);
+ return c; /* Return last read character */
+}
+
+/* Register a callback function to be called for tab-completion. */
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
+ completionCallback = fn;
+}
+
+/* Register a hits function to be called to show hits to the user at the
+ * right of the prompt. */
+void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
+ hintsCallback = fn;
+}
+
+/* Register a function to free the hints returned by the hints callback
+ * registered with linenoiseSetHintsCallback(). */
+void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
+ freeHintsCallback = fn;
+}
+
+/* This function is used by the callback function registered by the user
+ * in order to add completion options given the input string when the
+ * user typed <tab>. See the example.c source code for a very easy to
+ * understand example. */
+void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
+ size_t len = strlen(str);
+ char *copy, **cvec;
+
+ copy = malloc(len+1);
+ if (copy == NULL) return;
+ memcpy(copy,str,len+1);
+ cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
+ if (cvec == NULL) {
+ free(copy);
+ return;
+ }
+ lc->cvec = cvec;
+ lc->cvec[lc->len++] = copy;
+}
+
+/* =========================== Line editing ================================= */
+
+/* We define a very simple "append buffer" structure, that is an heap
+ * allocated string where we can append to. This is useful in order to
+ * write all the escape sequences in a buffer and flush them to the standard
+ * output in a single call, to avoid flickering effects. */
+struct abuf {
+ char *b;
+ int len;
+};
+
+static void abInit(struct abuf *ab) {
+ ab->b = NULL;
+ ab->len = 0;
+}
+
+static void abAppend(struct abuf *ab, const char *s, int len) {
+ char *new = realloc(ab->b,ab->len+len);
+
+ if (new == NULL) return;
+ memcpy(new+ab->len,s,len);
+ ab->b = new;
+ ab->len += len;
+}
+
+static void abFree(struct abuf *ab) {
+ free(ab->b);
+}
+
+/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
+ * to the right of the prompt. */
+void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
+ char seq[64];
+ if (hintsCallback && plen+l->len < l->cols) {
+ int color = -1, bold = 0;
+ char *hint = hintsCallback(l->buf,&color,&bold);
+ if (hint) {
+ int hintlen = strlen(hint);
+ int hintmaxlen = l->cols-(plen+l->len);
+ if (hintlen > hintmaxlen) hintlen = hintmaxlen;
+ if (bold == 1 && color == -1) color = 37;
+ if (color != -1 || bold != 0)
+ snprintf(seq,64,"\033[%d;%d;49m",bold,color);
+ else
+ seq[0] = '\0';
+ abAppend(ab,seq,strlen(seq));
+ abAppend(ab,hint,hintlen);
+ if (color != -1 || bold != 0)
+ abAppend(ab,"\033[0m",4);
+ /* Call the function to free the hint returned. */
+ if (freeHintsCallback) freeHintsCallback(hint);
+ }
+ }
+}
+
+/* Single line low level line refresh.
+ *
+ * Rewrite the currently edited line accordingly to the buffer content,
+ * cursor position, and number of columns of the terminal. */
+static void refreshSingleLine(struct linenoiseState *l) {
+ char seq[64];
+ size_t plen = strlen(l->prompt);
+ int fd = l->ofd;
+ char *buf = l->buf;
+ size_t len = l->len;
+ size_t pos = l->pos;
+ struct abuf ab;
+
+ while((plen+pos) >= l->cols) {
+ buf++;
+ len--;
+ pos--;
+ }
+ while (plen+len > l->cols) {
+ len--;
+ }
+
+ abInit(&ab);
+ /* Cursor to left edge */
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+ /* Write the prompt and the current buffer content */
+ abAppend(&ab,l->prompt,strlen(l->prompt));
+ if (maskmode == 1) {
+ while (len--) abAppend(&ab,"*",1);
+ } else {
+ abAppend(&ab,buf,len);
+ }
+ /* Show hits if any. */
+ refreshShowHints(&ab,l,plen);
+ /* Erase to right */
+ snprintf(seq,64,"\x1b[0K");
+ abAppend(&ab,seq,strlen(seq));
+ /* Move cursor to original position. */
+ snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
+ abAppend(&ab,seq,strlen(seq));
+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+ abFree(&ab);
+}
+
+/* Multi line low level line refresh.
+ *
+ * Rewrite the currently edited line accordingly to the buffer content,
+ * cursor position, and number of columns of the terminal. */
+static void refreshMultiLine(struct linenoiseState *l) {
+ char seq[64];
+ int plen = strlen(l->prompt);
+ int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */
+ int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */
+ int rpos2; /* rpos after refresh. */
+ int col; /* colum position, zero-based. */
+ int old_rows = l->maxrows;
+ int fd = l->ofd, j;
+ struct abuf ab;
+
+ /* Update maxrows if needed. */
+ if (rows > (int)l->maxrows) l->maxrows = rows;
+
+ /* First step: clear all the lines used before. To do so start by
+ * going to the last row. */
+ abInit(&ab);
+ if (old_rows-rpos > 0) {
+ lndebug("go down %d", old_rows-rpos);
+ snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Now for every row clear it, go up. */
+ for (j = 0; j < old_rows-1; j++) {
+ lndebug("clear+up");
+ snprintf(seq,64,"\r\x1b[0K\x1b[1A");
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Clean the top line. */
+ lndebug("clear");
+ snprintf(seq,64,"\r\x1b[0K");
+ abAppend(&ab,seq,strlen(seq));
+
+ /* Write the prompt and the current buffer content */
+ abAppend(&ab,l->prompt,strlen(l->prompt));
+ if (maskmode == 1) {
+ unsigned int i;
+ for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
+ } else {
+ abAppend(&ab,l->buf,l->len);
+ }
+
+ /* Show hits if any. */
+ refreshShowHints(&ab,l,plen);
+
+ /* If we are at the very end of the screen with our prompt, we need to
+ * emit a newline and move the prompt to the first column. */
+ if (l->pos &&
+ l->pos == l->len &&
+ (l->pos+plen) % l->cols == 0)
+ {
+ lndebug("<newline>");
+ abAppend(&ab,"\n",1);
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+ rows++;
+ if (rows > (int)l->maxrows) l->maxrows = rows;
+ }
+
+ /* Move cursor to right position. */
+ rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
+ lndebug("rpos2 %d", rpos2);
+
+ /* Go up till we reach the expected positon. */
+ if (rows-rpos2 > 0) {
+ lndebug("go-up %d", rows-rpos2);
+ snprintf(seq,64,"\x1b[%dA", rows-rpos2);
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Set column. */
+ col = (plen+(int)l->pos) % (int)l->cols;
+ lndebug("set col %d", 1+col);
+ if (col)
+ snprintf(seq,64,"\r\x1b[%dC", col);
+ else
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+
+ lndebug("\n");
+ l->oldpos = l->pos;
+
+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+ abFree(&ab);
+}
+
+/* Calls the two low level functions refreshSingleLine() or
+ * refreshMultiLine() according to the selected mode. */
+static void refreshLine(struct linenoiseState *l) {
+ if (mlmode)
+ refreshMultiLine(l);
+ else
+ refreshSingleLine(l);
+}
+
+/* Insert the character 'c' at cursor current position.
+ *
+ * On error writing to the terminal -1 is returned, otherwise 0. */
+int linenoiseEditInsert(struct linenoiseState *l, char c) {
+ if (l->len < l->buflen) {
+ if (l->len == l->pos) {
+ l->buf[l->pos] = c;
+ l->pos++;
+ l->len++;
+ l->buf[l->len] = '\0';
+ if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
+ /* Avoid a full update of the line in the
+ * trivial case. */
+ char d = (maskmode==1) ? '*' : c;
+ if (write(l->ofd,&d,1) == -1) return -1;
+ } else {
+ refreshLine(l);
+ }
+ } else {
+ memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);
+ l->buf[l->pos] = c;
+ l->len++;
+ l->pos++;
+ l->buf[l->len] = '\0';
+ refreshLine(l);
+ }
+ }
+ return 0;
+}
+
+/* Move cursor on the left. */
+void linenoiseEditMoveLeft(struct linenoiseState *l) {
+ if (l->pos > 0) {
+ l->pos--;
+ refreshLine(l);
+ }
+}
+
+/* Move cursor on the right. */
+void linenoiseEditMoveRight(struct linenoiseState *l) {
+ if (l->pos != l->len) {
+ l->pos++;
+ refreshLine(l);
+ }
+}
+
+/* Move cursor to the start of the line. */
+void linenoiseEditMoveHome(struct linenoiseState *l) {
+ if (l->pos != 0) {
+ l->pos = 0;
+ refreshLine(l);
+ }
+}
+
+/* Move cursor to the end of the line. */
+void linenoiseEditMoveEnd(struct linenoiseState *l) {
+ if (l->pos != l->len) {
+ l->pos = l->len;
+ refreshLine(l);
+ }
+}
+
+/* Substitute the currently edited line with the next or previous history
+ * entry as specified by 'dir'. */
+#define LINENOISE_HISTORY_NEXT 0
+#define LINENOISE_HISTORY_PREV 1
+void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {
+ if (history_len > 1) {
+ /* Update the current history entry before to
+ * overwrite it with the next one. */
+ free(history[history_len - 1 - l->history_index]);
+ history[history_len - 1 - l->history_index] = strdup(l->buf);
+ /* Show the new entry */
+ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
+ if (l->history_index < 0) {
+ l->history_index = 0;
+ return;
+ } else if (l->history_index >= history_len) {
+ l->history_index = history_len-1;
+ return;
+ }
+ strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);
+ l->buf[l->buflen-1] = '\0';
+ l->len = l->pos = strlen(l->buf);
+ refreshLine(l);
+ }
+}
+
+/* Delete the character at the right of the cursor without altering the cursor
+ * position. Basically this is what happens with the "Delete" keyboard key. */
+void linenoiseEditDelete(struct linenoiseState *l) {
+ if (l->len > 0 && l->pos < l->len) {
+ memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1);
+ l->len--;
+ l->buf[l->len] = '\0';
+ refreshLine(l);
+ }
+}
+
+/* Backspace implementation. */
+void linenoiseEditBackspace(struct linenoiseState *l) {
+ if (l->pos > 0 && l->len > 0) {
+ memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos);
+ l->pos--;
+ l->len--;
+ l->buf[l->len] = '\0';
+ refreshLine(l);
+ }
+}
+
+/* Delete the previosu word, maintaining the cursor at the start of the
+ * current word. */
+void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
+ size_t old_pos = l->pos;
+ size_t diff;
+
+ while (l->pos > 0 && l->buf[l->pos-1] == ' ')
+ l->pos--;
+ while (l->pos > 0 && l->buf[l->pos-1] != ' ')
+ l->pos--;
+ diff = old_pos - l->pos;
+ memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);
+ l->len -= diff;
+ refreshLine(l);
+}
+
+/* This function is the core of the line editing capability of linenoise.
+ * It expects 'fd' to be already in "raw mode" so that every key pressed
+ * will be returned ASAP to read().
+ *
+ * The resulting string is put into 'buf' when the user type enter, or
+ * when ctrl+d is typed.
+ *
+ * The function returns the length of the current buffer. */
+static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
+{
+ struct linenoiseState l;
+
+ /* Populate the linenoise state that we pass to functions implementing
+ * specific editing functionalities. */
+ l.ifd = stdin_fd;
+ l.ofd = stdout_fd;
+ l.buf = buf;
+ l.buflen = buflen;
+ l.prompt = prompt;
+ l.plen = strlen(prompt);
+ l.oldpos = l.pos = 0;
+ l.len = 0;
+ l.cols = getColumns(stdin_fd, stdout_fd);
+ l.maxrows = 0;
+ l.history_index = 0;
+
+ /* Buffer starts empty. */
+ l.buf[0] = '\0';
+ l.buflen--; /* Make sure there is always space for the nulterm */
+
+ /* The latest history entry is always our current buffer, that
+ * initially is just an empty string. */
+ linenoiseHistoryAdd("");
+
+ if (write(l.ofd,prompt,l.plen) == -1) return -1;
+ while(1) {
+ char c;
+ int nread;
+ char seq[3];
+
+ nread = read(l.ifd,&c,1);
+ if (nread <= 0) return l.len;
+
+ /* Only autocomplete when the callback is set. It returns < 0 when
+ * there was an error reading from fd. Otherwise it will return the
+ * character that should be handled next. */
+ if (c == 9 && completionCallback != NULL) {
+ c = completeLine(&l);
+ /* Return on errors */
+ if (c < 0) return l.len;
+ /* Read next character when 0 */
+ if (c == 0) continue;
+ }
+
+ switch(c) {
+ case ENTER: /* enter */
+ history_len--;
+ free(history[history_len]);
+ if (mlmode) linenoiseEditMoveEnd(&l);
+ if (hintsCallback) {
+ /* Force a refresh without hints to leave the previous
+ * line as the user typed it after a newline. */
+ linenoiseHintsCallback *hc = hintsCallback;
+ hintsCallback = NULL;
+ refreshLine(&l);
+ hintsCallback = hc;
+ }
+ return (int)l.len;
+ case CTRL_C: /* ctrl-c */
+ errno = EAGAIN;
+ return -1;
+ case BACKSPACE: /* backspace */
+ case 8: /* ctrl-h */
+ linenoiseEditBackspace(&l);
+ break;
+ case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
+ line is empty, act as end-of-file. */
+ if (l.len > 0) {
+ linenoiseEditDelete(&l);
+ } else {
+ history_len--;
+ free(history[history_len]);
+ return -1;
+ }
+ break;
+ case CTRL_T: /* ctrl-t, swaps current character with previous. */
+ if (l.pos > 0 && l.pos < l.len) {
+ int aux = buf[l.pos-1];
+ buf[l.pos-1] = buf[l.pos];
+ buf[l.pos] = aux;
+ if (l.pos != l.len-1) l.pos++;
+ refreshLine(&l);
+ }
+ break;
+ case CTRL_B: /* ctrl-b */
+ linenoiseEditMoveLeft(&l);
+ break;
+ case CTRL_F: /* ctrl-f */
+ linenoiseEditMoveRight(&l);
+ break;
+ case CTRL_P: /* ctrl-p */
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
+ break;
+ case CTRL_N: /* ctrl-n */
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
+ break;
+ case ESC: /* escape sequence */
+ /* Read the next two bytes representing the escape sequence.
+ * Use two calls to handle slow terminals returning the two
+ * chars at different times. */
+ if (read(l.ifd,seq,1) == -1) break;
+ if (read(l.ifd,seq+1,1) == -1) break;
+
+ /* ESC [ sequences. */
+ if (seq[0] == '[') {
+ if (seq[1] >= '0' && seq[1] <= '9') {
+ /* Extended escape, read additional byte. */
+ if (read(l.ifd,seq+2,1) == -1) break;
+ if (seq[2] == '~') {
+ switch(seq[1]) {
+ case '3': /* Delete key. */
+ linenoiseEditDelete(&l);
+ break;
+ }
+ }
+ } else {
+ switch(seq[1]) {
+ case 'A': /* Up */
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
+ break;
+ case 'B': /* Down */
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
+ break;
+ case 'C': /* Right */
+ linenoiseEditMoveRight(&l);
+ break;
+ case 'D': /* Left */
+ linenoiseEditMoveLeft(&l);
+ break;
+ case 'H': /* Home */
+ linenoiseEditMoveHome(&l);
+ break;
+ case 'F': /* End*/
+ linenoiseEditMoveEnd(&l);
+ break;
+ }
+ }
+ }
+
+ /* ESC O sequences. */
+ else if (seq[0] == 'O') {
+ switch(seq[1]) {
+ case 'H': /* Home */
+ linenoiseEditMoveHome(&l);
+ break;
+ case 'F': /* End*/
+ linenoiseEditMoveEnd(&l);
+ break;
+ }
+ }
+ break;
+ default:
+ if (linenoiseEditInsert(&l,c)) return -1;
+ break;
+ case CTRL_U: /* Ctrl+u, delete the whole line. */
+ buf[0] = '\0';
+ l.pos = l.len = 0;
+ refreshLine(&l);
+ break;
+ case CTRL_K: /* Ctrl+k, delete from current to end of line. */
+ buf[l.pos] = '\0';
+ l.len = l.pos;
+ refreshLine(&l);
+ break;
+ case CTRL_A: /* Ctrl+a, go to the start of the line */
+ linenoiseEditMoveHome(&l);
+ break;
+ case CTRL_E: /* ctrl+e, go to the end of the line */
+ linenoiseEditMoveEnd(&l);
+ break;
+ case CTRL_L: /* ctrl+l, clear screen */
+ linenoiseClearScreen();
+ refreshLine(&l);
+ break;
+ case CTRL_W: /* ctrl+w, delete previous word */
+ linenoiseEditDeletePrevWord(&l);
+ break;
+ }
+ }
+ return l.len;
+}
+
+/* This special mode is used by linenoise in order to print scan codes
+ * on screen for debugging / development purposes. It is implemented
+ * by the linenoise_example program using the --keycodes option. */
+void linenoisePrintKeyCodes(void) {
+ char quit[4];
+
+ printf("Linenoise key codes debugging mode.\n"
+ "Press keys to see scan codes. Type 'quit' at any time to exit.\n");
+ if (enableRawMode(STDIN_FILENO) == -1) return;
+ memset(quit,' ',4);
+ while(1) {
+ char c;
+ int nread;
+
+ nread = read(STDIN_FILENO,&c,1);
+ if (nread <= 0) continue;
+ memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
+ quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
+ if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
+
+ printf("'%c' %02x (%d) (type quit to exit)\n",
+ isprint(c) ? c : '?', (int)c, (int)c);
+ printf("\r"); /* Go left edge manually, we are in raw mode. */
+ fflush(stdout);
+ }
+ disableRawMode(STDIN_FILENO);
+}
+
+/* This function calls the line editing function linenoiseEdit() using
+ * the STDIN file descriptor set in raw mode. */
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+ int count;
+
+ if (buflen == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (enableRawMode(STDIN_FILENO) == -1) return -1;
+ count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
+ disableRawMode(STDIN_FILENO);
+ printf("\n");
+ return count;
+}
+
+/* This function is called when linenoise() is called with the standard
+ * input file descriptor not attached to a TTY. So for example when the
+ * program using linenoise is called in pipe or with a file redirected
+ * to its standard input. In this case, we want to be able to return the
+ * line regardless of its length (by default we are limited to 4k). */
+static char *linenoiseNoTTY(void) {
+ char *line = NULL;
+ size_t len = 0, maxlen = 0;
+
+ while(1) {
+ if (len == maxlen) {
+ if (maxlen == 0) maxlen = 16;
+ maxlen *= 2;
+ char *oldval = line;
+ line = realloc(line,maxlen);
+ if (line == NULL) {
+ if (oldval) free(oldval);
+ return NULL;
+ }
+ }
+ int c = fgetc(stdin);
+ if (c == EOF || c == '\n') {
+ if (c == EOF && len == 0) {
+ free(line);
+ return NULL;
+ } else {
+ line[len] = '\0';
+ return line;
+ }
+ } else {
+ line[len] = c;
+ len++;
+ }
+ }
+}
+
+/* The high level function that is the main API of the linenoise library.
+ * This function checks if the terminal has basic capabilities, just checking
+ * for a blacklist of stupid terminals, and later either calls the line
+ * editing function or uses dummy fgets() so that you will be able to type
+ * something even in the most desperate of the conditions. */
+char *linenoise(const char *prompt) {
+ char buf[LINENOISE_MAX_LINE];
+ int count;
+
+ if (!isatty(STDIN_FILENO)) {
+ /* Not a tty: read from file / pipe. In this mode we don't want any
+ * limit to the line size, so we call a function to handle that. */
+ return linenoiseNoTTY();
+ } else if (isUnsupportedTerm()) {
+ size_t len;
+
+ printf("%s",prompt);
+ fflush(stdout);
+ if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
+ len = strlen(buf);
+ while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
+ len--;
+ buf[len] = '\0';
+ }
+ return strdup(buf);
+ } else {
+ count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
+ if (count == -1) return NULL;
+ return strdup(buf);
+ }
+}
+
+/* This is just a wrapper the user may want to call in order to make sure
+ * the linenoise returned buffer is freed with the same allocator it was
+ * created with. Useful when the main program is using an alternative
+ * allocator. */
+void linenoiseFree(void *ptr) {
+ free(ptr);
+}
+
+/* ================================ History ================================= */
+
+/* Free the history, but does not reset it. Only used when we have to
+ * exit() to avoid memory leaks are reported by valgrind & co. */
+static void freeHistory(void) {
+ if (history) {
+ int j;
+
+ for (j = 0; j < history_len; j++)
+ free(history[j]);
+ free(history);
+ }
+}
+
+/* At exit we'll try to fix the terminal to the initial conditions. */
+static void linenoiseAtExit(void) {
+ disableRawMode(STDIN_FILENO);
+ freeHistory();
+}
+
+/* This is the API call to add a new entry in the linenoise history.
+ * It uses a fixed array of char pointers that are shifted (memmoved)
+ * when the history max length is reached in order to remove the older
+ * entry and make room for the new one, so it is not exactly suitable for huge
+ * histories, but will work well for a few hundred of entries.
+ *
+ * Using a circular buffer is smarter, but a bit more complex to handle. */
+int linenoiseHistoryAdd(const char *line) {
+ char *linecopy;
+
+ if (history_max_len == 0) return 0;
+
+ /* Initialization on first call. */
+ if (history == NULL) {
+ history = malloc(sizeof(char*)*history_max_len);
+ if (history == NULL) return 0;
+ memset(history,0,(sizeof(char*)*history_max_len));
+ }
+
+ /* Don't add duplicated lines. */
+ if (history_len && !strcmp(history[history_len-1], line)) return 0;
+
+ /* Add an heap allocated copy of the line in the history.
+ * If we reached the max length, remove the older line. */
+ linecopy = strdup(line);
+ if (!linecopy) return 0;
+ if (history_len == history_max_len) {
+ free(history[0]);
+ memmove(history,history+1,sizeof(char*)*(history_max_len-1));
+ history_len--;
+ }
+ history[history_len] = linecopy;
+ history_len++;
+ return 1;
+}
+
+/* Set the maximum length for the history. This function can be called even
+ * if there is already some history, the function will make sure to retain
+ * just the latest 'len' elements if the new history length value is smaller
+ * than the amount of items already inside the history. */
+int linenoiseHistorySetMaxLen(int len) {
+ char **new;
+
+ if (len < 1) return 0;
+ if (history) {
+ int tocopy = history_len;
+
+ new = malloc(sizeof(char*)*len);
+ if (new == NULL) return 0;
+
+ /* If we can't copy everything, free the elements we'll not use. */
+ if (len < tocopy) {
+ int j;
+
+ for (j = 0; j < tocopy-len; j++) free(history[j]);
+ tocopy = len;
+ }
+ memset(new,0,sizeof(char*)*len);
+ memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
+ free(history);
+ history = new;
+ }
+ history_max_len = len;
+ if (history_len > history_max_len)
+ history_len = history_max_len;
+ return 1;
+}
+
+/* Save the history in the specified file. On success 0 is returned
+ * otherwise -1 is returned. */
+int linenoiseHistorySave(const char *filename) {
+ mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ FILE *fp;
+ int j;
+
+ fp = fopen(filename,"w");
+ umask(old_umask);
+ if (fp == NULL) return -1;
+ chmod(filename,S_IRUSR|S_IWUSR);
+ for (j = 0; j < history_len; j++)
+ fprintf(fp,"%s\n",history[j]);
+ fclose(fp);
+ return 0;
+}
+
+/* Load the history from the specified file. If the file does not exist
+ * zero is returned and no operation is performed.
+ *
+ * If the file exists and the operation succeeded 0 is returned, otherwise
+ * on error -1 is returned. */
+int linenoiseHistoryLoad(const char *filename) {
+ FILE *fp = fopen(filename,"r");
+ char buf[LINENOISE_MAX_LINE];
+
+ if (fp == NULL) return -1;
+
+ while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
+ char *p;
+
+ p = strchr(buf,'\r');
+ if (!p) p = strchr(buf,'\n');
+ if (p) *p = '\0';
+ linenoiseHistoryAdd(buf);
+ }
+ fclose(fp);
+ return 0;
+}
diff --git a/deps/linenoise/linenoise.h b/deps/linenoise/linenoise.h
new file mode 100644
index 0000000..f74cdc0
--- /dev/null
+++ b/deps/linenoise/linenoise.h
@@ -0,0 +1,77 @@
+/* linenoise.h -- VERSION 1.0
+ *
+ * Guerrilla line editing library against the idea that a line editing lib
+ * needs to be 20,000 lines of C code.
+ *
+ * See linenoise.c for more information.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LINENOISE_H
+#define __LINENOISE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct linenoiseCompletions
+ {
+ size_t len;
+ char **cvec;
+ } linenoiseCompletions;
+
+ typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
+ typedef char *(linenoiseHintsCallback)(const char *, int *color, int *bold);
+ typedef void(linenoiseFreeHintsCallback)(void *);
+ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
+ void linenoiseSetHintsCallback(linenoiseHintsCallback *);
+ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
+ void linenoiseAddCompletion(linenoiseCompletions *, const char *);
+
+ char *linenoise(const char *prompt);
+ void linenoiseFree(void *ptr);
+ int linenoiseHistoryAdd(const char *line);
+ int linenoiseHistorySetMaxLen(int len);
+ int linenoiseHistorySave(const char *filename);
+ int linenoiseHistoryLoad(const char *filename);
+ void linenoiseClearScreen(void);
+ void linenoiseSetMultiLine(int ml);
+ void linenoisePrintKeyCodes(void);
+ void linenoiseMaskModeEnable(void);
+ void linenoiseMaskModeDisable(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LINENOISE_H */
diff --git a/deps/ringbuf/CMakeLists.txt b/deps/ringbuf/CMakeLists.txt
new file mode 100644
index 0000000..16222e2
--- /dev/null
+++ b/deps/ringbuf/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_library(ringbuf STATIC ringbuf.c)
+target_include_directories(ringbuf PUBLIC ${CMAKE_CURRENT_LIST_DIR}) \ No newline at end of file
diff --git a/deps/ringbuf/ringbuf.c b/deps/ringbuf/ringbuf.c
new file mode 100644
index 0000000..75cfee9
--- /dev/null
+++ b/deps/ringbuf/ringbuf.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2016-2017 Mindaugas Rasiukevicius <rmind at noxt eu>
+ * All rights reserved.
+ *
+ * Use is subject to license terms, as specified in the LICENSE file.
+ */
+
+/*
+ * Atomic multi-producer single-consumer ring buffer, which supports
+ * contiguous range operations and which can be conveniently used for
+ * message passing.
+ *
+ * There are three offsets -- think of clock hands:
+ * - NEXT: marks the beginning of the available space,
+ * - WRITTEN: the point up to which the data is actually written.
+ * - Observed READY: point up to which data is ready to be written.
+ *
+ * Producers
+ *
+ * Observe and save the 'next' offset, then request N bytes from
+ * the ring buffer by atomically advancing the 'next' offset. Once
+ * the data is written into the "reserved" buffer space, the thread
+ * clears the saved value; these observed values are used to compute
+ * the 'ready' offset.
+ *
+ * Consumer
+ *
+ * Writes the data between 'written' and 'ready' offsets and updates
+ * the 'written' value. The consumer thread scans for the lowest
+ * seen value by the producers.
+ *
+ * Key invariant
+ *
+ * Producers cannot go beyond the 'written' offset; producers are
+ * also not allowed to catch up with the consumer. Only the consumer
+ * is allowed to catch up with the producer i.e. set the 'written'
+ * offset to be equal to the 'next' offset.
+ *
+ * Wrap-around
+ *
+ * If the producer cannot acquire the requested length due to little
+ * available space at the end of the buffer, then it will wraparound.
+ * WRAP_LOCK_BIT in 'next' offset is used to lock the 'end' offset.
+ *
+ * There is an ABA problem if one producer stalls while a pair of
+ * producer and consumer would both successfully wrap-around and set
+ * the 'next' offset to the stale value of the first producer, thus
+ * letting it to perform a successful CAS violating the invariant.
+ * A counter in the 'next' offset (masked by WRAP_COUNTER) is used
+ * to prevent from this problem. It is incremented on wraparounds.
+ *
+ * The same ABA problem could also cause a stale 'ready' offset,
+ * which could be observed by the consumer. We set WRAP_LOCK_BIT in
+ * the 'seen' value before advancing the 'next' and clear this bit
+ * after the successful advancing; this ensures that only the stable
+ * 'ready' is observed by the consumer.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "ringbuf.h"
+#include "utils.h"
+
+#define RBUF_OFF_MASK (0x00000000ffffffffUL)
+#define WRAP_LOCK_BIT (0x8000000000000000UL)
+#define RBUF_OFF_MAX (UINT64_MAX & ~WRAP_LOCK_BIT)
+
+#define WRAP_COUNTER (0x7fffffff00000000UL)
+#define WRAP_INCR(x) (((x) + 0x100000000UL) & WRAP_COUNTER)
+
+typedef uint64_t ringbuf_off_t;
+
+struct ringbuf_worker {
+ volatile ringbuf_off_t seen_off;
+ int registered;
+};
+
+struct ringbuf {
+ /* Ring buffer space. */
+ size_t space;
+
+ /*
+ * The NEXT hand is atomically updated by the producer.
+ * WRAP_LOCK_BIT is set in case of wrap-around; in such case,
+ * the producer can update the 'end' offset.
+ */
+ volatile ringbuf_off_t next;
+ ringbuf_off_t end;
+
+ /* The following are updated by the consumer. */
+ ringbuf_off_t written;
+ unsigned nworkers;
+ ringbuf_worker_t workers[];
+};
+
+/*
+ * ringbuf_setup: initialise a new ring buffer of a given length.
+ */
+int
+ringbuf_setup(ringbuf_t *rbuf, unsigned nworkers, size_t length)
+{
+ if (length >= RBUF_OFF_MASK) {
+ errno = EINVAL;
+ return -1;
+ }
+ memset(rbuf, 0, offsetof(ringbuf_t, workers[nworkers]));
+ rbuf->space = length;
+ rbuf->end = RBUF_OFF_MAX;
+ rbuf->nworkers = nworkers;
+ return 0;
+}
+
+/*
+ * ringbuf_get_sizes: return the sizes of the ringbuf_t and ringbuf_worker_t.
+ */
+void
+ringbuf_get_sizes(unsigned nworkers,
+ size_t *ringbuf_size, size_t *ringbuf_worker_size)
+{
+ if (ringbuf_size)
+ *ringbuf_size = offsetof(ringbuf_t, workers[nworkers]);
+ if (ringbuf_worker_size)
+ *ringbuf_worker_size = sizeof(ringbuf_worker_t);
+}
+
+/*
+ * ringbuf_register: register the worker (thread/process) as a producer
+ * and pass the pointer to its local store.
+ */
+ringbuf_worker_t *
+ringbuf_register(ringbuf_t *rbuf, unsigned i)
+{
+ ringbuf_worker_t *w = &rbuf->workers[i];
+
+ w->seen_off = RBUF_OFF_MAX;
+ atomic_store_explicit(&w->registered, true, memory_order_release);
+ return w;
+}
+
+void
+ringbuf_unregister(ringbuf_t *rbuf, ringbuf_worker_t *w)
+{
+ w->registered = false;
+ (void)rbuf;
+}
+
+/*
+ * stable_nextoff: capture and return a stable value of the 'next' offset.
+ */
+static inline ringbuf_off_t
+stable_nextoff(ringbuf_t *rbuf)
+{
+ unsigned count = SPINLOCK_BACKOFF_MIN;
+ ringbuf_off_t next;
+retry:
+ next = atomic_load_explicit(&rbuf->next, memory_order_acquire);
+ if (next & WRAP_LOCK_BIT) {
+ SPINLOCK_BACKOFF(count);
+ goto retry;
+ }
+ ASSERT((next & RBUF_OFF_MASK) < rbuf->space);
+ return next;
+}
+
+/*
+ * stable_seenoff: capture and return a stable value of the 'seen' offset.
+ */
+static inline ringbuf_off_t
+stable_seenoff(ringbuf_worker_t *w)
+{
+ unsigned count = SPINLOCK_BACKOFF_MIN;
+ ringbuf_off_t seen_off;
+retry:
+ seen_off = atomic_load_explicit(&w->seen_off, memory_order_acquire);
+ if (seen_off & WRAP_LOCK_BIT) {
+ SPINLOCK_BACKOFF(count);
+ goto retry;
+ }
+ return seen_off;
+}
+
+/*
+ * ringbuf_acquire: request a space of a given length in the ring buffer.
+ *
+ * => On success: returns the offset at which the space is available.
+ * => On failure: returns -1.
+ */
+ssize_t
+ringbuf_acquire(ringbuf_t *rbuf, ringbuf_worker_t *w, size_t len)
+{
+ ringbuf_off_t seen, next, target;
+
+ ASSERT(len > 0 && len <= rbuf->space);
+ ASSERT(w->seen_off == RBUF_OFF_MAX);
+
+ do {
+ ringbuf_off_t written;
+
+ /*
+ * Get the stable 'next' offset. Save the observed 'next'
+ * value (i.e. the 'seen' offset), but mark the value as
+ * unstable (set WRAP_LOCK_BIT).
+ *
+ * Note: CAS will issue a memory_order_release for us and
+ * thus ensures that it reaches global visibility together
+ * with new 'next'.
+ */
+ seen = stable_nextoff(rbuf);
+ next = seen & RBUF_OFF_MASK;
+ ASSERT(next < rbuf->space);
+ atomic_store_explicit(&w->seen_off, next | WRAP_LOCK_BIT,
+ memory_order_relaxed);
+
+ /*
+ * Compute the target offset. Key invariant: we cannot
+ * go beyond the WRITTEN offset or catch up with it.
+ */
+ target = next + len;
+ written = rbuf->written;
+ if (__predict_false(next < written && target >= written)) {
+ /* The producer must wait. */
+ atomic_store_explicit(&w->seen_off,
+ RBUF_OFF_MAX, memory_order_release);
+ return -1;
+ }
+
+ if (__predict_false(target >= rbuf->space)) {
+ const bool exceed = target > rbuf->space;
+
+ /*
+ * Wrap-around and start from the beginning.
+ *
+ * If we would exceed the buffer, then attempt to
+ * acquire the WRAP_LOCK_BIT and use the space in
+ * the beginning. If we used all space exactly to
+ * the end, then reset to 0.
+ *
+ * Check the invariant again.
+ */
+ target = exceed ? (WRAP_LOCK_BIT | len) : 0;
+ if ((target & RBUF_OFF_MASK) >= written) {
+ atomic_store_explicit(&w->seen_off,
+ RBUF_OFF_MAX, memory_order_release);
+ return -1;
+ }
+ /* Increment the wrap-around counter. */
+ target |= WRAP_INCR(seen & WRAP_COUNTER);
+ } else {
+ /* Preserve the wrap-around counter. */
+ target |= seen & WRAP_COUNTER;
+ }
+ } while (!atomic_compare_exchange_weak(&rbuf->next, &seen, target));
+
+ /*
+ * Acquired the range. Clear WRAP_LOCK_BIT in the 'seen' value
+ * thus indicating that it is stable now.
+ *
+ * No need for memory_order_release, since CAS issued a fence.
+ */
+ atomic_store_explicit(&w->seen_off, w->seen_off & ~WRAP_LOCK_BIT,
+ memory_order_relaxed);
+
+ /*
+ * If we set the WRAP_LOCK_BIT in the 'next' (because we exceed
+ * the remaining space and need to wrap-around), then save the
+ * 'end' offset and release the lock.
+ */
+ if (__predict_false(target & WRAP_LOCK_BIT)) {
+ /* Cannot wrap-around again if consumer did not catch-up. */
+ ASSERT(rbuf->written <= next);
+ ASSERT(rbuf->end == RBUF_OFF_MAX);
+ rbuf->end = next;
+ next = 0;
+
+ /*
+ * Unlock: ensure the 'end' offset reaches global
+ * visibility before the lock is released.
+ */
+ atomic_store_explicit(&rbuf->next,
+ (target & ~WRAP_LOCK_BIT), memory_order_release);
+ }
+ ASSERT((target & RBUF_OFF_MASK) <= rbuf->space);
+ return (ssize_t)next;
+}
+
+/*
+ * ringbuf_produce: indicate the acquired range in the buffer is produced
+ * and is ready to be consumed.
+ */
+void
+ringbuf_produce(ringbuf_t *rbuf, ringbuf_worker_t *w)
+{
+ (void)rbuf;
+ ASSERT(w->registered);
+ ASSERT(w->seen_off != RBUF_OFF_MAX);
+ atomic_store_explicit(&w->seen_off, RBUF_OFF_MAX, memory_order_release);
+}
+
+/*
+ * ringbuf_consume: get a contiguous range which is ready to be consumed.
+ */
+size_t
+ringbuf_consume(ringbuf_t *rbuf, size_t *offset)
+{
+ ringbuf_off_t written = rbuf->written, next, ready;
+ size_t towrite;
+retry:
+ /*
+ * Get the stable 'next' offset. Note: stable_nextoff() issued
+ * a load memory barrier. The area between the 'written' offset
+ * and the 'next' offset will be the *preliminary* target buffer
+ * area to be consumed.
+ */
+ next = stable_nextoff(rbuf) & RBUF_OFF_MASK;
+ if (written == next) {
+ /* If producers did not advance, then nothing to do. */
+ return 0;
+ }
+
+ /*
+ * Observe the 'ready' offset of each producer.
+ *
+ * At this point, some producer might have already triggered the
+ * wrap-around and some (or all) seen 'ready' values might be in
+ * the range between 0 and 'written'. We have to skip them.
+ */
+ ready = RBUF_OFF_MAX;
+
+ for (unsigned i = 0; i < rbuf->nworkers; i++) {
+ ringbuf_worker_t *w = &rbuf->workers[i];
+ ringbuf_off_t seen_off;
+
+ /*
+ * Skip if the worker has not registered.
+ *
+ * Get a stable 'seen' value. This is necessary since we
+ * want to discard the stale 'seen' values.
+ */
+ if (!atomic_load_explicit(&w->registered, memory_order_relaxed))
+ continue;
+ seen_off = stable_seenoff(w);
+
+ /*
+ * Ignore the offsets after the possible wrap-around.
+ * We are interested in the smallest seen offset that is
+ * not behind the 'written' offset.
+ */
+ if (seen_off >= written) {
+ ready = MIN(seen_off, ready);
+ }
+ ASSERT(ready >= written);
+ }
+
+ /*
+ * Finally, we need to determine whether wrap-around occurred
+ * and deduct the safe 'ready' offset.
+ */
+ if (next < written) {
+ const ringbuf_off_t end = MIN(rbuf->space, rbuf->end);
+
+ /*
+ * Wrap-around case. Check for the cut off first.
+ *
+ * Reset the 'written' offset if it reached the end of
+ * the buffer or the 'end' offset (if set by a producer).
+ * However, we must check that the producer is actually
+ * done (the observed 'ready' offsets are clear).
+ */
+ if (ready == RBUF_OFF_MAX && written == end) {
+ /*
+ * Clear the 'end' offset if was set.
+ */
+ if (rbuf->end != RBUF_OFF_MAX) {
+ rbuf->end = RBUF_OFF_MAX;
+ }
+
+ /*
+ * Wrap-around the consumer and start from zero.
+ */
+ written = 0;
+ atomic_store_explicit(&rbuf->written,
+ written, memory_order_release);
+ goto retry;
+ }
+
+ /*
+ * We cannot wrap-around yet; there is data to consume at
+ * the end. The ready range is smallest of the observed
+ * 'ready' or the 'end' offset. If neither is set, then
+ * the actual end of the buffer.
+ */
+ ASSERT(ready > next);
+ ready = MIN(ready, end);
+ ASSERT(ready >= written);
+ } else {
+ /*
+ * Regular case. Up to the observed 'ready' (if set)
+ * or the 'next' offset.
+ */
+ ready = MIN(ready, next);
+ }
+ towrite = ready - written;
+ *offset = written;
+
+ ASSERT(ready >= written);
+ ASSERT(towrite <= rbuf->space);
+ return towrite;
+}
+
+/*
+ * ringbuf_release: indicate that the consumed range can now be released.
+ */
+void
+ringbuf_release(ringbuf_t *rbuf, size_t nbytes)
+{
+ const size_t nwritten = rbuf->written + nbytes;
+
+ ASSERT(rbuf->written <= rbuf->space);
+ ASSERT(rbuf->written <= rbuf->end);
+ ASSERT(nwritten <= rbuf->space);
+
+ rbuf->written = (nwritten == rbuf->space) ? 0 : nwritten;
+}
diff --git a/deps/ringbuf/ringbuf.h b/deps/ringbuf/ringbuf.h
new file mode 100644
index 0000000..e8fc767
--- /dev/null
+++ b/deps/ringbuf/ringbuf.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 Mindaugas Rasiukevicius <rmind at noxt eu>
+ * All rights reserved.
+ *
+ * Use is subject to license terms, as specified in the LICENSE file.
+ */
+
+#ifndef _RINGBUF_H_
+#define _RINGBUF_H_
+
+__BEGIN_DECLS
+
+typedef struct ringbuf ringbuf_t;
+typedef struct ringbuf_worker ringbuf_worker_t;
+
+int ringbuf_setup(ringbuf_t *, unsigned, size_t);
+void ringbuf_get_sizes(unsigned, size_t *, size_t *);
+
+ringbuf_worker_t *ringbuf_register(ringbuf_t *, unsigned);
+void ringbuf_unregister(ringbuf_t *, ringbuf_worker_t *);
+
+ssize_t ringbuf_acquire(ringbuf_t *, ringbuf_worker_t *, size_t);
+void ringbuf_produce(ringbuf_t *, ringbuf_worker_t *);
+size_t ringbuf_consume(ringbuf_t *, size_t *);
+void ringbuf_release(ringbuf_t *, size_t);
+
+__END_DECLS
+
+#endif
diff --git a/deps/ringbuf/utils.h b/deps/ringbuf/utils.h
new file mode 100644
index 0000000..413157b
--- /dev/null
+++ b/deps/ringbuf/utils.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cdefs.h 8.8 (Berkeley) 1/9/95
+ */
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include <assert.h>
+
+/*
+ * A regular assert (debug/diagnostic only).
+ */
+#if defined(DEBUG)
+#define ASSERT assert
+#else
+#define ASSERT(x)
+#endif
+
+/*
+ * Minimum, maximum and rounding macros.
+ */
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+/*
+ * Branch prediction macros.
+ */
+#ifndef __predict_true
+#define __predict_true(x) __builtin_expect((x) != 0, 1)
+#define __predict_false(x) __builtin_expect((x) != 0, 0)
+#endif
+
+/*
+ * Atomic operations and memory barriers. If C11 API is not available,
+ * then wrap the GCC builtin routines.
+ *
+ * Note: This atomic_compare_exchange_weak does not do the C11 thing of
+ * filling *(expected) with the actual value, because we don't need
+ * that here.
+ */
+#ifndef atomic_compare_exchange_weak
+#define atomic_compare_exchange_weak(ptr, expected, desired) \
+ __sync_bool_compare_and_swap(ptr, *(expected), desired)
+#endif
+
+#ifndef atomic_thread_fence
+#define memory_order_relaxed __ATOMIC_RELAXED
+#define memory_order_acquire __ATOMIC_ACQUIRE
+#define memory_order_release __ATOMIC_RELEASE
+#define memory_order_seq_cst __ATOMIC_SEQ_CST
+#define atomic_thread_fence(m) __atomic_thread_fence(m)
+#endif
+#ifndef atomic_store_explicit
+#define atomic_store_explicit __atomic_store_n
+#endif
+#ifndef atomic_load_explicit
+#define atomic_load_explicit __atomic_load_n
+#endif
+
+/*
+ * Exponential back-off for the spinning paths.
+ */
+#define SPINLOCK_BACKOFF_MIN 4
+#define SPINLOCK_BACKOFF_MAX 128
+#if defined(__x86_64__) || defined(__i386__)
+#define SPINLOCK_BACKOFF_HOOK __asm volatile("pause" ::: "memory")
+#else
+#define SPINLOCK_BACKOFF_HOOK
+#endif
+#define SPINLOCK_BACKOFF(count) \
+do { \
+ for (int __i = (count); __i != 0; __i--) { \
+ SPINLOCK_BACKOFF_HOOK; \
+ } \
+ if ((count) < SPINLOCK_BACKOFF_MAX) \
+ (count) += (count); \
+} while (/* CONSTCOND */ 0);
+
+#endif
diff --git a/deps/sds/CMakeLists.txt b/deps/sds/CMakeLists.txt
new file mode 100644
index 0000000..116d6ae
--- /dev/null
+++ b/deps/sds/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_library(sds STATIC sds.c)
+target_include_directories(sds PUBLIC ${CMAKE_CURRENT_LIST_DIR}) \ No newline at end of file
diff --git a/deps/sds/sds.c b/deps/sds/sds.c
new file mode 100644
index 0000000..9510a8b
--- /dev/null
+++ b/deps/sds/sds.c
@@ -0,0 +1,1335 @@
+/* https://github.com/antirez/sds
+ * SDSLib 2.0 -- A C dynamic strings library
+ *
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2015, Oran Agra
+ * Copyright (c) 2015, Redis Labs, Inc
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+#include "sds.h"
+#include "sdsalloc.h"
+
+const char *SDS_NOINIT = "SDS_NOINIT";
+
+static inline int sdsHdrSize(char type) {
+ switch(type&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ return sizeof(struct sdshdr5);
+ case SDS_TYPE_8:
+ return sizeof(struct sdshdr8);
+ case SDS_TYPE_16:
+ return sizeof(struct sdshdr16);
+ case SDS_TYPE_32:
+ return sizeof(struct sdshdr32);
+ case SDS_TYPE_64:
+ return sizeof(struct sdshdr64);
+ }
+ return 0;
+}
+
+static inline char sdsReqType(size_t string_size) {
+ if (string_size < 1<<5)
+ return SDS_TYPE_5;
+ if (string_size < 1<<8)
+ return SDS_TYPE_8;
+ if (string_size < 1<<16)
+ return SDS_TYPE_16;
+#if (LONG_MAX == LLONG_MAX)
+ if (string_size < 1ll<<32)
+ return SDS_TYPE_32;
+ return SDS_TYPE_64;
+#else
+ return SDS_TYPE_32;
+#endif
+}
+
+/* Create a new sds string with the content specified by the 'init' pointer
+ * and 'initlen'.
+ * If NULL is used for 'init' the string is initialized with zero bytes.
+ * If SDS_NOINIT is used, the buffer is left uninitialized;
+ *
+ * The string is always null-terminated (all the sds strings are, always) so
+ * even if you create an sds string with:
+ *
+ * mystring = sdsnewlen("abc",3);
+ *
+ * You can print the string with printf() as there is an implicit \0 at the
+ * end of the string. However the string is binary safe and can contain
+ * \0 characters in the middle, as the length is stored in the sds header. */
+sds sdsnewlen(const void *init, size_t initlen) {
+ void *sh;
+ sds s;
+ char type = sdsReqType(initlen);
+ /* Empty strings are usually created in order to append. Use type 8
+ * since type 5 is not good at this. */
+ if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
+ int hdrlen = sdsHdrSize(type);
+ unsigned char *fp; /* flags pointer. */
+
+ sh = s_malloc(hdrlen+initlen+1);
+ if (sh == NULL) return NULL;
+ if (init==SDS_NOINIT)
+ init = NULL;
+ else if (!init)
+ memset(sh, 0, hdrlen+initlen+1);
+ s = (char*)sh+hdrlen;
+ fp = ((unsigned char*)s)-1;
+ switch(type) {
+ case SDS_TYPE_5: {
+ *fp = type | (initlen << SDS_TYPE_BITS);
+ break;
+ }
+ case SDS_TYPE_8: {
+ SDS_HDR_VAR(8,s);
+ sh->len = initlen;
+ sh->alloc = initlen;
+ *fp = type;
+ break;
+ }
+ case SDS_TYPE_16: {
+ SDS_HDR_VAR(16,s);
+ sh->len = initlen;
+ sh->alloc = initlen;
+ *fp = type;
+ break;
+ }
+ case SDS_TYPE_32: {
+ SDS_HDR_VAR(32,s);
+ sh->len = initlen;
+ sh->alloc = initlen;
+ *fp = type;
+ break;
+ }
+ case SDS_TYPE_64: {
+ SDS_HDR_VAR(64,s);
+ sh->len = initlen;
+ sh->alloc = initlen;
+ *fp = type;
+ break;
+ }
+ }
+ if (initlen && init)
+ memcpy(s, init, initlen);
+ s[initlen] = '\0';
+ return s;
+}
+
+/* Create an empty (zero length) sds string. Even in this case the string
+ * always has an implicit null term. */
+sds sdsempty(void) {
+ return sdsnewlen("",0);
+}
+
+/* Create a new sds string starting from a null terminated C string. */
+sds sdsnew(const char *init) {
+ size_t initlen = (init == NULL) ? 0 : strlen(init);
+ return sdsnewlen(init, initlen);
+}
+
+/* Duplicate an sds string. */
+sds sdsdup(const sds s) {
+ return sdsnewlen(s, sdslen(s));
+}
+
+/* Free an sds string. No operation is performed if 's' is NULL. */
+void sdsfree(sds s) {
+ if (s == NULL) return;
+ s_free((char*)s-sdsHdrSize(s[-1]));
+}
+
+/* Set the sds string length to the length as obtained with strlen(), so
+ * considering as content only up to the first null term character.
+ *
+ * This function is useful when the sds string is hacked manually in some
+ * way, like in the following example:
+ *
+ * s = sdsnew("foobar");
+ * s[2] = '\0';
+ * sdsupdatelen(s);
+ * printf("%d\n", sdslen(s));
+ *
+ * The output will be "2", but if we comment out the call to sdsupdatelen()
+ * the output will be "6" as the string was modified but the logical length
+ * remains 6 bytes. */
+void sdsupdatelen(sds s) {
+ size_t reallen = strlen(s);
+ sdssetlen(s, reallen);
+}
+
+/* Modify an sds string in-place to make it empty (zero length).
+ * However all the existing buffer is not discarded but set as free space
+ * so that next append operations will not require allocations up to the
+ * number of bytes previously available. */
+void sdsclear(sds s) {
+ sdssetlen(s, 0);
+ s[0] = '\0';
+}
+
+/* Enlarge the free space at the end of the sds string so that the caller
+ * is sure that after calling this function can overwrite up to addlen
+ * bytes after the end of the string, plus one more byte for nul term.
+ *
+ * Note: this does not change the *length* of the sds string as returned
+ * by sdslen(), but only the free buffer space we have. */
+sds sdsMakeRoomFor(sds s, size_t addlen) {
+ void *sh, *newsh;
+ size_t avail = sdsavail(s);
+ size_t len, newlen;
+#ifndef NDEBUG
+ size_t reqlen;
+#endif
+ char type, oldtype = s[-1] & SDS_TYPE_MASK;
+ int hdrlen;
+
+ /* Return ASAP if there is enough space left. */
+ if (avail >= addlen) return s;
+
+ len = sdslen(s);
+ sh = (char*)s-sdsHdrSize(oldtype);
+#ifndef NDEBUG
+ reqlen = (len+addlen);
+#endif
+ newlen = (len+addlen);
+ if (newlen < SDS_MAX_PREALLOC)
+ newlen *= 2;
+ else
+ newlen += SDS_MAX_PREALLOC;
+
+ type = sdsReqType(newlen);
+
+ /* Don't use type 5: the user is appending to the string and type 5 is
+ * not able to remember empty space, so sdsMakeRoomFor() must be called
+ * at every appending operation. */
+ if (type == SDS_TYPE_5) type = SDS_TYPE_8;
+
+ hdrlen = sdsHdrSize(type);
+ assert(hdrlen + newlen + 1 > reqlen); /* Catch size_t overflow */
+ if (oldtype==type) {
+ newsh = s_realloc(sh, hdrlen+newlen+1);
+ if (newsh == NULL) return NULL;
+ s = (char*)newsh+hdrlen;
+ } else {
+ /* Since the header size changes, need to move the string forward,
+ * and can't use realloc */
+ newsh = s_malloc(hdrlen+newlen+1);
+ if (newsh == NULL) return NULL;
+ memcpy((char*)newsh+hdrlen, s, len+1);
+ s_free(sh);
+ s = (char*)newsh+hdrlen;
+ s[-1] = type;
+ sdssetlen(s, len);
+ }
+ sdssetalloc(s, newlen);
+ return s;
+}
+
+/* Reallocate the sds string so that it has no free space at the end. The
+ * contained string remains not altered, but next concatenation operations
+ * will require a reallocation.
+ *
+ * After the call, the passed sds string is no longer valid and all the
+ * references must be substituted with the new pointer returned by the call. */
+sds sdsRemoveFreeSpace(sds s) {
+ void *sh, *newsh;
+ char type, oldtype = s[-1] & SDS_TYPE_MASK;
+ int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
+ size_t len = sdslen(s);
+ size_t avail = sdsavail(s);
+ sh = (char*)s-oldhdrlen;
+
+ /* Return ASAP if there is no space left. */
+ if (avail == 0) return s;
+
+ /* Check what would be the minimum SDS header that is just good enough to
+ * fit this string. */
+ type = sdsReqType(len);
+ hdrlen = sdsHdrSize(type);
+
+ /* If the type is the same, or at least a large enough type is still
+ * required, we just realloc(), letting the allocator to do the copy
+ * only if really needed. Otherwise if the change is huge, we manually
+ * reallocate the string to use the different header type. */
+ if (oldtype==type || type > SDS_TYPE_8) {
+ newsh = s_realloc(sh, oldhdrlen+len+1);
+ if (newsh == NULL) return NULL;
+ s = (char*)newsh+oldhdrlen;
+ } else {
+ newsh = s_malloc(hdrlen+len+1);
+ if (newsh == NULL) return NULL;
+ memcpy((char*)newsh+hdrlen, s, len+1);
+ s_free(sh);
+ s = (char*)newsh+hdrlen;
+ s[-1] = type;
+ sdssetlen(s, len);
+ }
+ sdssetalloc(s, len);
+ return s;
+}
+
+/* Return the total size of the allocation of the specified sds string,
+ * including:
+ * 1) The sds header before the pointer.
+ * 2) The string.
+ * 3) The free buffer at the end if any.
+ * 4) The implicit null term.
+ */
+size_t sdsAllocSize(sds s) {
+ size_t alloc = sdsalloc(s);
+ return sdsHdrSize(s[-1])+alloc+1;
+}
+
+/* Return the pointer of the actual SDS allocation (normally SDS strings
+ * are referenced by the start of the string buffer). */
+void *sdsAllocPtr(sds s) {
+ return (void*) (s-sdsHdrSize(s[-1]));
+}
+
+/* Increment the sds length and decrements the left free space at the
+ * end of the string according to 'incr'. Also set the null term
+ * in the new end of the string.
+ *
+ * This function is used in order to fix the string length after the
+ * user calls sdsMakeRoomFor(), writes something after the end of
+ * the current string, and finally needs to set the new length.
+ *
+ * Note: it is possible to use a negative increment in order to
+ * right-trim the string.
+ *
+ * Usage example:
+ *
+ * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
+ * following schema, to cat bytes coming from the kernel to the end of an
+ * sds string without copying into an intermediate buffer:
+ *
+ * oldlen = sdslen(s);
+ * s = sdsMakeRoomFor(s, BUFFER_SIZE);
+ * nread = read(fd, s+oldlen, BUFFER_SIZE);
+ * ... check for nread <= 0 and handle it ...
+ * sdsIncrLen(s, nread);
+ */
+void sdsIncrLen(sds s, ssize_t incr) {
+ unsigned char flags = s[-1];
+ size_t len;
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5: {
+ unsigned char *fp = ((unsigned char*)s)-1;
+ unsigned char oldlen = SDS_TYPE_5_LEN(flags);
+ assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));
+ *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);
+ len = oldlen+incr;
+ break;
+ }
+ case SDS_TYPE_8: {
+ SDS_HDR_VAR(8,s);
+ assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
+ len = (sh->len += incr);
+ break;
+ }
+ case SDS_TYPE_16: {
+ SDS_HDR_VAR(16,s);
+ assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
+ len = (sh->len += incr);
+ break;
+ }
+ case SDS_TYPE_32: {
+ SDS_HDR_VAR(32,s);
+ assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
+ len = (sh->len += incr);
+ break;
+ }
+ case SDS_TYPE_64: {
+ SDS_HDR_VAR(64,s);
+ assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));
+ len = (sh->len += incr);
+ break;
+ }
+ default: len = 0; /* Just to avoid compilation warnings. */
+ }
+ s[len] = '\0';
+}
+
+/* Grow the sds to have the specified length. Bytes that were not part of
+ * the original length of the sds will be set to zero.
+ *
+ * if the specified length is smaller than the current length, no operation
+ * is performed. */
+sds sdsgrowzero(sds s, size_t len) {
+ size_t curlen = sdslen(s);
+
+ if (len <= curlen) return s;
+ s = sdsMakeRoomFor(s,len-curlen);
+ if (s == NULL) return NULL;
+
+ /* Make sure added region doesn't contain garbage */
+ memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
+ sdssetlen(s, len);
+ return s;
+}
+
+/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
+ * end of the specified sds string 's'.
+ *
+ * After the call, the passed sds string is no longer valid and all the
+ * references must be substituted with the new pointer returned by the call. */
+sds sdscatlen(sds s, const void *t, size_t len) {
+ size_t curlen = sdslen(s);
+
+ s = sdsMakeRoomFor(s,len);
+ if (s == NULL) return NULL;
+ memcpy(s+curlen, t, len);
+ sdssetlen(s, curlen+len);
+ s[curlen+len] = '\0';
+ return s;
+}
+
+/* Append the specified null termianted C string to the sds string 's'.
+ *
+ * After the call, the passed sds string is no longer valid and all the
+ * references must be substituted with the new pointer returned by the call. */
+sds sdscat(sds s, const char *t) {
+ return sdscatlen(s, t, strlen(t));
+}
+
+/* Append the specified sds 't' to the existing sds 's'.
+ *
+ * After the call, the modified sds string is no longer valid and all the
+ * references must be substituted with the new pointer returned by the call. */
+sds sdscatsds(sds s, const sds t) {
+ return sdscatlen(s, t, sdslen(t));
+}
+
+/* Destructively modify the sds string 's' to hold the specified binary
+ * safe string pointed by 't' of length 'len' bytes. */
+sds sdscpylen(sds s, const char *t, size_t len) {
+ if (sdsalloc(s) < len) {
+ s = sdsMakeRoomFor(s,len-sdslen(s));
+ if (s == NULL) return NULL;
+ }
+ memcpy(s, t, len);
+ s[len] = '\0';
+ sdssetlen(s, len);
+ return s;
+}
+
+/* Like sdscpylen() but 't' must be a null-terminated string so that the length
+ * of the string is obtained with strlen(). */
+sds sdscpy(sds s, const char *t) {
+ return sdscpylen(s, t, strlen(t));
+}
+
+/* Helper for sdscatlonglong() doing the actual number -> string
+ * conversion. 's' must point to a string with room for at least
+ * SDS_LLSTR_SIZE bytes.
+ *
+ * The function returns the length of the null-terminated string
+ * representation stored at 's'. */
+#define SDS_LLSTR_SIZE 21
+int sdsll2str(char *s, long long value) {
+ char *p, aux;
+ unsigned long long v;
+ size_t l;
+
+ /* Generate the string representation, this method produces
+ * an reversed string. */
+ if (value < 0) {
+ /* Since v is unsigned, if value==LLONG_MIN then
+ * -LLONG_MIN will overflow. */
+ if (value != LLONG_MIN) {
+ v = -value;
+ } else {
+ v = ((unsigned long long)LLONG_MAX) + 1;
+ }
+ } else {
+ v = value;
+ }
+
+ p = s;
+ do {
+ *p++ = '0'+(v%10);
+ v /= 10;
+ } while(v);
+ if (value < 0) *p++ = '-';
+
+ /* Compute length and add null term. */
+ l = p-s;
+ *p = '\0';
+
+ /* Reverse the string. */
+ p--;
+ while(s < p) {
+ aux = *s;
+ *s = *p;
+ *p = aux;
+ s++;
+ p--;
+ }
+ return l;
+}
+
+/* Identical sdsll2str(), but for unsigned long long type. */
+int sdsull2str(char *s, unsigned long long v) {
+ char *p, aux;
+ size_t l;
+
+ /* Generate the string representation, this method produces
+ * an reversed string. */
+ p = s;
+ do {
+ *p++ = '0'+(v%10);
+ v /= 10;
+ } while(v);
+
+ /* Compute length and add null term. */
+ l = p-s;
+ *p = '\0';
+
+ /* Reverse the string. */
+ p--;
+ while(s < p) {
+ aux = *s;
+ *s = *p;
+ *p = aux;
+ s++;
+ p--;
+ }
+ return l;
+}
+
+/* Create an sds string from a long long value. It is much faster than:
+ *
+ * sdscatprintf(sdsempty(),"%lld\n", value);
+ */
+sds sdsfromlonglong(long long value) {
+ char buf[SDS_LLSTR_SIZE];
+ int len = sdsll2str(buf,value);
+
+ return sdsnewlen(buf,len);
+}
+
+/* Like sdscatprintf() but gets va_list instead of being variadic. */
+sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
+ va_list cpy;
+ char staticbuf[1024], *buf = staticbuf, *t;
+ size_t buflen = strlen(fmt)*2;
+ int bufstrlen;
+
+ /* We try to start using a static buffer for speed.
+ * If not possible we revert to heap allocation. */
+ if (buflen > sizeof(staticbuf)) {
+ buf = s_malloc(buflen);
+ if (buf == NULL) return NULL;
+ } else {
+ buflen = sizeof(staticbuf);
+ }
+
+ /* Alloc enough space for buffer and \0 after failing to
+ * fit the string in the current buffer size. */
+ while(1) {
+ va_copy(cpy,ap);
+ bufstrlen = vsnprintf(buf, buflen, fmt, cpy);
+ va_end(cpy);
+ if (bufstrlen < 0) {
+ if (buf != staticbuf) s_free(buf);
+ return NULL;
+ }
+ if (((size_t)bufstrlen) >= buflen) {
+ if (buf != staticbuf) s_free(buf);
+ buflen = ((size_t)bufstrlen) + 1;
+ buf = s_malloc(buflen);
+ if (buf == NULL) return NULL;
+ continue;
+ }
+ break;
+ }
+
+ /* Finally concat the obtained string to the SDS string and return it. */
+ t = sdscatlen(s, buf, bufstrlen);
+ if (buf != staticbuf) s_free(buf);
+ return t;
+}
+
+/* Append to the sds string 's' a string obtained using printf-alike format
+ * specifier.
+ *
+ * After the call, the modified sds string is no longer valid and all the
+ * references must be substituted with the new pointer returned by the call.
+ *
+ * Example:
+ *
+ * s = sdsnew("Sum is: ");
+ * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
+ *
+ * Often you need to create a string from scratch with the printf-alike
+ * format. When this is the need, just use sdsempty() as the target string:
+ *
+ * s = sdscatprintf(sdsempty(), "... your format ...", args);
+ */
+sds sdscatprintf(sds s, const char *fmt, ...) {
+ va_list ap;
+ char *t;
+ va_start(ap, fmt);
+ t = sdscatvprintf(s,fmt,ap);
+ va_end(ap);
+ return t;
+}
+
+/* This function is similar to sdscatprintf, but much faster as it does
+ * not rely on sprintf() family functions implemented by the libc that
+ * are often very slow. Moreover directly handling the sds string as
+ * new data is concatenated provides a performance improvement.
+ *
+ * However this function only handles an incompatible subset of printf-alike
+ * format specifiers:
+ *
+ * %s - C String
+ * %S - SDS string
+ * %i - signed int
+ * %I - 64 bit signed integer (long long, int64_t)
+ * %u - unsigned int
+ * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
+ * %% - Verbatim "%" character.
+ */
+sds sdscatfmt(sds s, const char *fmt, ...) {
+ size_t initlen = sdslen(s);
+ const char *f;
+ long i;
+ va_list ap;
+
+ /* To avoid continuous reallocations, let's start with a buffer that
+ * can hold at least two times the format string itself. It's not the
+ * best heuristic but seems to work in practice. */
+ s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2);
+ va_start(ap,fmt);
+ f = (const char *)fmt; /* Next format specifier byte to process. */
+ i = initlen; /* Position of the next byte to write to dest str. */
+ while(*f) {
+ char next, *str;
+ size_t l;
+ long long num;
+ unsigned long long unum;
+
+ /* Make sure there is always space for at least 1 char. */
+ if (sdsavail(s)==0) {
+ s = sdsMakeRoomFor(s,1);
+ }
+
+ switch(*f) {
+ case '%':
+ next = *(f+1);
+ if (next == '\0') break;
+ f++;
+ switch(next) {
+ case 's':
+ case 'S':
+ str = va_arg(ap,char*);
+ l = (next == 's') ? strlen(str) : sdslen(str);
+ if (sdsavail(s) < l) {
+ s = sdsMakeRoomFor(s,l);
+ }
+ memcpy(s+i,str,l);
+ sdsinclen(s,l);
+ i += l;
+ break;
+ case 'i':
+ case 'I':
+ if (next == 'i')
+ num = va_arg(ap,int);
+ else
+ num = va_arg(ap,long long);
+ {
+ char buf[SDS_LLSTR_SIZE];
+ l = sdsll2str(buf,num);
+ if (sdsavail(s) < l) {
+ s = sdsMakeRoomFor(s,l);
+ }
+ memcpy(s+i,buf,l);
+ sdsinclen(s,l);
+ i += l;
+ }
+ break;
+ case 'u':
+ case 'U':
+ if (next == 'u')
+ unum = va_arg(ap,unsigned int);
+ else
+ unum = va_arg(ap,unsigned long long);
+ {
+ char buf[SDS_LLSTR_SIZE];
+ l = sdsull2str(buf,unum);
+ if (sdsavail(s) < l) {
+ s = sdsMakeRoomFor(s,l);
+ }
+ memcpy(s+i,buf,l);
+ sdsinclen(s,l);
+ i += l;
+ }
+ break;
+ default: /* Handle %% and generally %<unknown>. */
+ s[i++] = next;
+ sdsinclen(s,1);
+ break;
+ }
+ break;
+ default:
+ s[i++] = *f;
+ sdsinclen(s,1);
+ break;
+ }
+ f++;
+ }
+ va_end(ap);
+
+ /* Add null-term */
+ s[i] = '\0';
+ return s;
+}
+
+/* Remove the part of the string from left and from right composed just of
+ * contiguous characters found in 'cset', that is a null terminated C string.
+ *
+ * After the call, the modified sds string is no longer valid and all the
+ * references must be substituted with the new pointer returned by the call.
+ *
+ * Example:
+ *
+ * s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
+ * s = sdstrim(s,"Aa. :");
+ * printf("%s\n", s);
+ *
+ * Output will be just "HelloWorld".
+ */
+sds sdstrim(sds s, const char *cset) {
+ char *end, *sp, *ep;
+ size_t len;
+
+ sp = s;
+ ep = end = s+sdslen(s)-1;
+ while(sp <= end && strchr(cset, *sp)) sp++;
+ while(ep > sp && strchr(cset, *ep)) ep--;
+ len = (ep-sp)+1;
+ if (s != sp) memmove(s, sp, len);
+ s[len] = '\0';
+ sdssetlen(s,len);
+ return s;
+}
+
+/* Turn the string into a smaller (or equal) string containing only the
+ * substring specified by the 'start' and 'end' indexes.
+ *
+ * start and end can be negative, where -1 means the last character of the
+ * string, -2 the penultimate character, and so forth.
+ *
+ * The interval is inclusive, so the start and end characters will be part
+ * of the resulting string.
+ *
+ * The string is modified in-place.
+ *
+ * Example:
+ *
+ * s = sdsnew("Hello World");
+ * sdsrange(s,1,-1); => "ello World"
+ */
+void sdsrange(sds s, ssize_t start, ssize_t end) {
+ size_t newlen, len = sdslen(s);
+
+ if (len == 0) return;
+ if (start < 0) {
+ start = len+start;
+ if (start < 0) start = 0;
+ }
+ if (end < 0) {
+ end = len+end;
+ if (end < 0) end = 0;
+ }
+ newlen = (start > end) ? 0 : (end-start)+1;
+ if (newlen != 0) {
+ if (start >= (ssize_t)len) {
+ newlen = 0;
+ } else if (end >= (ssize_t)len) {
+ end = len-1;
+ newlen = (end-start)+1;
+ }
+ }
+ if (start && newlen) memmove(s, s+start, newlen);
+ s[newlen] = 0;
+ sdssetlen(s,newlen);
+}
+
+/* Apply tolower() to every character of the sds string 's'. */
+void sdstolower(sds s) {
+ size_t len = sdslen(s), j;
+
+ for (j = 0; j < len; j++) s[j] = tolower(s[j]);
+}
+
+/* Apply toupper() to every character of the sds string 's'. */
+void sdstoupper(sds s) {
+ size_t len = sdslen(s), j;
+
+ for (j = 0; j < len; j++) s[j] = toupper(s[j]);
+}
+
+/* Compare two sds strings s1 and s2 with memcmp().
+ *
+ * Return value:
+ *
+ * positive if s1 > s2.
+ * negative if s1 < s2.
+ * 0 if s1 and s2 are exactly the same binary string.
+ *
+ * If two strings share exactly the same prefix, but one of the two has
+ * additional characters, the longer string is considered to be greater than
+ * the smaller one. */
+int sdscmp(const sds s1, const sds s2) {
+ size_t l1, l2, minlen;
+ int cmp;
+
+ l1 = sdslen(s1);
+ l2 = sdslen(s2);
+ minlen = (l1 < l2) ? l1 : l2;
+ cmp = memcmp(s1,s2,minlen);
+ if (cmp == 0) return l1>l2? 1: (l1<l2? -1: 0);
+ return cmp;
+}
+
+/* Split 's' with separator in 'sep'. An array
+ * of sds strings is returned. *count will be set
+ * by reference to the number of tokens returned.
+ *
+ * On out of memory, zero length string, zero length
+ * separator, NULL is returned.
+ *
+ * Note that 'sep' is able to split a string using
+ * a multi-character separator. For example
+ * sdssplit("foo_-_bar","_-_"); will return two
+ * elements "foo" and "bar".
+ *
+ * This version of the function is binary-safe but
+ * requires length arguments. sdssplit() is just the
+ * same function but for zero-terminated strings.
+ */
+sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count) {
+ int elements = 0, slots = 5;
+ long start = 0, j;
+ sds *tokens;
+
+ if (seplen < 1 || len <= 0) {
+ *count = 0;
+ return NULL;
+ }
+
+ tokens = s_malloc(sizeof(sds)*slots);
+ if (tokens == NULL) return NULL;
+
+ for (j = 0; j < (len-(seplen-1)); j++) {
+ /* make sure there is room for the next element and the final one */
+ if (slots < elements+2) {
+ sds *newtokens;
+
+ slots *= 2;
+ newtokens = s_realloc(tokens,sizeof(sds)*slots);
+ if (newtokens == NULL) goto cleanup;
+ tokens = newtokens;
+ }
+ /* search the separator */
+ if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
+ tokens[elements] = sdsnewlen(s+start,j-start);
+ if (tokens[elements] == NULL) goto cleanup;
+ elements++;
+ start = j+seplen;
+ j = j+seplen-1; /* skip the separator */
+ }
+ }
+ /* Add the final element. We are sure there is room in the tokens array. */
+ tokens[elements] = sdsnewlen(s+start,len-start);
+ if (tokens[elements] == NULL) goto cleanup;
+ elements++;
+ *count = elements;
+ return tokens;
+
+cleanup:
+ {
+ int i;
+ for (i = 0; i < elements; i++) sdsfree(tokens[i]);
+ s_free(tokens);
+ *count = 0;
+ return NULL;
+ }
+}
+
+/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
+void sdsfreesplitres(sds *tokens, int count) {
+ if (!tokens) return;
+ while(count--)
+ sdsfree(tokens[count]);
+ s_free(tokens);
+}
+
+/* Append to the sds string "s" an escaped string representation where
+ * all the non-printable characters (tested with isprint()) are turned into
+ * escapes in the form "\n\r\a...." or "\x<hex-number>".
+ *
+ * After the call, the modified sds string is no longer valid and all the
+ * references must be substituted with the new pointer returned by the call. */
+sds sdscatrepr(sds s, const char *p, size_t len) {
+ s = sdscatlen(s,"\"",1);
+ while(len--) {
+ switch(*p) {
+ case '\\':
+ case '"':
+ s = sdscatprintf(s,"\\%c",*p);
+ break;
+ case '\n': s = sdscatlen(s,"\\n",2); break;
+ case '\r': s = sdscatlen(s,"\\r",2); break;
+ case '\t': s = sdscatlen(s,"\\t",2); break;
+ case '\a': s = sdscatlen(s,"\\a",2); break;
+ case '\b': s = sdscatlen(s,"\\b",2); break;
+ default:
+ if (isprint(*p))
+ s = sdscatprintf(s,"%c",*p);
+ else
+ s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
+ break;
+ }
+ p++;
+ }
+ return sdscatlen(s,"\"",1);
+}
+
+/* Helper function for sdssplitargs() that returns non zero if 'c'
+ * is a valid hex digit. */
+int is_hex_digit(char c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F');
+}
+
+/* Helper function for sdssplitargs() that converts a hex digit into an
+ * integer from 0 to 15 */
+int hex_digit_to_int(char c) {
+ switch(c) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ case 'a': case 'A': return 10;
+ case 'b': case 'B': return 11;
+ case 'c': case 'C': return 12;
+ case 'd': case 'D': return 13;
+ case 'e': case 'E': return 14;
+ case 'f': case 'F': return 15;
+ default: return 0;
+ }
+}
+
+/* Split a line into arguments, where every argument can be in the
+ * following programming-language REPL-alike form:
+ *
+ * foo bar "newline are supported\n" and "\xff\x00otherstuff"
+ *
+ * The number of arguments is stored into *argc, and an array
+ * of sds is returned.
+ *
+ * The caller should free the resulting array of sds strings with
+ * sdsfreesplitres().
+ *
+ * Note that sdscatrepr() is able to convert back a string into
+ * a quoted string in the same format sdssplitargs() is able to parse.
+ *
+ * The function returns the allocated tokens on success, even when the
+ * input string is empty, or NULL if the input contains unbalanced
+ * quotes or closed quotes followed by non space characters
+ * as in: "foo"bar or "foo'
+ */
+sds *sdssplitargs(const char *line, int *argc) {
+ const char *p = line;
+ char *current = NULL;
+ char **vector = NULL;
+
+ *argc = 0;
+ while(1) {
+ /* skip blanks */
+ while(*p && isspace(*p)) p++;
+ if (*p) {
+ /* get a token */
+ int inq=0; /* set to 1 if we are in "quotes" */
+ int insq=0; /* set to 1 if we are in 'single quotes' */
+ int done=0;
+
+ if (current == NULL) current = sdsempty();
+ while(!done) {
+ if (inq) {
+ if (*p == '\\' && *(p+1) == 'x' &&
+ is_hex_digit(*(p+2)) &&
+ is_hex_digit(*(p+3)))
+ {
+ unsigned char byte;
+
+ byte = (hex_digit_to_int(*(p+2))*16)+
+ hex_digit_to_int(*(p+3));
+ current = sdscatlen(current,(char*)&byte,1);
+ p += 3;
+ } else if (*p == '\\' && *(p+1)) {
+ char c;
+
+ p++;
+ switch(*p) {
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'b': c = '\b'; break;
+ case 'a': c = '\a'; break;
+ default: c = *p; break;
+ }
+ current = sdscatlen(current,&c,1);
+ } else if (*p == '"') {
+ /* closing quote must be followed by a space or
+ * nothing at all. */
+ if (*(p+1) && !isspace(*(p+1))) goto err;
+ done=1;
+ } else if (!*p) {
+ /* unterminated quotes */
+ goto err;
+ } else {
+ current = sdscatlen(current,p,1);
+ }
+ } else if (insq) {
+ if (*p == '\\' && *(p+1) == '\'') {
+ p++;
+ current = sdscatlen(current,"'",1);
+ } else if (*p == '\'') {
+ /* closing quote must be followed by a space or
+ * nothing at all. */
+ if (*(p+1) && !isspace(*(p+1))) goto err;
+ done=1;
+ } else if (!*p) {
+ /* unterminated quotes */
+ goto err;
+ } else {
+ current = sdscatlen(current,p,1);
+ }
+ } else {
+ switch(*p) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\0':
+ done=1;
+ break;
+ case '"':
+ inq=1;
+ break;
+ case '\'':
+ insq=1;
+ break;
+ default:
+ current = sdscatlen(current,p,1);
+ break;
+ }
+ }
+ if (*p) p++;
+ }
+ /* add the token to the vector */
+ vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
+ vector[*argc] = current;
+ (*argc)++;
+ current = NULL;
+ } else {
+ /* Even on empty input string return something not NULL. */
+ if (vector == NULL) vector = s_malloc(sizeof(void*));
+ return vector;
+ }
+ }
+
+err:
+ while((*argc)--)
+ sdsfree(vector[*argc]);
+ s_free(vector);
+ if (current) sdsfree(current);
+ *argc = 0;
+ return NULL;
+}
+
+/* Modify the string substituting all the occurrences of the set of
+ * characters specified in the 'from' string to the corresponding character
+ * in the 'to' array.
+ *
+ * For instance: sdsmapchars(mystring, "ho", "01", 2)
+ * will have the effect of turning the string "hello" into "0ell1".
+ *
+ * The function returns the sds string pointer, that is always the same
+ * as the input pointer since no resize is needed. */
+sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
+ size_t j, i, l = sdslen(s);
+
+ for (j = 0; j < l; j++) {
+ for (i = 0; i < setlen; i++) {
+ if (s[j] == from[i]) {
+ s[j] = to[i];
+ break;
+ }
+ }
+ }
+ return s;
+}
+
+/* Join an array of C strings using the specified separator (also a C string).
+ * Returns the result as an sds string. */
+sds sdsjoin(char **argv, int argc, char *sep) {
+ sds join = sdsempty();
+ int j;
+
+ for (j = 0; j < argc; j++) {
+ join = sdscat(join, argv[j]);
+ if (j != argc-1) join = sdscat(join,sep);
+ }
+ return join;
+}
+
+/* Like sdsjoin, but joins an array of SDS strings. */
+sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
+ sds join = sdsempty();
+ int j;
+
+ for (j = 0; j < argc; j++) {
+ join = sdscatsds(join, argv[j]);
+ if (j != argc-1) join = sdscatlen(join,sep,seplen);
+ }
+ return join;
+}
+
+/* Wrappers to the allocators used by SDS. Note that SDS will actually
+ * just use the macros defined into sdsalloc.h in order to avoid to pay
+ * the overhead of function calls. Here we define these wrappers only for
+ * the programs SDS is linked to, if they want to touch the SDS internals
+ * even if they use a different allocator. */
+void *sds_malloc(size_t size) { return s_malloc(size); }
+void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }
+void sds_free(void *ptr) { s_free(ptr); }
+
+#if defined(SDS_TEST_MAIN)
+#include <stdio.h>
+#include "testhelp.h"
+#include "limits.h"
+
+#define UNUSED(x) (void)(x)
+int sdsTest(void) {
+ {
+ sds x = sdsnew("foo"), y;
+
+ test_cond("Create a string and obtain the length",
+ sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
+
+ sdsfree(x);
+ x = sdsnewlen("foo",2);
+ test_cond("Create a string with specified length",
+ sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
+
+ x = sdscat(x,"bar");
+ test_cond("Strings concatenation",
+ sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
+
+ x = sdscpy(x,"a");
+ test_cond("sdscpy() against an originally longer string",
+ sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
+
+ x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
+ test_cond("sdscpy() against an originally shorter string",
+ sdslen(x) == 33 &&
+ memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
+
+ sdsfree(x);
+ x = sdscatprintf(sdsempty(),"%d",123);
+ test_cond("sdscatprintf() seems working in the base case",
+ sdslen(x) == 3 && memcmp(x,"123\0",4) == 0)
+
+ sdsfree(x);
+ x = sdscatprintf(sdsempty(),"a%cb",0);
+ test_cond("sdscatprintf() seems working with \\0 inside of result",
+ sdslen(x) == 3 && memcmp(x,"a\0""b\0",4) == 0)
+
+ {
+ sdsfree(x);
+ char etalon[1024*1024];
+ for (size_t i = 0; i < sizeof(etalon); i++) {
+ etalon[i] = '0';
+ }
+ x = sdscatprintf(sdsempty(),"%0*d",(int)sizeof(etalon),0);
+ test_cond("sdscatprintf() can print 1MB",
+ sdslen(x) == sizeof(etalon) && memcmp(x,etalon,sizeof(etalon)) == 0)
+ }
+
+ sdsfree(x);
+ x = sdsnew("--");
+ x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
+ test_cond("sdscatfmt() seems working in the base case",
+ sdslen(x) == 60 &&
+ memcmp(x,"--Hello Hi! World -9223372036854775808,"
+ "9223372036854775807--",60) == 0)
+ printf("[%s]\n",x);
+
+ sdsfree(x);
+ x = sdsnew("--");
+ x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX);
+ test_cond("sdscatfmt() seems working with unsigned numbers",
+ sdslen(x) == 35 &&
+ memcmp(x,"--4294967295,18446744073709551615--",35) == 0)
+
+ sdsfree(x);
+ x = sdsnew(" x ");
+ sdstrim(x," x");
+ test_cond("sdstrim() works when all chars match",
+ sdslen(x) == 0)
+
+ sdsfree(x);
+ x = sdsnew(" x ");
+ sdstrim(x," ");
+ test_cond("sdstrim() works when a single char remains",
+ sdslen(x) == 1 && x[0] == 'x')
+
+ sdsfree(x);
+ x = sdsnew("xxciaoyyy");
+ sdstrim(x,"xy");
+ test_cond("sdstrim() correctly trims characters",
+ sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
+
+ y = sdsdup(x);
+ sdsrange(y,1,1);
+ test_cond("sdsrange(...,1,1)",
+ sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
+
+ sdsfree(y);
+ y = sdsdup(x);
+ sdsrange(y,1,-1);
+ test_cond("sdsrange(...,1,-1)",
+ sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
+
+ sdsfree(y);
+ y = sdsdup(x);
+ sdsrange(y,-2,-1);
+ test_cond("sdsrange(...,-2,-1)",
+ sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
+
+ sdsfree(y);
+ y = sdsdup(x);
+ sdsrange(y,2,1);
+ test_cond("sdsrange(...,2,1)",
+ sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
+
+ sdsfree(y);
+ y = sdsdup(x);
+ sdsrange(y,1,100);
+ test_cond("sdsrange(...,1,100)",
+ sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
+
+ sdsfree(y);
+ y = sdsdup(x);
+ sdsrange(y,100,100);
+ test_cond("sdsrange(...,100,100)",
+ sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
+
+ sdsfree(y);
+ sdsfree(x);
+ x = sdsnew("foo");
+ y = sdsnew("foa");
+ test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
+
+ sdsfree(y);
+ sdsfree(x);
+ x = sdsnew("bar");
+ y = sdsnew("bar");
+ test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
+
+ sdsfree(y);
+ sdsfree(x);
+ x = sdsnew("aar");
+ y = sdsnew("bar");
+ test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
+
+ sdsfree(y);
+ sdsfree(x);
+ x = sdsnewlen("\a\n\0foo\r",7);
+ y = sdscatrepr(sdsempty(),x,sdslen(x));
+ test_cond("sdscatrepr(...data...)",
+ memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
+
+ {
+ char *p;
+ int step = 10, j, i;
+
+ sdsfree(x);
+ sdsfree(y);
+ x = sdsnew("0");
+ test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0);
+
+ /* Run the test a few times in order to hit the first two
+ * SDS header types. */
+ for (i = 0; i < 10; i++) {
+ int oldlen = sdslen(x);
+ x = sdsMakeRoomFor(x,step);
+ int type = x[-1]&SDS_TYPE_MASK;
+
+ test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen);
+ if (type != SDS_TYPE_5) {
+ test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step);
+ }
+ p = x+oldlen;
+ for (j = 0; j < step; j++) {
+ p[j] = 'A'+j;
+ }
+ sdsIncrLen(x,step);
+ }
+ test_cond("sdsMakeRoomFor() content",
+ memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0);
+ test_cond("sdsMakeRoomFor() final length",sdslen(x)==101);
+
+ sdsfree(x);
+ }
+ }
+ test_report()
+ return 0;
+}
+#endif
+
+#ifdef SDS_TEST_MAIN
+int main(void) {
+ return sdsTest();
+}
+#endif
diff --git a/deps/sds/sds.h b/deps/sds/sds.h
new file mode 100644
index 0000000..077b882
--- /dev/null
+++ b/deps/sds/sds.h
@@ -0,0 +1,285 @@
+/* https://github.com/antirez/sds
+ * SDSLib 2.0 -- A C dynamic strings library
+ *
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2015, Oran Agra
+ * Copyright (c) 2015, Redis Labs, Inc
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SDS_H
+#define __SDS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define SDS_MAX_PREALLOC (1024*1024)
+extern const char *SDS_NOINIT;
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+typedef char *sds;
+
+/* Note: sdshdr5 is never used, we just access the flags byte directly.
+ * However is here to document the layout of type 5 SDS strings. */
+struct __attribute__ ((__packed__)) sdshdr5 {
+ unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
+ char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr8 {
+ uint8_t len; /* used */
+ uint8_t alloc; /* excluding the header and null terminator */
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
+ char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr16 {
+ uint16_t len; /* used */
+ uint16_t alloc; /* excluding the header and null terminator */
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
+ char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr32 {
+ uint32_t len; /* used */
+ uint32_t alloc; /* excluding the header and null terminator */
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
+ char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr64 {
+ uint64_t len; /* used */
+ uint64_t alloc; /* excluding the header and null terminator */
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
+ char buf[];
+};
+
+#define SDS_TYPE_5 0
+#define SDS_TYPE_8 1
+#define SDS_TYPE_16 2
+#define SDS_TYPE_32 3
+#define SDS_TYPE_64 4
+#define SDS_TYPE_MASK 7
+#define SDS_TYPE_BITS 3
+// #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
+#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
+#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
+#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
+
+static inline size_t sdslen(const sds s) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ return SDS_TYPE_5_LEN(flags);
+ case SDS_TYPE_8:
+ return SDS_HDR(8,s)->len;
+ case SDS_TYPE_16:
+ return SDS_HDR(16,s)->len;
+ case SDS_TYPE_32:
+ return SDS_HDR(32,s)->len;
+ case SDS_TYPE_64:
+ return SDS_HDR(64,s)->len;
+ }
+ return 0;
+}
+
+static inline size_t sdsavail(const sds s) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5: {
+ return 0;
+ }
+ case SDS_TYPE_8: {
+ SDS_HDR_VAR(8,s);
+ return sh->alloc - sh->len;
+ }
+ case SDS_TYPE_16: {
+ SDS_HDR_VAR(16,s);
+ return sh->alloc - sh->len;
+ }
+ case SDS_TYPE_32: {
+ SDS_HDR_VAR(32,s);
+ return sh->alloc - sh->len;
+ }
+ case SDS_TYPE_64: {
+ SDS_HDR_VAR(64,s);
+ return sh->alloc - sh->len;
+ }
+ }
+ return 0;
+}
+
+static inline void sdssetlen(sds s, size_t newlen) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ {
+ unsigned char *fp = ((unsigned char*)s)-1;
+ *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
+ }
+ break;
+ case SDS_TYPE_8:
+ SDS_HDR(8,s)->len = newlen;
+ break;
+ case SDS_TYPE_16:
+ SDS_HDR(16,s)->len = newlen;
+ break;
+ case SDS_TYPE_32:
+ SDS_HDR(32,s)->len = newlen;
+ break;
+ case SDS_TYPE_64:
+ SDS_HDR(64,s)->len = newlen;
+ break;
+ }
+}
+
+static inline void sdsinclen(sds s, size_t inc) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ {
+ unsigned char *fp = ((unsigned char*)s)-1;
+ unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
+ *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
+ }
+ break;
+ case SDS_TYPE_8:
+ SDS_HDR(8,s)->len += inc;
+ break;
+ case SDS_TYPE_16:
+ SDS_HDR(16,s)->len += inc;
+ break;
+ case SDS_TYPE_32:
+ SDS_HDR(32,s)->len += inc;
+ break;
+ case SDS_TYPE_64:
+ SDS_HDR(64,s)->len += inc;
+ break;
+ }
+}
+
+/* sdsalloc() = sdsavail() + sdslen() */
+static inline size_t sdsalloc(const sds s) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ return SDS_TYPE_5_LEN(flags);
+ case SDS_TYPE_8:
+ return SDS_HDR(8,s)->alloc;
+ case SDS_TYPE_16:
+ return SDS_HDR(16,s)->alloc;
+ case SDS_TYPE_32:
+ return SDS_HDR(32,s)->alloc;
+ case SDS_TYPE_64:
+ return SDS_HDR(64,s)->alloc;
+ }
+ return 0;
+}
+
+static inline void sdssetalloc(sds s, size_t newlen) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ /* Nothing to do, this type has no total allocation info. */
+ break;
+ case SDS_TYPE_8:
+ SDS_HDR(8,s)->alloc = newlen;
+ break;
+ case SDS_TYPE_16:
+ SDS_HDR(16,s)->alloc = newlen;
+ break;
+ case SDS_TYPE_32:
+ SDS_HDR(32,s)->alloc = newlen;
+ break;
+ case SDS_TYPE_64:
+ SDS_HDR(64,s)->alloc = newlen;
+ break;
+ }
+}
+
+sds sdsnewlen(const void *init, size_t initlen);
+sds sdsnew(const char *init);
+sds sdsempty(void);
+sds sdsdup(const sds s);
+void sdsfree(sds s);
+sds sdsgrowzero(sds s, size_t len);
+sds sdscatlen(sds s, const void *t, size_t len);
+sds sdscat(sds s, const char *t);
+sds sdscatsds(sds s, const sds t);
+sds sdscpylen(sds s, const char *t, size_t len);
+sds sdscpy(sds s, const char *t);
+
+sds sdscatvprintf(sds s, const char *fmt, va_list ap);
+#ifdef __GNUC__
+sds sdscatprintf(sds s, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+#else
+sds sdscatprintf(sds s, const char *fmt, ...);
+#endif
+
+sds sdscatfmt(sds s, const char *fmt, ...);
+sds sdstrim(sds s, const char *cset);
+void sdsrange(sds s, ssize_t start, ssize_t end);
+void sdsupdatelen(sds s);
+void sdsclear(sds s);
+int sdscmp(const sds s1, const sds s2);
+sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);
+void sdsfreesplitres(sds *tokens, int count);
+void sdstolower(sds s);
+void sdstoupper(sds s);
+sds sdsfromlonglong(long long value);
+sds sdscatrepr(sds s, const char *p, size_t len);
+sds *sdssplitargs(const char *line, int *argc);
+sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
+sds sdsjoin(char **argv, int argc, char *sep);
+sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
+
+/* Low level functions exposed to the user API */
+sds sdsMakeRoomFor(sds s, size_t addlen);
+void sdsIncrLen(sds s, ssize_t incr);
+sds sdsRemoveFreeSpace(sds s);
+size_t sdsAllocSize(sds s);
+void *sdsAllocPtr(sds s);
+
+/* Export the allocator used by SDS to the program using SDS.
+ * Sometimes the program SDS is linked to, may use a different set of
+ * allocators, but may want to allocate or free things that SDS will
+ * respectively free or allocate. */
+void *sds_malloc(size_t size);
+void *sds_realloc(void *ptr, size_t size);
+void sds_free(void *ptr);
+
+#ifdef REDIS_TEST
+int sdsTest(int argc, char *argv[]);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/deps/sds/sdsalloc.h b/deps/sds/sdsalloc.h
new file mode 100644
index 0000000..f43023c
--- /dev/null
+++ b/deps/sds/sdsalloc.h
@@ -0,0 +1,42 @@
+/* SDSLib 2.0 -- A C dynamic strings library
+ *
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2015, Oran Agra
+ * Copyright (c) 2015, Redis Labs, Inc
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* SDS allocator selection.
+ *
+ * This file is used in order to change the SDS allocator at compile time.
+ * Just define the following defines to what you want to use. Also add
+ * the include of your alternate allocator if needed (not needed in order
+ * to use the default libc allocator). */
+
+#define s_malloc malloc
+#define s_realloc realloc
+#define s_free free
diff --git a/deps/sds/testhelp.h b/deps/sds/testhelp.h
new file mode 100644
index 0000000..4503340
--- /dev/null
+++ b/deps/sds/testhelp.h
@@ -0,0 +1,57 @@
+/* This is a really minimal testing framework for C.
+ *
+ * Example:
+ *
+ * test_cond("Check if 1 == 1", 1==1)
+ * test_cond("Check if 5 > 10", 5 > 10)
+ * test_report()
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __TESTHELP_H
+#define __TESTHELP_H
+
+int __failed_tests = 0;
+int __test_num = 0;
+#define test_cond(descr,_c) do { \
+ __test_num++; printf("%d - %s: ", __test_num, descr); \
+ if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
+} while(0);
+#define test_report() do { \
+ printf("%d tests, %d passed, %d failed\n", __test_num, \
+ __test_num-__failed_tests, __failed_tests); \
+ if (__failed_tests) { \
+ printf("=== WARNING === We have failed tests here...\n"); \
+ exit(1); \
+ } \
+} while(0);
+
+#endif
diff --git a/include/stellar/monitor.h b/include/stellar/monitor.h
new file mode 100644
index 0000000..9e50949
--- /dev/null
+++ b/include/stellar/monitor.h
@@ -0,0 +1,54 @@
+#pragma once
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "stellar/module.h"
+#define MONITOR_MODULE_NAME "monitor"
+ struct stellar_monitor;
+ /* use stellar_module_manager_get_module() to get monitor_module */
+ struct stellar_monitor *monitor_module_to_monitor(struct module *monitor_module);
+
+#define error_format_server_close "ERR Server closed the connection"
+#define error_format_connection_error "ERR could not connect to '%s:%u' : Connection refused"
+#define error_format_unknown_command "ERR unknown command '%s'"
+#define error_format_unknown_arg "ERR unrecognized args '%s'"
+#define error_format_wrong_number_of_args "ERR wrong number of arguments for '%s' command"
+#define error_format_arg_not_valid_integer "ERR arg '%s' is not an integer or out of range"
+
+ struct monitor_reply;
+ struct monitor_reply *monitor_reply_nil(void);
+ struct monitor_reply *monitor_reply_new_string(const char *format, ...);
+ struct monitor_reply *monitor_reply_new_integer(long long integer);
+ struct monitor_reply *monitor_reply_new_double(double dval);
+ struct monitor_reply *monitor_reply_new_error(const char *format, ...); /* for common errors, "error_format_xxx" should be used */
+ struct monitor_reply *monitor_reply_new_status(const char *format, ...);
+
+ /* Like Linux bash, argv[0] is always the command name itself */
+ typedef struct monitor_reply *(monitor_cmd_cb)(struct stellar_monitor *monitor, int argc, char *argv[], void *arg);
+
+ /*
+ * All command characters are case-insensitive.
+ * The set of flags 'flags' specify the behavior of the command, and should be
+ * passed as a C string composed of space separated words, The set of flags are:
+ *
+ * "write" : The command may modify the data set (it may also read from it).
+ * "readonly": The command returns data from keys but never writes.
+ *
+ * return value:
+ * 0: success
+ * -1: failed
+ * 1: already exist, has been registered
+ */
+ int monitor_register_cmd(struct stellar_monitor *monitor, const char *cmd, monitor_cmd_cb *cb, const char *flags,
+ const char *hint, const char *description, void *arg);
+
+/*
+ All modules should support "-h/--help/help" args to output usage information.
+ "show modulea help"
+ "show moduleb -h"
+ "show modulec --help"
+*/
+#ifdef __cplusplus
+}
+#endif
diff --git a/infra/CMakeLists.txt b/infra/CMakeLists.txt
index e2e45ee..fc0ad21 100644
--- a/infra/CMakeLists.txt
+++ b/infra/CMakeLists.txt
@@ -1,5 +1,5 @@
-set(INFRA exdata mq tuple packet_manager packet_io ip_reassembly tcp_reassembly session_manager module_manager)
-set(DEPS bitmap dablooms interval_tree logger nmx_pool rbtree timeout toml)
+set(INFRA exdata mq tuple packet_manager packet_io ip_reassembly tcp_reassembly session_manager module_manager monitor)
+set(DEPS bitmap dablooms interval_tree logger nmx_pool rbtree timeout toml ringbuf)
set(DECODERS lpi_plus)
set(WHOLE_ARCHIVE ${DEPS} ${INFRA} ${DECODERS})
set(LIBS fieldstat4)
diff --git a/infra/monitor/CMakeLists.txt b/infra/monitor/CMakeLists.txt
new file mode 100644
index 0000000..0588f8c
--- /dev/null
+++ b/infra/monitor/CMakeLists.txt
@@ -0,0 +1,22 @@
+# add_subdirectory(enforcer)
+
+add_library(monitor
+ monitor_cmd_assistant.c
+ monitor_transaction.c
+ monitor_server.c
+ monitor_utils.c
+ monitor_stat.c
+ monitor_spinlock.c
+ monitor_ringbuf.c
+ monitor_rpc.c
+)
+
+include_directories(${CMAKE_SOURCE_DIR}/include/)
+include_directories(${CMAKE_SOURCE_DIR}/deps)
+include_directories(${CMAKE_SOURCE_DIR}/infra)
+target_include_directories(monitor PUBLIC ${CMAKE_CURRENT_LIST_DIR})
+
+set_target_properties(monitor PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/infra/monitor/version.map")
+set_target_properties(monitor PROPERTIES PREFIX "")
+target_link_libraries(monitor toml sds linenoise tuple session_manager libevent-static libevent-static cjson-static ringbuf)
+target_link_options(monitor PRIVATE -rdynamic) \ No newline at end of file
diff --git a/infra/monitor/enforcer/CMakeLists.txt b/infra/monitor/enforcer/CMakeLists.txt
new file mode 100644
index 0000000..2691059
--- /dev/null
+++ b/infra/monitor/enforcer/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(monitor_enforcer STATIC show_session_enforcer.c )
+include_directories(${CMAKE_SOURCE_DIR}/include/)
+include_directories(${CMAKE_SOURCE_DIR}/deps)
+include_directories(${CMAKE_SOURCE_DIR}/deps/uthash)
+include_directories(${CMAKE_SOURCE_DIR}/deps/timeout)
+include_directories(${CMAKE_SOURCE_DIR}/infra)
+include_directories(${CMAKE_SOURCE_DIR}/infra/tuple/)
+include_directories(${CMAKE_SOURCE_DIR}/infra/packet_manager)
+include_directories(${CMAKE_SOURCE_DIR}/infra/tcp_reassembly)
+
+target_include_directories(monitor_enforcer PUBLIC ${CMAKE_CURRENT_LIST_DIR}) \ No newline at end of file
diff --git a/infra/monitor/enforcer/show_session_enforcer.c b/infra/monitor/enforcer/show_session_enforcer.c
new file mode 100644
index 0000000..f72218a
--- /dev/null
+++ b/infra/monitor/enforcer/show_session_enforcer.c
@@ -0,0 +1,818 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <assert.h>
+#include "stellar/session.h"
+#include "stellar/monitor.h"
+#include "session_manager/session_manager_rte.h"
+#include "monitor/monitor_utils.h"
+#include "monitor/monitor_rpc.h"
+#include "sds/sds.h"
+#include "session_manager/session_internal.h"
+
+// temp add
+extern struct session_manager_rte *session_manager_get_runtime(struct session_manager *sess_mgr, uint16_t thread_id);
+
+#define SHOW_SESSION_BRIEF_LIMIT_DEFAULT 10
+#define SHOW_SESSION_BRIEF_LIMIT_MAX 1000
+#define SHOW_SESSION_BRIEF_SCAN_COUNT_DEFAULT 100
+
+/* show session brief */
+struct stm_show_session_brief_opt
+{
+#define SESSION_SCAN_SNET (1 << 25)
+#define SESSION_SCAN_DNET (1 << 26)
+#define SESSION_SCAN_ID (1 << 27)
+#define SESSION_SCAN_CURSOR (1 << 28)
+#define SESSION_SCAN_COUNT (1 << 29)
+#define SESSION_SCAN_LIMIT (1 << 30)
+ struct session_scan_opts scan_opt;
+ uint32_t limit;
+};
+
+/* show session detail <id> */
+struct stm_show_session_detail_opt
+{
+ uint64_t sess_id;
+ int thread_idx; // todo, not used now
+};
+
+struct show_session_brief_result
+{
+ uint64_t sid;
+ int thread_index;
+ enum session_state state;
+ enum session_type protocol;
+ time_t create_time_in_sec;
+ time_t last_pkt_time_in_sec;
+ uint8_t flow_dir;
+ struct tuple6 addr;
+} __attribute__((packed));
+
+struct show_session_brief_array
+{
+ size_t array_num;
+ struct show_session_brief_result array[0]; /* Continuous memory, array_num * sizeof(struct show_session_brief_result) */
+} __attribute__((packed));
+
+struct show_session_detail_result
+{
+ struct show_session_brief_result sess_brief;
+ unsigned long long sess_stat[MAX_FLOW_TYPE][MAX_STAT];
+ enum session_direction direction; // in or out
+ unsigned char application; // L7 appid
+ // todo, other info
+} __attribute__((packed));
+
+static void stm_session_brief_cli_args_set_default(struct stm_show_session_brief_opt *show_opt)
+{
+ memset(show_opt, 0, sizeof(struct stm_show_session_brief_opt));
+ show_opt->limit = SHOW_SESSION_BRIEF_LIMIT_DEFAULT;
+ show_opt->scan_opt.cursor = 0;
+ show_opt->scan_opt.count = SHOW_SESSION_BRIEF_SCAN_COUNT_DEFAULT;
+}
+
+static uint32_t stm_session_brief_cli_args_get_flags(const char *para_name)
+{
+ if (NULL == para_name)
+ {
+ return 0;
+ }
+
+ if (strncasecmp(para_name, "id", 2) == 0)
+ {
+ return SESSION_SCAN_ID;
+ }
+ else if (strncasecmp(para_name, "cursor", 6) == 0)
+ {
+ return SESSION_SCAN_CURSOR;
+ }
+ else if (strncasecmp(para_name, "count", 5) == 0)
+ {
+ return SESSION_SCAN_COUNT;
+ }
+ else if (strncasecmp(para_name, "state", 5) == 0)
+ {
+ return SESSION_SCAN_STATE;
+ }
+ else if (strncasecmp(para_name, "sip", 3) == 0)
+ {
+ return SESSION_SCAN_SIP;
+ }
+ else if (strncasecmp(para_name, "dip", 3) == 0)
+ {
+ return SESSION_SCAN_DIP;
+ }
+ else if (strncasecmp(para_name, "snet", 4) == 0)
+ {
+ return SESSION_SCAN_SNET;
+ }
+ else if (strncasecmp(para_name, "dnet", 4) == 0)
+ {
+ return SESSION_SCAN_DNET;
+ }
+ else if (strncasecmp(para_name, "sport", 5) == 0)
+ {
+ return SESSION_SCAN_SPORT;
+ }
+ else if (strncasecmp(para_name, "dport", 5) == 0)
+ {
+ return SESSION_SCAN_DPORT;
+ }
+ else if (strncasecmp(para_name, "protocol", 8) == 0)
+ {
+ return SESSION_SCAN_TYPE;
+ }
+ else if (strncasecmp(para_name, "limit", 5) == 0)
+ {
+ return SESSION_SCAN_LIMIT;
+ }
+ else if (strncasecmp(para_name, "create-time-in", strlen("create-time-in")) == 0)
+ {
+ return SESSION_SCAN_CREATE_TIME;
+ }
+ else if (strncasecmp(para_name, "last-pkt-time-in", strlen("last-pkt-time-in")) == 0)
+ {
+ return SESSION_SCAN_LASPKT_TIME;
+ }
+ return 0;
+}
+
+static enum session_state show_session_state_pton(const char *state_str)
+{
+ if (strncasecmp(state_str, "opening", strlen("opening")) == 0)
+ {
+ return SESSION_STATE_OPENING;
+ }
+ else if (strncasecmp(state_str, "active", strlen("active")) == 0)
+ {
+ return SESSION_STATE_ACTIVE;
+ }
+ else if (strncasecmp(state_str, "closing", strlen("closing")) == 0)
+ {
+ return SESSION_STATE_CLOSING;
+ }
+
+ return MAX_STATE;
+}
+
+static uint32_t show_session_ipaddr_ntop(const char *ip_string, struct session_scan_opts *scan_opt, uint32_t flag)
+{
+ uint32_t addr_family;
+ if (SESSION_SCAN_SIP == flag)
+ {
+ addr_family = stm_inet_pton(ip_string, &scan_opt->src_addr[0].v4, &scan_opt->src_addr[0].v6);
+ }
+ else
+ {
+ addr_family = stm_inet_pton(ip_string, &scan_opt->dst_addr[0].v4, &scan_opt->dst_addr[0].v6);
+ }
+
+ if (addr_family == 0)
+ {
+ return 0;
+ }
+ if (AF_INET == addr_family)
+ {
+ if (SESSION_SCAN_SIP == flag)
+ {
+ scan_opt->src_addr[1].v4 = scan_opt->src_addr[0].v4;
+ }
+ else
+ {
+ scan_opt->dst_addr[1].v4 = scan_opt->dst_addr[0].v4;
+ }
+ }
+ else
+ {
+ if (SESSION_SCAN_SIP == flag)
+ {
+ scan_opt->src_addr[1].v6 = scan_opt->src_addr[0].v6;
+ }
+ else
+ {
+ scan_opt->dst_addr[1].v6 = scan_opt->dst_addr[0].v6;
+ }
+ }
+ return addr_family;
+}
+
+static sds show_session_cli_args_sanity_check(const struct stm_show_session_brief_opt *brief_opt)
+{
+ uint32_t flags = brief_opt->scan_opt.flags;
+ sds ss = sdsempty();
+
+ if ((flags & SESSION_SCAN_SIP) && (flags & SESSION_SCAN_SNET))
+ {
+ ss = sdscatprintf(ss, "error: the 'sip' and 'snet' options conflict!");
+ return ss;
+ }
+ if ((flags & SESSION_SCAN_DIP) && (flags & SESSION_SCAN_DNET))
+ {
+ ss = sdscatprintf(ss, "error: the 'dip' and 'dnet' options conflict!");
+ return ss;
+ }
+ return ss;
+}
+
+static int show_session_is_help(int argc, char *argv[])
+{
+ for (int i = 0; i < argc; i++)
+ {
+ if (strncasecmp(argv[i], "help", 4) == 0)
+ {
+ return 1;
+ }
+ if (strncasecmp(argv[i], "--help", 6) == 0)
+ {
+ return 1;
+ }
+ if (strncasecmp(argv[i], "-h", 2) == 0)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static struct monitor_reply *show_session_brief_usage(void)
+{
+ sds ss = sdsempty();
+ ss = sdscatprintf(ss, "Usage: show session brief [options]\n");
+ ss = sdscatprintf(ss, "Options:\n");
+ ss = sdscatprintf(ss, " -h, --help help\tdisplay usage information and exit\n");
+ ss = sdscatprintf(ss, " cursor <cursor>\n");
+ ss = sdscatprintf(ss, " count <count>\n");
+ ss = sdscatprintf(ss, " state <opening|active|closing>\n");
+ ss = sdscatprintf(ss, " protocol <tcp|udp>\n");
+ ss = sdscatprintf(ss, " sip <source ip>\n");
+ ss = sdscatprintf(ss, " dip <destination ip>\n");
+ ss = sdscatprintf(ss, " sport <source port>\n");
+ ss = sdscatprintf(ss, " dport <destination port>\n");
+ ss = sdscatprintf(ss, " snet <source network/netmask>\texample 192.168.1.0/24\n");
+ ss = sdscatprintf(ss, " dnet <destination network/netmask>\texample 1234::abcd/48\n");
+ ss = sdscatprintf(ss, " create-time-in <last-N-[seconds|minutes|hours|days]>\texample last-1-hours\n");
+ ss = sdscatprintf(ss, " last-pkt-time-in <last-N-[seconds|minutes|hours|days]>\texample last-7-days\n");
+ ss = sdscatprintf(ss, " limit <limit>\n");
+
+ struct monitor_reply *reply = monitor_reply_new_string("%s", ss);
+ sdsfree(ss);
+ return reply;
+}
+
+static struct monitor_reply *show_session_detail_usage(void)
+{
+ sds ss = sdsempty();
+ ss = sdscatprintf(ss, "Usage: show session detial [options]\n");
+ ss = sdscatprintf(ss, "Options:\n");
+ ss = sdscatprintf(ss, " -h, --help help\tdisplay usage information and exit\n");
+ ss = sdscatprintf(ss, " id <session id>\n");
+ struct monitor_reply *reply = monitor_reply_new_string("%s", ss);
+ sdsfree(ss);
+ return reply;
+}
+
+/*
+ "show session ..." command args parser
+ return:
+ NULL: success, brief_opt is filled
+ not NULL: error message
+ */
+static struct monitor_reply *show_session_brief_cli_args_parse(int argc, char *argv[], struct stm_show_session_brief_opt *brief_opt)
+{
+ uint32_t ipaddr_family = 0, history_ipaddr_family = 0;
+ sds ss = NULL;
+ uint32_t flag;
+ struct monitor_reply *error_reply = NULL;
+ const char *opt_name, *opt_value;
+
+ stm_session_brief_cli_args_set_default(brief_opt);
+ int i = 3; // skip "show session brief"
+ while (i < argc)
+ {
+ ipaddr_family = 0;
+ opt_name = argv[i];
+ flag = stm_session_brief_cli_args_get_flags(opt_name);
+ if (i + 1 >= argc)
+ {
+ error_reply = monitor_reply_new_error("option %s requires an argument\n", opt_name);
+ return error_reply;
+ }
+ opt_value = argv[++i];
+
+ switch (flag)
+ {
+ case SESSION_SCAN_TYPE:
+ {
+ if (strncasecmp(opt_value, "tcp", 3) == 0)
+ {
+ brief_opt->scan_opt.type = SESSION_TYPE_TCP;
+ }
+ else if (strncasecmp(opt_value, "udp", 3) == 0)
+ {
+ brief_opt->scan_opt.type = SESSION_TYPE_UDP;
+ }
+ else
+ {
+ error_reply = monitor_reply_new_error("unsupported protocol type: %s. should be <tcp|udp>\n", opt_value);
+ goto error_exit;
+ }
+ }
+ break;
+ case SESSION_SCAN_STATE:
+ {
+ enum session_state tmp_state = show_session_state_pton(opt_value);
+ if (tmp_state == MAX_STATE)
+ {
+ error_reply = monitor_reply_new_error("unrecognized session state: %s. should be <opening|active|closing>\n", opt_value);
+ goto error_exit;
+ }
+ brief_opt->scan_opt.state = tmp_state;
+ }
+ break;
+ // case SESSION_SCAN_ID:
+ // if (stm_string_isdigit(opt_value) == 0)
+ // {
+ // *error_reply = monitor_reply_new_error("invalid session id: %s. should be integer in range: <1 - UINT64_MAX>\n", opt_value);
+ // goto error_exit;
+ // }
+ // show_opt->detail_opt.sess_id = strtoull(opt_value, NULL, 10);
+ // break;
+ case SESSION_SCAN_SIP:
+ ipaddr_family = show_session_ipaddr_ntop(opt_value, &brief_opt->scan_opt, SESSION_SCAN_SIP);
+ if (ipaddr_family == 0)
+ {
+ error_reply = monitor_reply_new_error("invalid sip address: %s\n", opt_value);
+ goto error_exit;
+ }
+ brief_opt->scan_opt.addr_family = ipaddr_family;
+ break;
+ case SESSION_SCAN_DIP:
+ ipaddr_family = show_session_ipaddr_ntop(opt_value, &brief_opt->scan_opt, SESSION_SCAN_DIP);
+ if (ipaddr_family == 0)
+ {
+ error_reply = monitor_reply_new_error("invalid dip address: %s\n", opt_value);
+ goto error_exit;
+ }
+ brief_opt->scan_opt.addr_family = ipaddr_family;
+ break;
+
+ case SESSION_SCAN_SNET:
+ {
+ uint32_t ipv4addr, ipv4mask;
+ struct in6_addr ipv6addr, ipv6mask;
+ ipaddr_family = stm_ip_cidr_pton(opt_value, &ipv4addr, &ipv4mask, &ipv6addr, &ipv6mask);
+ if (ipaddr_family == 0)
+ {
+ error_reply = monitor_reply_new_error("invalid snet CIDR address: %s\n", opt_value);
+ goto error_exit;
+ }
+ if (AF_INET == ipaddr_family)
+ {
+ uint32_t ipv4_range[2];
+ stm_ipv4_cidr_to_range(ipv4addr, ipv4mask, ipv4_range);
+ brief_opt->scan_opt.src_addr[0].v4.s_addr = ipv4_range[0];
+ brief_opt->scan_opt.src_addr[1].v4.s_addr = ipv4_range[1];
+ }
+ else
+ {
+ struct in6_addr ipv6_range[2];
+ stm_ipv6_cidr_to_range(&ipv6addr, &ipv6mask, ipv6_range);
+ brief_opt->scan_opt.src_addr[0].v6 = ipv6_range[0];
+ brief_opt->scan_opt.src_addr[1].v6 = ipv6_range[1];
+ }
+ brief_opt->scan_opt.addr_family = ipaddr_family;
+ flag = SESSION_SCAN_SIP;
+ }
+ break;
+ case SESSION_SCAN_DNET:
+ {
+ uint32_t ipv4addr, ipv4mask;
+ struct in6_addr ipv6addr, ipv6mask;
+ ipaddr_family = stm_ip_cidr_pton(opt_value, &ipv4addr, &ipv4mask, &ipv6addr, &ipv6mask);
+ if (ipaddr_family == 0)
+ {
+ error_reply = monitor_reply_new_error("invalid dnet CIDR address: %s\n", opt_value);
+ goto error_exit;
+ }
+ if (AF_INET == ipaddr_family)
+ {
+ uint32_t ipv4_range[2];
+ stm_ipv4_cidr_to_range(ipv4addr, ipv4mask, ipv4_range);
+ brief_opt->scan_opt.dst_addr[0].v4.s_addr = ipv4_range[0];
+ brief_opt->scan_opt.dst_addr[1].v4.s_addr = ipv4_range[1];
+ }
+ else
+ {
+ struct in6_addr ipv6_range[2];
+ stm_ipv6_cidr_to_range(&ipv6addr, &ipv6mask, ipv6_range);
+ brief_opt->scan_opt.dst_addr[0].v6 = ipv6_range[0];
+ brief_opt->scan_opt.dst_addr[1].v6 = ipv6_range[1];
+ }
+ brief_opt->scan_opt.addr_family = ipaddr_family;
+ flag = SESSION_SCAN_DIP;
+ }
+ break;
+ case SESSION_SCAN_SPORT:
+ {
+ if (stm_string_isdigit(opt_value) == 0)
+ {
+ error_reply = monitor_reply_new_error("illegal sport: %s. should be integer\n", opt_value);
+ goto error_exit;
+ }
+ int tmp_val = atoi(opt_value);
+ if (tmp_val <= 0 || tmp_val > 65535)
+ {
+ error_reply = monitor_reply_new_error("illegal sport: %s. should be <1-65535>\n", opt_value);
+ goto error_exit;
+ }
+ brief_opt->scan_opt.src_port = htons((unsigned short)tmp_val);
+ }
+ break;
+ case SESSION_SCAN_DPORT:
+ {
+ if (stm_string_isdigit(opt_value) == 0)
+ {
+ error_reply = monitor_reply_new_error("illegal sport: %s. should be integer\n", opt_value);
+ goto error_exit;
+ }
+ int tmp_val = atoi(opt_value);
+ if (tmp_val <= 0 || tmp_val > 65535)
+ {
+ error_reply = monitor_reply_new_error("illegal sport: %s. should be <1-65535>\n", opt_value);
+ goto error_exit;
+ }
+ brief_opt->scan_opt.dst_port = htons((unsigned short)tmp_val);
+ }
+ break;
+
+ case SESSION_SCAN_CURSOR:
+ brief_opt->scan_opt.cursor = strtoul(opt_value, NULL, 10);
+ break;
+ case SESSION_SCAN_COUNT:
+ brief_opt->scan_opt.count = strtoul(opt_value, NULL, 10);
+ if (brief_opt->scan_opt.count == 0)
+ {
+ error_reply = monitor_reply_new_error("illegal count: %s. should be <1 - UINT32_MAX>\n", opt_value);
+ goto error_exit;
+ }
+ break;
+ case SESSION_SCAN_LIMIT:
+ brief_opt->limit = strtoul(opt_value, NULL, 10);
+ if (brief_opt->limit == 0 || brief_opt->limit > SHOW_SESSION_BRIEF_LIMIT_MAX)
+ {
+ error_reply = monitor_reply_new_error("illegal limit: %s. should be <1 - %u>\n", opt_value, SHOW_SESSION_BRIEF_LIMIT_MAX);
+ goto error_exit;
+ }
+ break;
+ case SESSION_SCAN_CREATE_TIME:
+ {
+ time_t tmp_range[2] = {0, 0};
+ if (stm_time_range_pton(opt_value, time(NULL), tmp_range) < 0)
+ {
+ error_reply = monitor_reply_new_error("invalid create-time-in: %s. \r\nsyntax: <last-N-[seconds|minutes|hours|days]>\n", opt_value);
+ goto error_exit;
+ }
+ brief_opt->scan_opt.create_time_ms[0] = tmp_range[0] * 1000; // second to ms
+ brief_opt->scan_opt.create_time_ms[1] = tmp_range[1] * 1000; // second to ms
+ }
+ break;
+ case SESSION_SCAN_LASPKT_TIME:
+ {
+ time_t tmp_range[2] = {0, 0};
+ if (stm_time_range_pton(opt_value, time(NULL), tmp_range) < 0)
+ {
+ error_reply = monitor_reply_new_error("invalid last-pkt-time-in: %s. \r\nsyntax: <last-N-[seconds|minutes|hours|days]>\n", opt_value);
+ goto error_exit;
+ }
+ brief_opt->scan_opt.laspkt_time_ms[0] = tmp_range[0] * 1000; // second to ms
+ brief_opt->scan_opt.laspkt_time_ms[1] = tmp_range[1] * 1000; // second to ms
+ }
+ break;
+ default:
+ error_reply = monitor_reply_new_error("unrecognized params: %s \n", opt_name);
+ return error_reply;
+ }
+
+ if ((history_ipaddr_family != 0) && (ipaddr_family != 0) && (history_ipaddr_family != ipaddr_family))
+ {
+ error_reply = monitor_reply_new_error("contradictory ip version, expression rejects all sessions!\n");
+ goto error_exit;
+ }
+ history_ipaddr_family = ipaddr_family;
+ i++; // to next option
+ brief_opt->scan_opt.flags |= flag;
+ }
+ ss = show_session_cli_args_sanity_check(brief_opt);
+ if (ss && sdslen(ss) > 0)
+ {
+ error_reply = monitor_reply_new_error("%s\n", ss);
+ sdsfree(ss);
+ goto error_exit;
+ }
+ sdsfree(ss);
+ error_reply = NULL;
+ return NULL;
+
+error_exit:
+ return error_reply;
+}
+
+static void get_single_session_brief(struct session *sess, int thread_id, struct show_session_brief_result *brief)
+{
+ brief->thread_index = thread_id;
+ brief->state = session_get_current_state(sess);
+ brief->protocol = session_get_type(sess);
+ session_is_symmetric(sess, &brief->flow_dir);
+ brief->create_time_in_sec = session_get_timestamp(sess, SESSION_TIMESTAMP_START) / 1000; // ms to sec
+ brief->last_pkt_time_in_sec = session_get_timestamp(sess, SESSION_TIMESTAMP_LAST) / 1000; // ms to sec
+ const struct tuple6 *tp6 = session_get_tuple6(sess);
+ memcpy(&brief->addr, tp6, sizeof(struct tuple6));
+}
+
+struct iovec show_session_brief_on_worker_thread_rpc_cb(int thread_idx, struct iovec request, void *args)
+{
+ struct iovec response = {};
+ assert(request.iov_len == sizeof(struct stm_show_session_brief_opt));
+ struct stm_show_session_brief_opt *show_brief_opt = (struct stm_show_session_brief_opt *)request.iov_base;
+
+ uint64_t session_id_array[SHOW_SESSION_BRIEF_LIMIT_MAX];
+ uint64_t session_id_array_count = SHOW_SESSION_BRIEF_LIMIT_MAX;
+ struct session_manager *sess_mgr = stellar_module_get_session_manager((struct stellar_module_manager *)args);
+ struct session_manager_rte *sess_mgr_rt = session_manager_get_runtime(sess_mgr, thread_idx);
+ session_id_array_count = session_manager_rte_scan_session(sess_mgr_rt, &show_brief_opt->scan_opt, session_id_array, show_brief_opt->limit);
+ if (session_id_array_count == 0)
+ {
+ // no session match the filter params, but no error! still need to build a empty reply!
+ // go on !!!
+ response.iov_base = NULL;
+ response.iov_len = 0;
+ return response;
+ }
+
+ struct show_session_brief_result *brief_result_array = (struct show_session_brief_result *)calloc(session_id_array_count, sizeof(struct show_session_brief_result));
+
+ for (uint32_t i = 0; i < session_id_array_count; i++)
+ {
+ struct session *sess = session_manager_rte_lookup_session_by_id(sess_mgr_rt, session_id_array[i]);
+ assert(sess != NULL);
+ get_single_session_brief(sess, thread_idx, &brief_result_array[i]);
+ brief_result_array[i].sid = session_id_array[i];
+ }
+ response.iov_base = brief_result_array;
+ response.iov_len = session_id_array_count;
+ return response;
+}
+
+static sds session_brief_to_readable(const struct show_session_brief_result *sess_brief)
+{
+ char time_buf[64];
+ char addr_buf[256];
+ sds ss = sdsempty();
+ strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_brief->create_time_in_sec));
+ ss = sdscatprintf(ss, "%-6d %-21lu %-5s %-8s %-20s %s", sess_brief->thread_index, sess_brief->sid,
+ (sess_brief->protocol == SESSION_TYPE_TCP) ? "tcp" : "udp",
+ stm_session_state_ntop(sess_brief->state), time_buf,
+ stm_get0_readable_session_addr(&sess_brief->addr, addr_buf, sizeof(addr_buf)));
+ return ss;
+}
+
+static struct monitor_reply *show_session_brief_cmd_cb(struct stellar_monitor *monitor, int argc, char *argv[], void *arg)
+{
+ struct stm_show_session_brief_opt brief_opt = {};
+ if (show_session_is_help(argc, argv))
+ {
+ return show_session_brief_usage();
+ }
+ struct monitor_reply *error_reply = show_session_brief_cli_args_parse(argc, argv, &brief_opt);
+ if (error_reply != NULL)
+ {
+ return error_reply;
+ }
+ struct monitor_reply *cmd_reply;
+ struct stellar_module_manager *mod_mgr = (struct stellar_module_manager *)arg;
+ int thread_num = stellar_module_manager_get_max_thread_num(mod_mgr);
+ struct iovec request;
+ request.iov_base = (void *)&brief_opt;
+ request.iov_len = sizeof(struct stm_show_session_brief_opt);
+
+ struct iovec response[thread_num];
+ memset(response, 0, sizeof(response));
+ size_t tot_sess_id_num = 0;
+ sds ss = sdsempty();
+ ss = sdscatprintf(ss, "%-6s %-21s %-5s %-8s %-20s %s\r\n", "thread", "session-id", "proto", "state", "create-time", "tuple4(sip:sport-dip:dport)");
+ ss = sdscatprintf(ss, "--------------------------------------------------------------------------------------------\r\n");
+
+ for (int i = 0; i < thread_num; i++)
+ {
+ response[i] = monitor_worker_thread_rpc(monitor, i, request, show_session_brief_on_worker_thread_rpc_cb, mod_mgr);
+ if (response[i].iov_base == NULL || response[i].iov_len == 0) // empty result
+ {
+ continue;
+ }
+ struct show_session_brief_result *brief_res_array = (struct show_session_brief_result *)(response[i].iov_base);
+ tot_sess_id_num += response[i].iov_len; // session_id_array_count
+ for (size_t j = 0; j < response[i].iov_len; j++)
+ {
+ ss = sdscatprintf(ss, "%s\n", session_brief_to_readable(&brief_res_array[j]));
+ }
+ if (tot_sess_id_num >= brief_opt.limit)
+ {
+ break;
+ }
+ }
+
+ if (tot_sess_id_num == 0)
+ {
+ cmd_reply = monitor_reply_new_string("No session found");
+ goto empty_result;
+ }
+ cmd_reply = monitor_reply_new_string("%s", ss);
+
+empty_result:
+ sdsfree(ss);
+ for (int i = 0; i < thread_num; i++)
+ {
+ if (response[i].iov_base)
+ {
+ free(response[i].iov_base);
+ }
+ }
+ return cmd_reply;
+}
+
+/*
+ todo: add thread id,
+ fast patch, avoid traversing all worker threads
+ */
+static struct monitor_reply *show_session_detail_cli_args_parse(int argc, char *argv[], struct stm_show_session_detail_opt *detail_opt)
+{
+ if (argc < 4)
+ {
+ return monitor_reply_new_error("missing session id\n");
+ }
+ if (argc > 5)
+ {
+ return monitor_reply_new_error("too many arguments\n");
+ }
+ if (strncasecmp(argv[3], "id", 2) != 0)
+ {
+ return monitor_reply_new_error("missing session id\n");
+ }
+
+ if (stm_string_isdigit(argv[4]) == 0)
+ {
+ return monitor_reply_new_error("invalid session id: %s. should be integer in range: <1 - UINT64_MAX>\n", argv[4]);
+ }
+ detail_opt->sess_id = strtoull(argv[4], NULL, 10);
+ return NULL;
+}
+
+struct iovec show_session_detail_on_worker_thread_rpc_cb(int thread_idx, struct iovec request, void *args)
+{
+ struct iovec response = {};
+ assert(request.iov_len == sizeof(struct stm_show_session_detail_opt));
+ struct stm_show_session_detail_opt *detail_opt = (struct stm_show_session_detail_opt *)request.iov_base;
+
+ struct session_manager *sess_mgr = stellar_module_get_session_manager((struct stellar_module_manager *)args);
+ struct session_manager_rte *sess_mgr_rt = session_manager_get_runtime(sess_mgr, thread_idx);
+
+ struct session *sess = session_manager_rte_lookup_session_by_id(sess_mgr_rt, detail_opt->sess_id);
+ if (NULL == sess)
+ {
+ return response;
+ }
+
+ struct show_session_detail_result *detail_res = (struct show_session_detail_result *)calloc(1, sizeof(struct show_session_detail_result));
+ get_single_session_brief(sess, thread_idx, &detail_res->sess_brief);
+ detail_res->sess_brief.sid = detail_opt->sess_id;
+ detail_res->direction = session_get_direction(sess);
+ // todo, get some exact stat, not all
+ memcpy(detail_res->sess_stat, sess->stats, sizeof(detail_res->sess_stat));
+
+ // todo, get application info
+
+ response.iov_base = detail_res;
+ response.iov_len = sizeof(struct show_session_detail_result);
+ return response;
+}
+
+static sds session_detail_to_readable(const struct show_session_detail_result *sess_detail)
+{
+ char addr_buf[256];
+ sds ss = sdsempty();
+ char time_buf[64];
+#define SHOW_SESSION_DETAIL_NAME_FORMAT "%-30s"
+#define SHOW_SESSION_DETAIL_VALUE_FORMAT "%llu"
+ const char *flow_str[MAX_FLOW_TYPE] = {"C2S flow", "S2C flow"};
+ ss = sdscatprintf(ss, "%-15s: %lu\r\n", "session-id", sess_detail->sess_brief.sid);
+ ss = sdscatprintf(ss, "%-15s: %d\r\n", "thread", sess_detail->sess_brief.thread_index);
+ ss = sdscatprintf(ss, "%-15s: %s\r\n", "state", stm_session_state_ntop(sess_detail->sess_brief.state));
+ ss = sdscatprintf(ss, "%-15s: %s\r\n", "protocol", (sess_detail->sess_brief.protocol == SESSION_TYPE_TCP) ? "tcp" : "udp");
+ strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_detail->sess_brief.create_time_in_sec));
+ ss = sdscatprintf(ss, "%-15s: %s\r\n", "create-time", time_buf);
+ strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_detail->sess_brief.last_pkt_time_in_sec));
+ ss = sdscatprintf(ss, "%-15s: %s\r\n", "last-pkt-time", time_buf);
+ ss = sdscatprintf(ss, "%-15s: %s\r\n", "tuple4", stm_get0_readable_session_addr(&sess_detail->sess_brief.addr, addr_buf, sizeof(addr_buf)));
+ ss = sdscatprintf(ss, "%-15s: %s\r\n", "symmetric", stm_session_flow_dir_ntop(sess_detail->sess_brief.flow_dir));
+ ss = sdscatprintf(ss, "%-15s: %s\r\n", "direction", sess_detail->direction == SESSION_DIRECTION_INBOUND ? "INBOUND" : "OUTBOUND");
+
+ ss = sdscatprintf(ss, "statistics:\r\n");
+ char printf_format[256];
+ snprintf(printf_format, sizeof(printf_format), "\t%s : %s\r\n", SHOW_SESSION_DETAIL_NAME_FORMAT, SHOW_SESSION_DETAIL_VALUE_FORMAT);
+ for (int flow = 0; flow < MAX_FLOW_TYPE; flow++)
+ {
+ ss = sdscatprintf(ss, " %s:\r\n", flow_str[flow]);
+ ss = sdscatprintf(ss, printf_format, "raw_packets_received", sess_detail->sess_stat[flow][STAT_RAW_PACKETS_RECEIVED]);
+ ss = sdscatprintf(ss, printf_format, "raw_bytes_received", sess_detail->sess_stat[flow][STAT_RAW_BYTES_RECEIVED]);
+ ss = sdscatprintf(ss, printf_format, "duplicate_packets_bypass", sess_detail->sess_stat[flow][STAT_DUPLICATE_PACKETS_BYPASS]);
+ ss = sdscatprintf(ss, printf_format, "injected_packets", sess_detail->sess_stat[flow][STAT_INJECTED_PACKETS_SUCCESS]);
+ ss = sdscatprintf(ss, printf_format, "injected_bytes", sess_detail->sess_stat[flow][STAT_INJECTED_BYTES_SUCCESS]);
+ if (SESSION_TYPE_TCP == sess_detail->sess_brief.protocol)
+ {
+ ss = sdscatprintf(ss, printf_format, "tcp_segments_retransmit", sess_detail->sess_stat[flow][STAT_TCP_SEGMENTS_RETRANSMIT]);
+ ss = sdscatprintf(ss, printf_format, "tcp_payloads_retransmit", sess_detail->sess_stat[flow][STAT_TCP_PAYLOADS_RETRANSMIT]);
+ ss = sdscatprintf(ss, printf_format, "tcp_segments_reordered", sess_detail->sess_stat[flow][STAT_TCP_SEGMENTS_REORDERED]);
+ ss = sdscatprintf(ss, printf_format, "tcp_payloads_reordered", sess_detail->sess_stat[flow][STAT_TCP_PAYLOADS_REORDERED]);
+ }
+ }
+
+ // todo:
+ ss = sdscatprintf(ss, "\r\n \033[31mtodo: add security policy rule id, HTTP URL, Server FQDN, etc... \033[0m");
+ return ss;
+}
+
+static struct monitor_reply *show_session_detail_cmd_cb(struct stellar_monitor *stm, int argc, char *argv[], void *arg)
+{
+ struct stm_show_session_detail_opt detail_opt = {};
+ detail_opt.thread_idx = -1;
+
+ if (show_session_is_help(argc, argv))
+ {
+ return show_session_detail_usage();
+ }
+ struct monitor_reply *error_reply = show_session_detail_cli_args_parse(argc, argv, &detail_opt);
+ if (error_reply != NULL)
+ {
+ return error_reply;
+ }
+
+ struct monitor_reply *cmd_reply;
+ struct stellar_module_manager *mod_mgr = (struct stellar_module_manager *)arg;
+ int thread_num = stellar_module_manager_get_max_thread_num(mod_mgr);
+ struct iovec request;
+ request.iov_base = (void *)&detail_opt;
+ request.iov_len = sizeof(struct stm_show_session_detail_opt);
+
+ struct iovec response[thread_num];
+ memset(response, 0, sizeof(response));
+ size_t tot_sess_id_num = 0;
+ sds ss = sdsempty();
+
+ for (int i = 0; i < thread_num; i++)
+ {
+ if (detail_opt.thread_idx != -1 && detail_opt.thread_idx != i)
+ {
+ continue;
+ }
+ response[i] = monitor_worker_thread_rpc(stm, i, request, show_session_detail_on_worker_thread_rpc_cb, mod_mgr);
+ if (response[i].iov_base == NULL || response[i].iov_len == 0) // empty result
+ {
+ continue;
+ }
+ struct show_session_detail_result *detail_res = (struct show_session_detail_result *)(response[i].iov_base);
+ ss = sdscatprintf(ss, "%s\n", session_detail_to_readable(detail_res));
+ tot_sess_id_num++;
+ break;
+ }
+ if (tot_sess_id_num == 0)
+ {
+ cmd_reply = monitor_reply_new_string("No session found by id %lu", detail_opt.sess_id);
+ goto empty_result;
+ }
+ cmd_reply = monitor_reply_new_string("%s", ss);
+
+empty_result:
+ sdsfree(ss);
+ for (int i = 0; i < thread_num; i++)
+ {
+ if (response[i].iov_base)
+ {
+ free(response[i].iov_base);
+ }
+ }
+ return cmd_reply;
+}
+
+int show_session_enforcer_init(struct stellar_module_manager *mod_mgr, struct stellar_monitor *stm)
+{
+ monitor_register_cmd(stm, "show session brief", show_session_brief_cmd_cb, "readonly",
+ "[sip | dip | sport | dport | protocol | state | cursor | count | limit | create-time-in | last-pkt-time-in ]",
+ "Show session brief information", (void *)mod_mgr);
+ monitor_register_cmd(stm, "show session detail", show_session_detail_cmd_cb, "readonly",
+ "id <id>",
+ "Show session verbose information", (void *)mod_mgr);
+ return 0;
+}
diff --git a/infra/monitor/monitor_cmd_assistant.c b/infra/monitor/monitor_cmd_assistant.c
new file mode 100644
index 0000000..a2d02fa
--- /dev/null
+++ b/infra/monitor/monitor_cmd_assistant.c
@@ -0,0 +1,537 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "cJSON.h"
+#include "sds/sds.h"
+#include "monitor_private.h"
+#include "monitor_cmd_assistant.h"
+#include "monitor_utils.h"
+#ifdef __cplusplus
+}
+#endif
+
+struct stm_cmd_spec
+{
+ const char *cmd_name;
+ const char *cmd_flags;
+ const char *cmd_hint;
+ int cmd_arity;
+ int cmd_first_key_offset;
+};
+
+struct stm_cmd_assistant
+{
+ cJSON *cjson_root;
+ stm_cmd_assistant_completion_cb *cmd_completion_cb;
+ stm_cmd_assistant_hints_cb *cmd_hints_cb;
+ sds hints_result; // mallo and free for every hits
+};
+static __thread struct stm_cmd_assistant *__g_stm_cli_assistant = NULL;
+
+static cJSON *stm_cli_register_cmd(cJSON *father_next_array, const char *cur_cmd_prefix)
+{
+ cJSON *cur_level_item = NULL;
+
+ /* search current cmd (name is prefix) in father->next array[] */
+ int array_size = cJSON_GetArraySize(father_next_array);
+ for (int i = 0; i < array_size; i++)
+ {
+ cur_level_item = cJSON_GetArrayItem(father_next_array, i);
+ cJSON *cmd_name_item = cJSON_GetObjectItem(cur_level_item, "prefix");
+ if (cmd_name_item != NULL && (0 == strcmp(cur_cmd_prefix, cmd_name_item->valuestring)))
+ {
+ break;
+ }
+ else
+ {
+ cur_level_item = NULL;
+ }
+ }
+ if (NULL == cur_level_item)
+ {
+ /* if not exist, create new cmd (name is prefix) */
+ cur_level_item = cJSON_CreateObject();
+ cJSON_AddItemToObject(cur_level_item, "prefix", cJSON_CreateString(cur_cmd_prefix));
+ cJSON *new_cmd_next_array = cJSON_CreateArray();
+ cJSON_AddItemToObject(cur_level_item, "next", new_cmd_next_array);
+
+ /* insert into father->next array */
+ cJSON_AddItemToArray(father_next_array, cur_level_item);
+ }
+ else
+ {
+ ; // already exist, do nothing
+ }
+ return cur_level_item;
+}
+
+/*
+ search json object by cli_cmd_line, if exsit , return 0, do nothing,
+ the search json object function can be used for register_usage();
+ if not exist, create new json object and insert into father->next array
+*/
+int stm_cmd_assistant_register(struct stm_cmd_assistant *aide, const char *cli_cmd_line)
+{
+ int argc = 0;
+ if (NULL == aide->cjson_root)
+ {
+ aide->cjson_root = cJSON_CreateArray();
+ }
+ sds *array = sdssplitargs(cli_cmd_line, &argc);
+ cJSON *father_candidate_array = aide->cjson_root;
+ cJSON *cur_cmd_obj = NULL;
+ for (int i = 0; i < argc; i++)
+ {
+ cur_cmd_obj = stm_cli_register_cmd(father_candidate_array, array[i]);
+ father_candidate_array = cJSON_GetObjectItem(cur_cmd_obj, "next");
+ }
+ sdsfreesplitres(array, argc);
+ return 0;
+}
+
+static cJSON *stm_cli_search_cmd(cJSON *father_next_array, const char *cur_cmd_prefix)
+{
+ cJSON *cur_level_item = NULL;
+ /* search current cmd (name is prefix) in father->next array[] */
+ int array_size = cJSON_GetArraySize(father_next_array);
+ for (int i = 0; i < array_size; i++)
+ {
+ cur_level_item = cJSON_GetArrayItem(father_next_array, i);
+ cJSON *cmd_name_item = cJSON_GetObjectItem(cur_level_item, "prefix");
+ if (cmd_name_item != NULL && (0 == strcmp(cur_cmd_prefix, cmd_name_item->valuestring)))
+ {
+ break;
+ }
+ else
+ {
+ cur_level_item = NULL;
+ }
+ }
+ return cur_level_item;
+}
+
+cJSON *stm_cmd_assistant_search(struct stm_cmd_assistant *aide, const char *cli_cmd_line)
+{
+ int argc = 0;
+ sds *array = sdssplitargs(cli_cmd_line, &argc);
+ cJSON *father_candidate_array = aide->cjson_root;
+ cJSON *match_item = NULL;
+ for (int i = 0; i < argc; i++)
+ {
+ match_item = stm_cli_search_cmd(father_candidate_array, array[i]);
+ if (NULL == match_item)
+ {
+ return NULL;
+ }
+ father_candidate_array = cJSON_GetObjectItem(match_item, "next");
+ }
+ sdsfreesplitres(array, argc);
+ return match_item;
+}
+
+int stm_cmd_assistant_register_usage(struct stm_cmd_assistant *aide, const char *cli_cmd_line, const char *usage)
+{
+ if (NULL == aide || NULL == cli_cmd_line || NULL == usage)
+ {
+ return -1;
+ }
+ if (strlen(cli_cmd_line) == 0)
+ {
+ cJSON *obj = cJSON_CreateObject();
+ cJSON_AddItemToObjectCS(obj, "usage", cJSON_CreateString(usage));
+ cJSON_AddItemToArray(aide->cjson_root, obj);
+ return 0;
+ }
+
+ cJSON *obj = stm_cmd_assistant_search(aide, cli_cmd_line);
+ if (NULL == obj)
+ {
+ stm_cmd_assistant_register(aide, cli_cmd_line);
+ obj = stm_cmd_assistant_search(aide, cli_cmd_line);
+ if (NULL == obj)
+ {
+ return -1;
+ }
+ }
+ cJSON_AddItemToObject(obj, "usage", cJSON_CreateString(usage));
+ return 0;
+}
+
+char *stm_cmd_assistant_verbose_print(int format)
+{
+ struct stm_cmd_assistant *aide = __g_stm_cli_assistant;
+ if (NULL == aide->cjson_root)
+ {
+ return (char *)strdup("[]");
+ }
+ char *debug_print;
+ if (format == 1)
+ {
+ debug_print = cJSON_Print(aide->cjson_root);
+ }
+ else
+ {
+ debug_print = cJSON_PrintUnformatted(aide->cjson_root);
+ }
+ return debug_print;
+}
+
+char *stm_cmd_assistant_brief_print(void)
+{
+ struct stm_cmd_assistant *aide = __g_stm_cli_assistant;
+ sds s = sdsempty();
+ s = sdscat(s, "Usage:\r\n");
+ int array_size = cJSON_GetArraySize(aide->cjson_root);
+ for (int sz = 0; sz < array_size; sz++)
+ {
+ cJSON *item = cJSON_GetArrayItem(aide->cjson_root, sz);
+ cJSON *cmd_name_item = cJSON_GetObjectItem(item, "prefix");
+ s = sdscatprintf(s, "\t%s\r\n", cmd_name_item->valuestring);
+ }
+ char *print_str = strdup(s);
+ sdsfree(s);
+ return print_str;
+}
+
+int stm_cmd_assistant_json_load(struct stm_cmd_assistant *aide, const char *json_str)
+{
+ if (NULL == aide || NULL == json_str || strlen(json_str) == 0)
+ {
+ return -1;
+ }
+ cJSON *root = cJSON_Parse(json_str);
+ if (NULL == root)
+ {
+ return -1;
+ }
+ aide->cjson_root = root;
+ return 0;
+}
+
+void stm_cmd_assistant_free(struct stm_cmd_assistant *aide)
+{
+ if (aide)
+ {
+ if (aide->cjson_root)
+ {
+ cJSON_Delete(aide->cjson_root);
+ }
+ if (aide->hints_result)
+ {
+ sdsfree(aide->hints_result);
+ }
+ free(aide);
+ }
+}
+
+struct stm_cmd_assistant *stm_cmd_assistant_new(void)
+{
+ struct stm_cmd_assistant *aide = calloc(1, sizeof(struct stm_cmd_assistant));
+ aide->cjson_root = NULL;
+ aide->hints_result = NULL;
+ __g_stm_cli_assistant = aide;
+ return aide;
+}
+
+struct stm_cmd_assistant *stm_cmd_assistant_get(void)
+{
+ return __g_stm_cli_assistant;
+}
+
+static int stm_cmd_exist(struct stm_cmd_assistant *aide, const char *cmd_name)
+{
+ cJSON *root = aide->cjson_root;
+ if (NULL == root)
+ {
+ return 0;
+ }
+ int array_size = cJSON_GetArraySize(root);
+ for (int sz = 0; sz < array_size; sz++)
+ {
+ cJSON *item = cJSON_GetArrayItem(root, sz);
+ cJSON *cmd_name_item = cJSON_GetObjectItem(item, "prefix");
+ if (cmd_name_item && cmd_name_item->valuestring)
+ {
+ if (strcasecmp(cmd_name, cmd_name_item->valuestring) == 0)
+ {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * return value:
+ * 0: success
+ * -1: failed
+ * 1: already exist
+ */
+int stm_cmd_assistant_register_cmd(struct stm_cmd_assistant *aide, const char *cmd_name, void *cmd_cb, void *cmd_arg,
+ const char *flags, const char *hint, const char *desc)
+{
+ if (NULL == aide || NULL == cmd_name || NULL == cmd_cb)
+ {
+ return -1;
+ }
+ if (stm_cmd_exist(aide, cmd_name))
+ {
+ return 1;
+ }
+ if (stm_strncasecmp_exactly(flags, "readonly", 8) != 0 && stm_strncasecmp_exactly(flags, "write", 5) != 0)
+ {
+ return -1;
+ }
+ cJSON *obj = cJSON_CreateObject();
+ cJSON_AddItemToObject(obj, "prefix", cJSON_CreateString(cmd_name));
+ cJSON_AddItemToObject(obj, "flags", cJSON_CreateString(flags));
+ cJSON_AddItemToObject(obj, "hints", cJSON_CreateString(hint));
+ cJSON_AddItemToObject(obj, "desc", cJSON_CreateString(desc));
+
+ char tmp_str[32] = {};
+ snprintf(tmp_str, sizeof(tmp_str), "%p", cmd_cb);
+ cJSON_AddItemToObject(obj, "cmd_cb", cJSON_CreateString(tmp_str));
+
+ snprintf(tmp_str, sizeof(tmp_str), "%p", cmd_arg);
+ cJSON_AddItemToObject(obj, "cmd_arg", cJSON_CreateString(tmp_str));
+
+ if (NULL == aide->cjson_root)
+ {
+ aide->cjson_root = cJSON_CreateArray();
+ }
+ cJSON_AddItemToArray(aide->cjson_root, obj);
+ return 0;
+}
+
+char *stm_cmd_assistant_serialize(struct stm_cmd_assistant *aide)
+{
+ if (NULL == aide)
+ {
+ return NULL;
+ }
+ char *debug_print = cJSON_Print(aide->cjson_root);
+ return debug_print;
+}
+
+int stm_cmd_assistant_dserialize(struct stm_cmd_assistant *aide, const char *json)
+{
+ cJSON *tmp_root = cJSON_Parse(json);
+ if (NULL == tmp_root)
+ {
+ return -1;
+ }
+ aide->cjson_root = tmp_root;
+ return 0;
+}
+
+/*
+ return value:
+ 0: equal exactly, uesed for hints, or get_cmd_cb()
+ -1: cli_array < register_cmd_array //raw command1 and command2 line has same section, and command1 line is shorter than command2, used for tab auto completion
+ 1: cli_array > register_cmd_array //raw command1 and command2 line has same section, and command1 line is longer than command2, used for get_cmd_cb()
+ -2: not match any secions
+*/
+int stm_cmd_assistant_sds_compare(sds *cli_array, int cli_argc, sds *register_cmd_array, int register_argc)
+{
+ if (0 == cli_argc || 0 == register_argc)
+ {
+ return -2;
+ }
+ // compare the first n-1 words, must be exactly match
+ int min_argc = MIN(cli_argc, register_argc);
+ for (int i = 0; i < min_argc - 1; i++)
+ {
+ // previous words must be exactly match, so use strcasecmp()
+ if (strcasecmp(cli_array[i], register_cmd_array[i]) != 0)
+ {
+ return -2;
+ }
+ }
+
+ // compare the last common word use substring match
+ if (strncasecmp(cli_array[min_argc - 1], register_cmd_array[min_argc - 1], strlen(cli_array[min_argc - 1])) == 0)
+ {
+ if (strcasecmp(cli_array[min_argc - 1], register_cmd_array[min_argc - 1]) == 0)
+ {
+ if (cli_argc == register_argc)
+ {
+ return 0;
+ }
+ else if (cli_argc < register_argc)
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ else
+ {
+ // cli command is not complete
+ return -1;
+ }
+ }
+ return -2;
+}
+
+int stm_cmd_assistant_set_completion_cb(struct stm_cmd_assistant *aide, stm_cmd_assistant_completion_cb *cb)
+{
+ aide->cmd_completion_cb = cb;
+ return 0;
+}
+
+int stm_cmd_assistant_set_hints_cb(struct stm_cmd_assistant *aide, stm_cmd_assistant_hints_cb *cb)
+{
+ aide->cmd_hints_cb = cb;
+ return 0;
+}
+
+static void cjson_traversal_completion(struct stm_cmd_assistant *aide, sds *sds_cli_array, int cli_argc, cJSON *cjson_root, void *arg)
+{
+ int array_size = cJSON_GetArraySize(cjson_root);
+ for (int sz = 0; sz < array_size; sz++)
+ {
+ cJSON *array_item = cJSON_GetArrayItem(cjson_root, sz);
+ cJSON *prefix_item = cJSON_GetObjectItem(array_item, "prefix");
+ int register_cmd_section_num;
+ sds *register_cmd_array = sdssplitargs(prefix_item->valuestring, &register_cmd_section_num);
+
+ int match = stm_cmd_assistant_sds_compare(sds_cli_array, cli_argc, register_cmd_array, register_cmd_section_num);
+ sdsfreesplitres(register_cmd_array, register_cmd_section_num);
+ if (match == 0 || match == -1)
+ {
+ aide->cmd_completion_cb(arg, prefix_item->valuestring);
+ }
+ }
+ return;
+}
+
+static void stm_cmd_assistant_completion_cb_fun(struct stm_cmd_assistant *aide, const char *buf, void *arg)
+{
+ /* input cmd buf must <= registed command
+ if buf_len < command_len ,auto completion the longest prefix
+ if buf_len == command_len, add a blank space
+ */
+ int argc = 0;
+ sds *cli_cmd_array = sdssplitargs(buf, &argc);
+ cjson_traversal_completion(aide, cli_cmd_array, argc, aide->cjson_root, arg);
+ sdsfreesplitres(cli_cmd_array, argc);
+}
+
+void stm_cmd_assistant_input_line(struct stm_cmd_assistant *aide, const char *line, void *arg)
+{
+ stm_cmd_assistant_completion_cb_fun(aide, line, arg);
+}
+
+static const char *cjson_traversal_hints(sds *sds_cli_array, int cli_argc, cJSON *cjson_root)
+{
+ int array_size = cJSON_GetArraySize(cjson_root);
+ for (int sz = 0; sz < array_size; sz++)
+ {
+ cJSON *array_item = cJSON_GetArrayItem(cjson_root, sz);
+ cJSON *prefix_item = cJSON_GetObjectItem(array_item, "prefix");
+ int register_cmd_section_num;
+ sds *register_cmd_array = sdssplitargs(prefix_item->valuestring, &register_cmd_section_num);
+
+ int match = stm_cmd_assistant_sds_compare(sds_cli_array, cli_argc, register_cmd_array, register_cmd_section_num);
+ sdsfreesplitres(register_cmd_array, register_cmd_section_num);
+ if (match == 0) // hints must be exactly match
+ {
+ cJSON *hint_item = cJSON_GetObjectItem(array_item, "hints");
+ if (hint_item)
+ {
+ return hint_item->valuestring;
+ }
+ }
+ }
+ return NULL;
+}
+
+const char *stm_cmd_assistant_input_line_for_hints(struct stm_cmd_assistant *aide, const char *line)
+{
+ int argc = 0;
+ sds *cli_cmd_array = sdssplitargs(line, &argc);
+ const char *hints = cjson_traversal_hints(cli_cmd_array, argc, aide->cjson_root);
+ sdsfreesplitres(cli_cmd_array, argc);
+ return hints;
+}
+
+static long long stm_cmd_assistant_get_item(struct stm_cmd_assistant *aide, const char *cmd_line, const char *json_item_name, const char *format)
+{
+ long long item_value = 0;
+ int array_size = cJSON_GetArraySize(aide->cjson_root);
+ int cli_argc;
+ sds *cli_cmd_array = sdssplitargs(cmd_line, &cli_argc);
+ int last_match_cmd_section_num = -1;
+ for (int sz = 0; sz < array_size; sz++)
+ {
+ cJSON *array_item = cJSON_GetArrayItem(aide->cjson_root, sz);
+ cJSON *prefix_item = cJSON_GetObjectItem(array_item, "prefix");
+ int register_cmd_section_num;
+ sds *register_cmd_array = sdssplitargs(prefix_item->valuestring, &register_cmd_section_num);
+
+ int match = stm_cmd_assistant_sds_compare(cli_cmd_array, cli_argc, register_cmd_array, register_cmd_section_num);
+ sdsfreesplitres(register_cmd_array, register_cmd_section_num);
+ if (match >= 0)
+ {
+ cJSON *hint_item = cJSON_GetObjectItem(array_item, json_item_name);
+ if (hint_item)
+ {
+ /* longest match */
+ if (register_cmd_section_num > last_match_cmd_section_num)
+ {
+ last_match_cmd_section_num = register_cmd_section_num;
+ sscanf(hint_item->valuestring, format, &item_value);
+ }
+ }
+ }
+ }
+ sdsfreesplitres(cli_cmd_array, cli_argc);
+ return item_value;
+}
+
+void *stm_cmd_assistant_get_cb(struct stm_cmd_assistant *aide, const char *cmd_line)
+{
+ return (void *)stm_cmd_assistant_get_item(aide, cmd_line, "cmd_cb", "%p");
+}
+
+void *stm_cmd_assistant_get_user_arg(struct stm_cmd_assistant *aide, const char *cmd_line)
+{
+ return (void *)stm_cmd_assistant_get_item(aide, cmd_line, "cmd_arg", "%p");
+}
+
+int stm_cmd_assistant_get_arity(struct stm_cmd_assistant *aide, const char *cmd_line)
+{
+ return (int)stm_cmd_assistant_get_item(aide, cmd_line, "arity", "%d");
+}
+
+sds stm_cmd_assistant_list_cmd_brief(struct stm_cmd_assistant *aide)
+{
+ sds res = sdsempty();
+ int array_size = cJSON_GetArraySize(aide->cjson_root);
+ for (int sz = 0; sz < array_size; sz++)
+ {
+ cJSON *item = cJSON_GetArrayItem(aide->cjson_root, sz);
+ cJSON *cmd_name_item = cJSON_GetObjectItem(item, "prefix");
+ cJSON *cmd_desc_item = cJSON_GetObjectItem(item, "desc");
+ res = sdscatprintf(res, "\"%s\", %s \r\n", cmd_name_item->valuestring, cmd_desc_item ? cmd_desc_item->valuestring : "");
+ }
+ return res;
+}
+
+sds stm_cmd_assistant_list_cmd_verbose(struct stm_cmd_assistant *aide)
+{
+ if (NULL == aide->cjson_root)
+ {
+ return sdsempty();
+ }
+ char *json_str = cJSON_PrintUnformatted(aide->cjson_root);
+ sds res = sdsnew(json_str);
+ free(json_str);
+ return res;
+}
diff --git a/infra/monitor/monitor_cmd_assistant.h b/infra/monitor/monitor_cmd_assistant.h
new file mode 100644
index 0000000..02100a7
--- /dev/null
+++ b/infra/monitor/monitor_cmd_assistant.h
@@ -0,0 +1,60 @@
+#pragma once
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "cJSON.h"
+#include "sds/sds.h"
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+ struct stm_cmd_assistant;
+ struct stm_cmd_spec;
+
+ struct stm_cmd_assistant *stm_cmd_assistant_new();
+ void stm_cmd_assistant_free(struct stm_cmd_assistant *aide);
+ int stm_cmd_assistant_json_load(struct stm_cmd_assistant *aide, const char *json_str);
+ char *stm_cmd_assistant_brief_print(void);
+ char *stm_cmd_assistant_verbose_print(int format);
+ int stm_cmd_assistant_is_help(const char *line);
+ cJSON *stm_cmd_assistant_search(struct stm_cmd_assistant *aide, const char *cli_cmd_line);
+ int stm_cmd_assistant_register(struct stm_cmd_assistant *aide, const char *cli_cmd_line);
+ int stm_cmd_assistant_register_usage(struct stm_cmd_assistant *aide, const char *cli_cmd_line, const char *usage);
+
+ // return value shoule be free after uesd.
+ char *stm_cmd_assistant_serialize(struct stm_cmd_assistant *aide);
+ int stm_cmd_assistant_dserialize(struct stm_cmd_assistant *aide, const char *json);
+
+ /*
+ * return value:
+ * 0: success
+ * -1: failed
+ * 1: already exist
+ */
+ int stm_cmd_assistant_register_cmd(struct stm_cmd_assistant *aide, const char *cmd, void *cmd_cb, void *cmd_arg,
+ const char *flags, const char *hint, const char *description);
+
+ int stm_cmd_assistant_sds_compare(sds *cli_array, int cli_argc, sds *register_cmd_array, int register_argc);
+ typedef void(stm_cmd_assistant_completion_cb)(void *arg, const char *candidate_completion);
+ typedef char *(stm_cmd_assistant_hints_cb)(const char *line);
+ int stm_cmd_assistant_set_completion_cb(struct stm_cmd_assistant *aide, stm_cmd_assistant_completion_cb *cb);
+ int stm_cmd_assistant_set_hints_cb(struct stm_cmd_assistant *aide, stm_cmd_assistant_hints_cb *cb);
+ void stm_cmd_assistant_input_line(struct stm_cmd_assistant *aide, const char *line, void *arg);
+ const char *stm_cmd_assistant_input_line_for_hints(struct stm_cmd_assistant *aide, const char *line);
+ struct stm_cmd_assistant *stm_cmd_assistant_get(void);
+
+ // struct stm_cmd_spec *stm_cmd_assistant_get_cmd(struct stm_cmd_assistant *aide, const char *cmd);
+ void *stm_cmd_assistant_get_cb(struct stm_cmd_assistant *aide, const char *cmd);
+ void *stm_cmd_assistant_get_user_arg(struct stm_cmd_assistant *aide, const char *cmd);
+ int stm_cmd_assistant_get_arity(struct stm_cmd_assistant *aide, const char *cmd);
+ sds stm_cmd_assistant_list_cmd_brief(struct stm_cmd_assistant *aide);
+ sds stm_cmd_assistant_list_cmd_verbose(struct stm_cmd_assistant *aide);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/infra/monitor/monitor_private.h b/infra/monitor/monitor_private.h
new file mode 100644
index 0000000..2e18836
--- /dev/null
+++ b/infra/monitor/monitor_private.h
@@ -0,0 +1,331 @@
+#pragma once
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#include <pcap/pcap.h>
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "stellar/monitor.h"
+#include "sds/sds.h"
+
+#include "stellar/module.h"
+#include "stellar/log.h"
+
+#include <event2/event.h>
+#include <event2/listener.h>
+#include <event2/bufferevent.h>
+#include <event2/buffer.h>
+#include <event2/thread.h>
+#include <event2/http.h>
+#include "monitor_rpc.h"
+ /********************************** limit definition *****************************************/
+
+#ifndef STELLAR_MAX_THREAD_NUM
+#define STELLAR_MAX_THREAD_NUM (256)
+#endif
+#define STM_RINGBUF_SIZE (1024 * 1024) /* per thread */
+#define STM_CONNECTION_IDLE_TIMEOUT 300 /* How many seconds elapsed without input command, connection will closed */
+#define STM_REQUEST_TIMEOUT 5
+
+#define STM_SERVER_LISTEN_IP "127.0.0.1"
+#define STM_SERVER_LISTEN_PORT 80
+#define STM_TZSP_UDP_PORT 37008 /* default port of TZSP protocol: https://en.wikipedia.org/wiki/TZSP# */
+
+#define STM_SESSION_DEFAULT_SEARCH_COUNT 100 /* if no count params, max search session number */
+#define STM_SESSION_DEFAULT_LIMIT_NUM 10 /* if no limit params, max support result session number */
+#define STM_SESSION_MAX_LIMIT_NUM 1000
+
+#define STM_UINT64_READABLE_STRING_MAX_LEN 21 /* MAX value is: 18446744073709551615 */
+#define STM_UINT32_READABLE_STRING_MAX_LEN 11 /* MAX value is: 4294967295 */
+
+#define STM_CONNECTIVITY_DEFALUT_COUNT 5 /* ping default count */
+#define STM_CONNECTIVITY_DEFALUT_SIZE 64 /* ping default bytes */
+#define STM_CONNECTIVITY_MAX_COUNT 100 /* ping max count */
+#define STM_CONNECTIVITY_MAX_SIZE 65535 /* ping max bytes */
+
+ /************************************************************************/
+#define STM_CMD_CALLBACK_THREAD_LOCAL_MAGIC (0x1234ABCD)
+#define STM_RINGBUF_HDR_MAGIC (0x0ABCD12345678)
+
+#define STM_RINGBUF_THREAD_IDX_SERVER 0
+#define STM_RINGBUF_THREAD_IDX_AGENT 1
+
+#define STM_MONITOR_THREAD_ID 0 // There are only two threads, use fix id
+#define STM_WORKER_THREAD_ID 1 // There are only two threads, use fix id
+
+#define STM_LOG_MODULE_NAME "monitor"
+#define STM_STAT_OUTPUT_PATH "log/monitor.fs4"
+#define STM_STAT_OUTPUT_INTERVAL_MS 3000
+#define STM_RESTFUL_VERSION "v1"
+#define STM_RESTFUL_RESOURCE "stellar_monitor"
+#define STM_RESTFUL_URI_CMD_KEY "raw_cmd" // example: http://127.0.0.1:80/v1/stellar_monitor?raw_cmd=show%20session
+#define STM_CLIENT_SERVER_SYNC_CMD "show command verbose"
+
+#define STM_CLI_CMD_HINTS_COLOR 90
+#define STM_CLI_CMD_HINTS_BOLD 0
+
+#ifndef UNUSED
+#define UNUSED __attribute__((unused))
+#endif
+
+#ifdef NDEBUG // release version
+#define STM_DBG_PRINT(fmt, args...)
+#else
+#define STM_DBG_PRINT(fmt, args...) fprintf(stderr, fmt, ##args)
+#endif
+
+#ifndef CALLOC
+#define CALLOC(type, number) ((type *)calloc(sizeof(type), number))
+#endif
+
+#ifndef FREE
+#define FREE(ptr) \
+ { \
+ if (ptr) \
+ { \
+ free((void *)ptr); \
+ ptr = NULL; \
+ } \
+ }
+#endif
+
+#define STM_TIME_START() \
+ struct timespec __start_time, __end_time; \
+ unsigned long long diff; \
+ clock_gettime(CLOCK_MONOTONIC, &__start_time);
+
+#define STM_TIME_DIFF() \
+ { \
+ clock_gettime(CLOCK_MONOTONIC, &__end_time); \
+ if (__start_time.tv_sec == __end_time.tv_sec) \
+ { \
+ diff = (unsigned long long)(__end_time.tv_nsec - __start_time.tv_nsec); \
+ } \
+ diff = ((unsigned long long)__end_time.tv_sec * 1000 * 1000 * 1000 + __end_time.tv_nsec) - ((unsigned long long)__start_time.tv_sec * 1000 * 1000 * 1000 + __start_time.tv_nsec); \
+ }
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define STM_LOG_DEBUG(format, ...) STELLAR_LOG_DEBUG(__thread_local_logger, STM_LOG_MODULE_NAME, format, ##__VA_ARGS__)
+#define STM_LOG_INFO(format, ...) STELLAR_LOG_INFO(__thread_local_logger, STM_LOG_MODULE_NAME, format, ##__VA_ARGS__)
+#define STM_LOG_ERROR(format, ...) STELLAR_LOG_ERROR(__thread_local_logger, STM_LOG_MODULE_NAME, format, ##__VA_ARGS__)
+#define STM_LOG_FATAL(format, ...) STELLAR_LOG_FATAL(__thread_local_logger, STM_LOG_MODULE_NAME, format, ##__VA_ARGS__)
+
+ enum stm_http_response_code
+ {
+ STM_HTTP_200_OK = 200,
+ STM_HTTP_204_NO_CONTENT = 204,
+ STM_HTTP_403_FORBIDDEN = 403,
+ STM_HTTP_408_REQUEST_TIMEOUT = 408,
+ STM_HTTP_413_PAYLOAD_TOO_LARGE = 413,
+ };
+
+ enum stm_stat_type
+ {
+ STM_STAT_CLI_CONNECTION_NEW,
+ STM_STAT_CLI_CONNECTION_CLOSE,
+ STM_STAT_CLI_REQUEST_SUCC,
+ STM_STAT_CLI_RESPONSE_SUCC,
+ STM_STAT_CLI_REQUEST_ERR, // RESTFul Syntax error!
+ STM_STAT_CLI_RESPONSE_ERR, // attention: empty result is not error!
+ STM_STAT_MAX,
+ };
+
+ struct stm_spinlock;
+
+ struct stellar_monitor_config
+ {
+ // int thread_count;
+ size_t ringbuf_size; /* bytes */
+ int connection_idle_timeout;
+ int cli_request_timeout;
+ char *listen_ipaddr;
+ unsigned short listen_port_host_order;
+ unsigned short data_link_bind_port_host_order; // for TZSP protocol
+ int output_interval_ms;
+ char *output_path;
+ };
+
+ struct stm_key_value_tuple
+ {
+ char *key;
+ char *value;
+ };
+
+ struct stm_key_value
+ {
+ int tuple_num;
+ struct stm_key_value_tuple *tuple;
+ };
+
+ typedef struct evhttp_request stm_network_connection;
+
+ struct stm_cmd_transaction
+ {
+ struct stm_cmd_request *cmd_req;
+ struct stm_cmd_reply *cmd_res[STELLAR_MAX_THREAD_NUM]; // multi thread merge to one
+ };
+
+ struct stm_connection_manager
+ {
+ struct timeval link_start_time;
+ struct timeval last_active_time;
+ struct evhttp_connection *conn;
+ char peer_ipaddr[INET6_ADDRSTRLEN];
+ uint16_t peer_port_host_order;
+ struct stm_connection_manager *next, *prev;
+ };
+
+ struct stm_stat_counter
+ {
+ int counter_id;
+ uint64_t count;
+ uint64_t bytes;
+ };
+
+ struct stm_stat
+ {
+ void *fs4_ins;
+ struct stm_stat_counter counters[STM_STAT_MAX];
+ };
+
+ struct monitor_connection
+ {
+ struct evhttp_connection *current_evconn_ref;
+ };
+
+ /* optional API */
+ struct monitor_connection;
+ typedef void(monitor_connection_close_cb)(struct monitor_connection *conn, void *arg);
+ int monitor_register_connection_close_cb(struct stellar_monitor *monitor, monitor_connection_close_cb *cb, void *arg);
+ struct stm_conn_close_cb_manager
+ {
+ monitor_connection_close_cb *cb;
+ void *arg;
+ struct stm_conn_close_cb_manager *next, *prev;
+ };
+
+ struct stm_pktdump_runtime;
+ struct stellar_monitor
+ {
+ struct module_manager *mod_mgr_ref;
+ struct logger *logger_ref;
+ int worker_thread_num;
+ struct stellar_monitor_config *config;
+ struct stm_cmd_assistant *aide; // reference, share with stellar
+ struct stm_connection_manager *connection_mgr; // used to tracking all connections, for cli "who" command
+ struct stm_conn_close_cb_manager *conn_close_mgr;
+ // struct stm_ringbuf_mgr *ringbuf_mgr[STELLAR_MAX_THREAD_NUM];
+ struct event_base *evt_base;
+ // struct event *ev_timeout;
+ struct evhttp *evt_http_server;
+ pthread_t evt_main_loop_tid;
+ struct timeval time_now;
+ struct stm_stat *stat;
+ struct stm_spinlock *lock; // for dynamic register command, conn_close_cb
+ int (*gettime_cb)(struct timeval *tv, struct timezone *tz);
+ struct monitor_connection current_conn;
+ struct stm_pktdump_runtime *packet_dump;
+
+ struct monitor_rpc **rpc_ins_array; // multir threads
+ };
+
+ enum monitor_reply_type
+ {
+ MONITOR_REPLY_INTEGER,
+ MONITOR_REPLY_DOUBLE,
+ MONITOR_REPLY_STRING,
+ MONITOR_REPLY_ERROR,
+ MONITOR_REPLY_STATUS,
+ MONITOR_REPLY_NIL,
+ };
+
+ struct monitor_reply
+ {
+ enum monitor_reply_type type;
+ long long integer; /* The integer when type is SWARMKV_REPLY_INTEGER */
+ double dval; /* The double when type is SWARMKV_REPLY_DOUBLE */
+ int len; /* Length of string */
+ char *str;
+ int http_code;
+ const char *http_reason;
+ };
+
+ struct monitor_cli_args
+ {
+ const char *short_opt;
+ const char *long_opt;
+ int require_arg_value;
+ int value_is_multi_words; // "a b c d e f g"
+ char *value; // should be free after use
+ };
+ /************************************************************************************************************/
+ /* monitor call gettimeofday(2) by default */
+ struct stellar_monitor_config *stellar_monitor_config_new(const char *toml);
+ int stellar_monitor_set_gettime_callback(struct stellar_monitor *stm, int (*gettime_cb)(struct timeval *tv, struct timezone *tz));
+ struct stellar_monitor *stellar_monitor_get(void);
+ struct stm_connection_manager *stm_connection_insert(struct evhttp_connection *evconn);
+ void stm_connection_update(struct stm_connection_manager *conn_mgr, const struct evhttp_connection *evconn);
+ void stm_connection_delete(struct evhttp_connection *evconn);
+ const struct stm_connection_manager *stm_connection_search(const struct stm_connection_manager *conn_mgr_head, const struct evhttp_connection *evconn);
+
+ struct stm_key_value *stm_cmd_key_value_new(void);
+ void stm_cmd_key_value_append(struct stm_key_value **kv, const char *key, const char *value);
+ void stm_cmd_key_value_free(struct stm_key_value *kv);
+
+ /************************************** command manager **********************************************/
+ struct stm_stat *stm_stat_init(struct stellar_monitor *stm);
+ sds stm_config_print(const struct stellar_monitor_config *config);
+ void stm_stat_free(struct stm_stat *stat);
+ void stm_stat_update(struct stm_stat *stat, int thread_idx, enum stm_stat_type type, long long value);
+ long long stm_get_stat_count(struct stm_stat *stat, enum stm_stat_type type);
+ long long stm_get_stat_bytes(struct stm_stat *stat, enum stm_stat_type type);
+ sds monitor_reply_to_string(const struct monitor_reply *reply);
+ void monitor_reply_free(struct monitor_reply *reply);
+ int monitor_util_parse_cmd_args(int argc, const char *argv[], struct monitor_cli_args cli_args[], size_t cli_args_array_size);
+ char *stm_http_url_encode(const char *originalText);
+ struct stm_spinlock *stm_spinlock_new(void);
+ void stm_spinlock_lock(struct stm_spinlock *splock);
+ void stm_spinlock_unlock(struct stm_spinlock *splock);
+ void stm_spinlock_free(struct stm_spinlock *splock);
+ struct stm_pktdump_runtime *stm_packet_dump_new(struct stellar_monitor *stm, const struct stellar_monitor_config *config);
+ void stm_pktdump_enforcer_free(struct stellar_monitor *stm);
+
+ struct monitor_rpc *stm_rpc_new(void);
+ void stm_rpc_free(struct monitor_rpc *rpc_ins);
+ int stm_rpc_exec(int thread_idx, struct monitor_rpc *rpc_ins);
+ struct iovec stm_rpc_call(struct monitor_rpc *rpc_ins, struct iovec rpc_request, monitor_rpc_callabck *cb, void *user_args);
+ void monitor_rpc_free(struct monitor_rpc *rpc_ins);
+ struct monitor_rpc *monitor_rpc_new(struct stellar_monitor *stm, struct module_manager *mod_mgr);
+ struct stellar_monitor *monitor_new(const char *toml_file, struct module_manager *mod_mgr, struct logger *logh);
+
+ /* Must be called in 'monitor_cmd_cb' context */
+ struct monitor_connection *monitor_get_current_connection(struct stellar_monitor *monitor);
+ /* Get the remote address and port associated with this connection. */
+ int monitor_get_peer_addr(struct monitor_connection *conn, char **peer_ip, unsigned short *peer_port);
+
+ /* command enforcer */
+ int show_session_enforcer_init(struct module_manager *mod_mgr, struct stellar_monitor *stm);
+#ifdef __cplusplus
+}
+#endif
diff --git a/infra/monitor/monitor_ringbuf.c b/infra/monitor/monitor_ringbuf.c
new file mode 100644
index 0000000..63270ba
--- /dev/null
+++ b/infra/monitor/monitor_ringbuf.c
@@ -0,0 +1,137 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include "monitor_private.h"
+#include "monitor_ringbuf.h"
+
+struct monitor_ringbuf_wrap
+{
+ ringbuf_t *ringbuf;
+ char *ringbuf_data;
+ unsigned long long push_number; // only for statistics
+ unsigned long long push_bytes; // only for statistics
+ unsigned long long pop_number; // only for statistics
+ unsigned long long pop_bytes; // only for statistics
+};
+
+ssize_t stm_ringbuf_stream_start(int thread_id, struct monitor_ringbuf_wrap *rbf, size_t require_size)
+{
+ ringbuf_worker_t *rb_worker = ringbuf_register(rbf->ringbuf, thread_id);
+ ssize_t offset = ringbuf_acquire(rbf->ringbuf, rb_worker, require_size);
+ if (offset < 0)
+ {
+ STM_DBG_PRINT("stm ringbuf stream prealloc buffer(): ringbuf_acquire fail, no valid space!\n");
+ return 0;
+ }
+ return offset;
+}
+
+int stm_ringbuf_stream_append(int thread_id, struct monitor_ringbuf_wrap *rbf, size_t rbf_offset, const void *value, size_t len)
+{
+ (void)thread_id;
+ memcpy(rbf->ringbuf_data + rbf_offset, value, len);
+ rbf->push_number++;
+ rbf->push_bytes += len;
+ return 0;
+}
+
+void stm_ringbuf_stream_finish(int thread_id, struct monitor_ringbuf_wrap *rbf)
+{
+ ringbuf_worker_t *rb_worker = ringbuf_register(rbf->ringbuf, thread_id);
+ ringbuf_produce(rbf->ringbuf, rb_worker);
+}
+
+int stm_ringbuf_easy_push(int thread_id, struct monitor_ringbuf_wrap *rbf, const void *push_value /*must continuous*/, size_t push_len)
+{
+ ringbuf_worker_t *rb_worker = ringbuf_register(rbf->ringbuf, thread_id);
+ ssize_t offset = ringbuf_acquire(rbf->ringbuf, rb_worker, push_len);
+ if (offset < 0)
+ {
+ STM_DBG_PRINT("stm ringbuf easy push(): ringbuf_acquire fail, no valid space!\n");
+ return -1;
+ }
+ memcpy(rbf->ringbuf_data + offset, push_value, push_len);
+
+ ringbuf_produce(rbf->ringbuf, rb_worker);
+ rbf->push_number++;
+ rbf->push_bytes += push_len;
+ // STM_DBG_PRINT("stm ringbuf push() success, len:%llu, number:%llu\n", push_len, rbf->push_number);
+ return 0;
+}
+
+void *stm_ringbuf_pop(struct monitor_ringbuf_wrap *rbf, size_t *pop_len)
+{
+ size_t len = 0, offset = 0;
+ len = ringbuf_consume(rbf->ringbuf, &offset);
+ if (0 == len)
+ {
+ // STM_DBG_PRINT("stm_ringbuf_pop(): not valid data\n");
+ *pop_len = 0;
+ return NULL;
+ }
+ rbf->pop_number++;
+ *pop_len = len;
+ // STM_DBG_PRINT("stm_ringbuf_pop() success, len:%llu, number:%llu\n", len, rbf->pop_number);
+ return rbf->ringbuf_data + offset;
+}
+
+void stm_ringbuf_release(struct monitor_ringbuf_wrap *rbf, int rel_len)
+{
+ ringbuf_release(rbf->ringbuf, rel_len);
+ rbf->pop_bytes += rel_len;
+}
+
+struct monitor_ringbuf_wrap *stm_ringbuf_wrap_new(int thread_tot_num, size_t ringbuf_size)
+{
+ struct monitor_ringbuf_wrap *rbf = (struct monitor_ringbuf_wrap *)calloc(1, sizeof(struct monitor_ringbuf_wrap));
+ size_t ringbuf_obj_size;
+ ringbuf_get_sizes(thread_tot_num, &ringbuf_obj_size, NULL);
+ rbf->ringbuf = (ringbuf_t *)calloc(1, ringbuf_obj_size);
+ rbf->ringbuf_data = (char *)calloc(1, ringbuf_size);
+ ringbuf_setup(rbf->ringbuf, thread_tot_num, ringbuf_size);
+ return rbf;
+}
+
+void stm_ringbuf_wrap_free(struct monitor_ringbuf_wrap *rbf)
+{
+ if (NULL == rbf)
+ {
+ return;
+ }
+ if (rbf->ringbuf)
+ {
+ free(rbf->ringbuf);
+ }
+ if (rbf->ringbuf_data)
+ {
+ free(rbf->ringbuf_data);
+ }
+ free(rbf);
+}
+
+void stm_ringbuf_get_statistics(const struct monitor_ringbuf_wrap *rbf, unsigned long long *push_number, unsigned long long *push_bytes, unsigned long long *pop_number, unsigned long long *pop_bytes)
+{
+ if (NULL == rbf)
+ {
+ return;
+ }
+ if (push_number)
+ {
+ *push_number = rbf->push_number;
+ }
+ if (push_bytes)
+ {
+ *push_bytes = rbf->push_bytes;
+ }
+ if (pop_number)
+ {
+ *pop_number = rbf->pop_number;
+ }
+ if (pop_bytes)
+ {
+ *pop_bytes = rbf->pop_bytes;
+ }
+} \ No newline at end of file
diff --git a/infra/monitor/monitor_ringbuf.h b/infra/monitor/monitor_ringbuf.h
new file mode 100644
index 0000000..294bac5
--- /dev/null
+++ b/infra/monitor/monitor_ringbuf.h
@@ -0,0 +1,30 @@
+#pragma once
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "ringbuf/ringbuf.h"
+
+ /*
+ Message format:
+ || header | payload ....(variable-length) || header | payload ....(variable-length) || ....
+ */
+
+ struct monitor_ringbuf_wrap;
+ struct monitor_ringbuf_wrap *stm_ringbuf_wrap_new(int thread_tot_num, size_t ringbuf_size);
+ int stm_ringbuf_easy_push(int thread_id, struct monitor_ringbuf_wrap *rbf, const void *value, size_t len);
+ void stm_ringbuf_release(struct monitor_ringbuf_wrap *rbf, int rel_len);
+ void *stm_ringbuf_pop(struct monitor_ringbuf_wrap *rbf, size_t *pop_len);
+ void stm_ringbuf_wrap_free(struct monitor_ringbuf_wrap *rbf);
+ ssize_t stm_ringbuf_stream_start(int thread_id, struct monitor_ringbuf_wrap *rbf, size_t require_size);
+ int stm_ringbuf_stream_append(int thread_id, struct monitor_ringbuf_wrap *rbf, size_t rbf_offset, const void *value, size_t len);
+ void stm_ringbuf_stream_finish(int thread_id, struct monitor_ringbuf_wrap *rbf);
+ void stm_ringbuf_get_statistics(const struct monitor_ringbuf_wrap *rbf, unsigned long long *push_number,
+ unsigned long long *push_bytes, unsigned long long *pop_number, unsigned long long *pop_bytes);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/infra/monitor/monitor_rpc.c b/infra/monitor/monitor_rpc.c
new file mode 100644
index 0000000..8e2af67
--- /dev/null
+++ b/infra/monitor/monitor_rpc.c
@@ -0,0 +1,115 @@
+#include <stddef.h>
+#include "stellar/monitor.h"
+#include "monitor_private.h"
+#include "monitor_rpc.h"
+#include "stellar/module.h"
+
+#define RPC_WORKER_THREAD_BUSY 1
+#define RPC_WORKER_THREAD_IDLE 0
+
+struct monitor_rpc_msg_hdr
+{
+ unsigned int type;
+ unsigned int length; // total messaage length, include this header, = payload length + sizeof(struct monitor_rpc_msg_hdr)
+ char value[0]; // variable-length, continuous
+} __attribute__((packed));
+
+enum monitor_rpc_ringbuf_dir
+{ // full duplex, dir: 0: worker thread to monitor thread; 1: monitor thread to worker thread
+ RPC_RINBUG_DIR_W2M = 0,
+ RPC_RINBUG_DIR_M2W = 1,
+ RPC_RINBUG_DIR_MAX = 2,
+};
+
+struct monitor_rpc
+{
+ volatile long atomic_val;
+
+ monitor_rpc_callabck *rpc_cb;
+ void *rpc_args;
+ struct iovec rpc_request;
+ struct iovec rpc_response;
+};
+
+struct iovec stm_rpc_call(struct monitor_rpc *rpc_ins, struct iovec rpc_request, monitor_rpc_callabck *cb, void *user_args)
+{
+ while (__sync_or_and_fetch(&rpc_ins->atomic_val, 0) == RPC_WORKER_THREAD_BUSY)
+ {
+ // wait for the last rpc response, not support concurrent rpc yet!
+ usleep(1000);
+ }
+ rpc_ins->rpc_cb = cb;
+ rpc_ins->rpc_args = user_args;
+ rpc_ins->rpc_request = rpc_request;
+ __sync_fetch_and_or(&rpc_ins->atomic_val, 1);
+
+ while (__sync_or_and_fetch(&rpc_ins->atomic_val, 0) == RPC_WORKER_THREAD_BUSY)
+ {
+ // wait for the rpc response...
+ usleep(1000);
+ }
+ return rpc_ins->rpc_response;
+}
+
+int stm_rpc_exec(int thread_idx, struct monitor_rpc *rpc_ins)
+{
+ if (0 == __sync_or_and_fetch(&rpc_ins->atomic_val, RPC_WORKER_THREAD_IDLE))
+ {
+ return 0;
+ }
+ rpc_ins->rpc_response = rpc_ins->rpc_cb(thread_idx, rpc_ins->rpc_request, rpc_ins->rpc_args);
+ __sync_fetch_and_and(&rpc_ins->atomic_val, RPC_WORKER_THREAD_IDLE);
+ return 1;
+}
+
+/*
+ * Communicate between different threads by ringbuf.
+ */
+struct iovec monitor_worker_thread_rpc(struct stellar_monitor *stm, int worker_thread_idx, struct iovec rpc_request, monitor_rpc_callabck *cb, void *user_args)
+{
+ int worker_thread_num = module_manager_get_max_thread_num(stm->mod_mgr_ref);
+ if (worker_thread_idx >= worker_thread_num)
+ {
+ struct iovec response = {0};
+ return response;
+ }
+ struct monitor_rpc *rpc_ins = stm->rpc_ins_array[worker_thread_idx];
+ return stm_rpc_call(rpc_ins, rpc_request, cb, user_args);
+}
+
+__thread long long rpc_idle_num = 0;
+
+void module_rpc_worker_thread_polling_cb(struct module_manager *mod_mgr, void *polling_arg)
+{
+ struct stellar_monitor *stm = (struct stellar_monitor *)polling_arg;
+ int thread_idx = module_manager_get_thread_id(mod_mgr);
+ struct monitor_rpc *rpc_ins = stm->rpc_ins_array[thread_idx];
+
+ stm_rpc_exec(thread_idx, rpc_ins);
+}
+
+struct monitor_rpc *stm_rpc_new(void)
+{
+ struct monitor_rpc *rpc_ins = (struct monitor_rpc *)calloc(1, sizeof(struct monitor_rpc));
+ return rpc_ins;
+}
+
+void stm_rpc_free(struct monitor_rpc *rpc_ins)
+{
+ if (NULL == rpc_ins)
+ {
+ return;
+ }
+ free(rpc_ins);
+}
+
+struct monitor_rpc *monitor_rpc_new(struct stellar_monitor *stm, struct module_manager *mod_mgr)
+{
+ module_manager_polling_subscribe(mod_mgr, module_rpc_worker_thread_polling_cb, (void *)stm);
+ return stm_rpc_new();
+}
+
+void monitor_rpc_free(struct monitor_rpc *rpc_ins)
+{
+ stm_rpc_free(rpc_ins);
+} \ No newline at end of file
diff --git a/infra/monitor/monitor_rpc.h b/infra/monitor/monitor_rpc.h
new file mode 100644
index 0000000..9945c5c
--- /dev/null
+++ b/infra/monitor/monitor_rpc.h
@@ -0,0 +1,10 @@
+#pragma once
+#include <stdint.h>
+#include <stddef.h>
+#include "stellar/monitor.h"
+#include <bits/types/struct_iovec.h>
+
+struct monitor_rpc;
+
+typedef struct iovec(monitor_rpc_callabck)(int worker_thread_idx, struct iovec user_data, void *user_args);
+struct iovec monitor_worker_thread_rpc(struct stellar_monitor *monitor, int worker_thread_idx, struct iovec user_data, monitor_rpc_callabck *cb, void *user_args);
diff --git a/infra/monitor/monitor_server.c b/infra/monitor/monitor_server.c
new file mode 100644
index 0000000..35bcea3
--- /dev/null
+++ b/infra/monitor/monitor_server.c
@@ -0,0 +1,591 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <pthread.h>
+#include <assert.h>
+#include <evhttp.h>
+#include <event2/event.h>
+#include <event2/keyvalq_struct.h>
+#include <sys/queue.h>
+#include "stellar/log.h"
+#include "monitor_private.h"
+#include "monitor_cmd_assistant.h"
+#include "monitor/monitor_utils.h"
+#include "toml/toml.h"
+#include "sds/sds.h"
+#include "uthash/utlist.h"
+
+static __thread struct stellar_monitor *__thread_local_stm;
+static __thread pthread_t __thread_local_tid;
+static __thread pthread_t __stm_libevevt_callback_thread_local_tid;
+static __thread struct logger *__stm_thread_local_logger = NULL;
+
+static void stm_save_thread_local_context(struct stellar_monitor *stm)
+{
+ __thread_local_stm = stm;
+ __thread_local_tid = pthread_self();
+ __stm_thread_local_logger = stm->logger_ref;
+}
+
+static void stm_connection_close_notify(struct stellar_monitor *stm, UNUSED struct evhttp_connection *evconn)
+{
+ struct stm_conn_close_cb_manager *ele, *tmp;
+ DL_FOREACH_SAFE(stm->conn_close_mgr, ele, tmp)
+ {
+ ele->cb(&stm->current_conn, ele->arg);
+ }
+}
+
+static void on_connection_close_cb(UNUSED struct evhttp_connection *ev_conn, UNUSED void *arg)
+{
+ __stm_libevevt_callback_thread_local_tid = pthread_self();
+ struct stellar_monitor *stm = stellar_monitor_get();
+ stm->current_conn.current_evconn_ref = ev_conn;
+ stm_spinlock_lock(stm->lock);
+ stm_connection_delete(ev_conn);
+ stm_connection_close_notify(stm, ev_conn);
+ stm_spinlock_unlock(stm->lock);
+ char *peer_ip_addr;
+ uint16_t peer_port;
+ evhttp_connection_get_peer(ev_conn, &peer_ip_addr, &peer_port);
+ STELLAR_LOG_INFO(stm->logger_ref, STM_LOG_MODULE_NAME, "cli connection closed, client %s:%u\n", peer_ip_addr, peer_port);
+ stm_stat_update(stm->stat, stm->worker_thread_num, STM_STAT_CLI_CONNECTION_CLOSE, 1);
+ stm->current_conn.current_evconn_ref = NULL;
+}
+
+static void stm_command_send_reply_by_cstr(struct evhttp_request *request, int http_status_code, const char *reply, UNUSED void *params)
+{
+ struct evbuffer *buffer = evbuffer_new();
+ evbuffer_add(buffer, reply, strlen(reply));
+ evhttp_send_reply(request, http_status_code, "OK", buffer);
+ evbuffer_free(buffer);
+}
+
+static void stm_command_send_reply(struct evhttp_request *request, struct monitor_reply *reply)
+{
+ struct evbuffer *buffer = evbuffer_new();
+ sds reply_str = monitor_reply_to_string(reply);
+ evbuffer_add(buffer, reply_str, sdslen(reply_str));
+ evhttp_send_reply(request, reply->http_code, reply->http_reason, buffer);
+ evbuffer_free(buffer);
+ sdsfree(reply_str);
+ monitor_reply_free(reply);
+}
+
+static void stm_command_notfound(struct evhttp_request *request, UNUSED void *arg)
+{
+ struct stellar_monitor *stm = stellar_monitor_get();
+ const char *req_str_uri = evhttp_request_get_uri(request);
+ struct evkeyvalq headers = {};
+ evhttp_parse_query(req_str_uri, &headers);
+ const char *raw_cmd_content = evhttp_find_header(&headers, STM_RESTFUL_URI_CMD_KEY);
+
+ stm_command_send_reply(request, monitor_reply_new_error(error_format_unknown_command, raw_cmd_content));
+ STELLAR_LOG_ERROR(stm->logger_ref, STM_LOG_MODULE_NAME, "invlid http uri: %s\r\n", evhttp_request_get_uri(request));
+ evhttp_clear_headers(&headers);
+}
+
+static void stm_exec_command(struct stellar_monitor *stm, struct evhttp_request *request, const char *cmd_line)
+{
+ stm_spinlock_lock(stm->lock);
+ monitor_cmd_cb *cmd_cb = stm_cmd_assistant_get_cb(stm->aide, cmd_line);
+ if (NULL == cmd_cb)
+ {
+ stm_command_notfound(request, NULL);
+ stm_spinlock_unlock(stm->lock);
+ return;
+ }
+ void *cmd_user_arg = stm_cmd_assistant_get_user_arg(stm->aide, cmd_line);
+ int argc;
+ sds *cmd_argv = sdssplitargs(cmd_line, &argc);
+ struct monitor_reply *reply = cmd_cb(stm, argc, cmd_argv, cmd_user_arg);
+
+ stm_command_send_reply(request, reply);
+ sdsfreesplitres(cmd_argv, argc);
+ stm_spinlock_unlock(stm->lock);
+}
+
+static void stm_new_request_cb(struct evhttp_request *request, UNUSED void *privParams)
+{
+ __stm_libevevt_callback_thread_local_tid = pthread_self();
+ struct stellar_monitor *stm = stellar_monitor_get();
+ struct evhttp_connection *ev_conn = evhttp_request_get_connection(request);
+ stm->current_conn.current_evconn_ref = ev_conn;
+ stm_spinlock_lock(stm->lock);
+ stm_connection_insert(ev_conn);
+ stm_spinlock_unlock(stm->lock);
+ evhttp_connection_set_closecb(ev_conn, on_connection_close_cb, request);
+ // evhttp_request_set_error_cb(request, on_request_error_cb);
+
+ const char *req_str_uri = evhttp_request_get_uri(request);
+ char *peer_ip_addr;
+ uint16_t peer_port;
+ evhttp_connection_get_peer(ev_conn, &peer_ip_addr, &peer_port);
+
+ STELLAR_LOG_INFO(stm->logger_ref, STM_LOG_MODULE_NAME, "new cli request, client:%s:%u, uri: %s\n", peer_ip_addr, peer_port, req_str_uri);
+
+ struct evkeyvalq headers = {};
+ evhttp_parse_query(req_str_uri, &headers);
+ const char *raw_cmd_content = evhttp_find_header(&headers, STM_RESTFUL_URI_CMD_KEY);
+ if (NULL == raw_cmd_content)
+ {
+ stm_command_send_reply_by_cstr(request, HTTP_BADREQUEST, "http uri syntax error\r\n", NULL);
+ evhttp_clear_headers(&headers);
+ return;
+ }
+ stm_exec_command(stm, request, raw_cmd_content);
+ evhttp_clear_headers(&headers);
+}
+
+static int stm_event_http_init(struct stellar_monitor *stm)
+{
+ // Create a new event handler
+ stm->evt_base = event_base_new();
+ // Create a http server using that handler
+ stm->evt_http_server = evhttp_new(stm->evt_base);
+ // Limit serving GET requests
+ evhttp_set_allowed_methods(stm->evt_http_server, EVHTTP_REQ_GET);
+
+ char restful_path[256] = {0}; /* must start with '/' */
+ snprintf(restful_path, sizeof(restful_path), "/%s/%s", STM_RESTFUL_VERSION, STM_RESTFUL_RESOURCE);
+ evhttp_set_cb(stm->evt_http_server, restful_path, stm_new_request_cb, stm->evt_base);
+
+ // Set the callback for anything not recognized
+ evhttp_set_gencb(stm->evt_http_server, stm_command_notfound, NULL);
+ if (evhttp_bind_socket(stm->evt_http_server, stm->config->listen_ipaddr, stm->config->listen_port_host_order) != 0)
+ {
+ STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "Could not bind to %s:%u\r\n", stm->config->listen_ipaddr, stm->config->listen_port_host_order);
+ return -1;
+ }
+ evhttp_set_timeout(stm->evt_http_server, stm->config->connection_idle_timeout);
+ STELLAR_LOG_INFO(stm->logger_ref, STM_LOG_MODULE_NAME, "accept http uri path: %s\r\n", restful_path);
+ return 0;
+}
+
+static void *stm_event_main_loop(void *arg)
+{
+ struct stellar_monitor *stm = (struct stellar_monitor *)arg;
+ stm_save_thread_local_context(stm);
+ event_base_dispatch(stm->evt_base);
+ return NULL;
+}
+
+static void stm_event_http_free(struct stellar_monitor *stm)
+{
+ event_base_loopbreak(stm->evt_base);
+ pthread_cancel(stm->evt_main_loop_tid);
+ pthread_join(stm->evt_main_loop_tid, NULL);
+ evhttp_free(stm->evt_http_server);
+ // event_free(stm->ev_timeout);
+ event_base_free(stm->evt_base);
+}
+
+static void stm_server_set_default_cfg(struct stellar_monitor_config *config)
+{
+ config->ringbuf_size = STM_RINGBUF_SIZE;
+ config->connection_idle_timeout = STM_CONNECTION_IDLE_TIMEOUT;
+ config->cli_request_timeout = STM_REQUEST_TIMEOUT;
+ config->listen_ipaddr = "0.0.0.0";
+ config->listen_port_host_order = STM_SERVER_LISTEN_PORT;
+ config->data_link_bind_port_host_order = STM_TZSP_UDP_PORT;
+ config->output_interval_ms = STM_STAT_OUTPUT_INTERVAL_MS;
+}
+
+int stellar_monitor_set_gettime_callback(struct stellar_monitor *stm, int (*gettime_cb)(struct timeval *tv, struct timezone *tz))
+{
+ if (NULL == gettime_cb)
+ {
+ return -1;
+ }
+ stm->gettime_cb = gettime_cb;
+ return 0;
+}
+
+struct stellar_monitor_config *stellar_monitor_config_new(const char *toml_file)
+{
+ struct stellar_monitor_config *config = CALLOC(struct stellar_monitor_config, 1);
+ stm_server_set_default_cfg(config);
+ int64_t int64_val = 0;
+ char errbuf[256];
+ FILE *fp = NULL;
+ toml_table_t *root = NULL;
+ toml_table_t *table = NULL;
+ toml_raw_t ptr = NULL;
+
+ fp = fopen(toml_file, "r");
+ if (fp == NULL)
+ {
+ fprintf(stderr, "config file %s open failed, %s", toml_file, strerror(errno));
+ goto fail_exit;
+ }
+
+ root = toml_parse_file(fp, errbuf, sizeof(errbuf));
+ if (root == NULL)
+ {
+ fprintf(stderr, "config file %s parse failed, %s", toml_file, errbuf);
+ goto fail_exit;
+ }
+
+ table = toml_table_in(root, "monitor");
+ if (table == NULL)
+ {
+ fprintf(stderr, "config file %s missing [monitor]", toml_file);
+ goto fail_exit;
+ }
+
+ /* listen_port */
+ ptr = toml_raw_in(table, "listen_port");
+ if (ptr != NULL && toml_rtoi(ptr, &int64_val) == 0)
+ {
+ if (int64_val < 1 || int64_val > 65535)
+ {
+ fprintf(stderr, "invalid monitor.listen_port %ld\n", int64_val);
+ FREE(config);
+ goto fail_exit;
+ }
+ config->listen_port_host_order = (uint16_t)int64_val;
+ }
+
+ /* data link bind port */
+ ptr = toml_raw_in(table, "data_link_bind_port");
+ if (ptr != NULL && toml_rtoi(ptr, &int64_val) == 0)
+ {
+ if (int64_val < 1 || int64_val > 65535)
+ {
+ fprintf(stderr, "invalid monitor.data_link_bind_port %ld\n", int64_val);
+ FREE(config);
+ goto fail_exit;
+ }
+ config->data_link_bind_port_host_order = (uint16_t)int64_val;
+ }
+
+ /* connection_idle_timeout */
+ ptr = toml_raw_in(table, "connection_idle_timeout");
+ if (ptr != NULL && toml_rtoi(ptr, &int64_val) == 0)
+ {
+ if (int64_val < 1 || int64_val > 3600)
+ {
+ fprintf(stderr, "invalid monitor.connection_idle_timeout %ld, should be [1, 3600]\n", int64_val);
+ FREE(config);
+ goto fail_exit;
+ }
+ config->connection_idle_timeout = (int)int64_val;
+ }
+
+ /* cli_request_timeout */
+ ptr = toml_raw_in(table, "cli_request_timeout");
+ if (ptr != NULL || toml_rtoi(ptr, &int64_val) == 0)
+ {
+ if (int64_val < 1 || int64_val > 360)
+ {
+ fprintf(stderr, "invalid monitor.cli_request_timeout %ld, , should be [1, 360]\n", int64_val);
+ FREE(config);
+ goto fail_exit;
+ }
+ config->cli_request_timeout = (int)int64_val;
+ }
+
+ /* stat */
+ ptr = toml_raw_in(table, "stat_output_path");
+ if (ptr == NULL || toml_rtos(ptr, &config->output_path) != 0)
+ {
+ config->output_path = strdup(STM_STAT_OUTPUT_PATH);
+ }
+
+ ptr = toml_raw_in(table, "stat_output_interval_ms");
+ if (ptr != NULL && toml_rtoi(ptr, &int64_val) == 0)
+ {
+ if (int64_val < 1000 || int64_val > 1000 * 60)
+ {
+ fprintf(stderr, "invalid monitor.stat_output_interval_ms %ld, , should be [1, 600000]\n", int64_val);
+ FREE(config);
+ goto fail_exit;
+ }
+ config->output_interval_ms = (int)int64_val;
+ }
+
+fail_exit:
+ if (root)
+ {
+ toml_free(root);
+ }
+ if (fp)
+ {
+ fclose(fp);
+ }
+ return config;
+}
+
+struct stellar_monitor *stellar_monitor_get(void)
+{
+ if (pthread_self() != __thread_local_tid)
+ {
+ assert(0);
+ // fprintf(stderr, "ERR stellar_monitor_get() failed, caller must in same thread context!\n");
+ return NULL;
+ }
+ return __thread_local_stm;
+}
+
+// support dynamic register command, independent of the order of initialization
+int monitor_register_cmd(struct stellar_monitor *stm, const char *cmd, monitor_cmd_cb *cb, const char *flags,
+ const char *hint, const char *desc, void *arg)
+{
+ stm_spinlock_lock(stm->lock);
+ int ret = stm_cmd_assistant_register_cmd(stm->aide, cmd, cb, arg, flags, hint, desc);
+ stm_spinlock_unlock(stm->lock);
+ return ret;
+}
+
+int monitor_register_connection_close_cb(struct stellar_monitor *stm, monitor_connection_close_cb *cb, void *arg)
+{
+ stm_spinlock_lock(stm->lock);
+ struct stm_conn_close_cb_manager *ele = CALLOC(struct stm_conn_close_cb_manager, 1);
+ ele->cb = cb;
+ ele->arg = arg;
+ DL_APPEND(stm->conn_close_mgr, ele);
+ stm_spinlock_unlock(stm->lock);
+ return 0;
+}
+
+static struct monitor_reply *monitor_cmd_show_brief_cb(struct stellar_monitor *stm)
+{
+ sds cmd_brief = sdsempty();
+ cmd_brief = sdscatfmt(cmd_brief, "%s, %s\r\n", "\"command\"", "description");
+ cmd_brief = sdscatfmt(cmd_brief, "-----------------------------\r\n");
+ sds cmd_brief_cont = stm_cmd_assistant_list_cmd_brief(stm->aide);
+ cmd_brief = sdscatsds(cmd_brief, cmd_brief_cont);
+ struct monitor_reply *reply = monitor_reply_new_string("%s", cmd_brief);
+ sdsfree(cmd_brief);
+ sdsfree(cmd_brief_cont);
+ return reply;
+}
+
+static struct monitor_reply *monitor_cmd_show_verbose_cb(struct stellar_monitor *stm)
+{
+ sds cmd_verbose = stm_cmd_assistant_list_cmd_verbose(stm->aide);
+ struct monitor_reply *reply = monitor_reply_new_string("%s", cmd_verbose);
+ sdsfree(cmd_verbose);
+ return reply;
+}
+
+static struct monitor_reply *monitor_server_builtin_show_command_cb(struct stellar_monitor *stm UNUSED, int argc, char *argv[], UNUSED void *arg)
+{
+ if (argc != 3)
+ {
+ return monitor_reply_new_error(error_format_wrong_number_of_args, "show command");
+ }
+ if (stm_strncasecmp_exactly(argv[2], "brief", 5) == 0)
+ {
+ return monitor_cmd_show_brief_cb((struct stellar_monitor *)arg);
+ }
+ else if (stm_strncasecmp_exactly(argv[2], "verbose", 7) == 0)
+ {
+ return monitor_cmd_show_verbose_cb((struct stellar_monitor *)arg);
+ }
+ return monitor_reply_new_error(error_format_unknown_arg, argv[2]);
+}
+
+static struct monitor_reply *monitor_server_builtin_ping_cb(struct stellar_monitor *stm UNUSED, int argc, char *argv[], UNUSED void *arg)
+{
+ if (argc == 1)
+ {
+ return monitor_reply_new_string("pong");
+ }
+ else if (argc == 2)
+ {
+ return monitor_reply_new_string("%s", argv[1]);
+ }
+ return monitor_reply_new_error(error_format_wrong_number_of_args, "ping");
+}
+
+static struct monitor_reply *monitor_server_builtin_who_cb(struct stellar_monitor *stm, int argc UNUSED, char *argv[] UNUSED, UNUSED void *arg)
+{
+ struct stm_connection_manager *conn_mgr = stm->connection_mgr;
+ struct stm_connection_manager *ele, *tmp;
+ sds who = sdsempty();
+ char timestr[64];
+ DL_FOREACH_SAFE(conn_mgr, ele, tmp)
+ {
+ struct timeval tv = ele->link_start_time;
+ struct tm *tm = localtime(&tv.tv_sec);
+ strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", tm);
+ who = sdscatprintf(who, "%s %s:%u", timestr, ele->peer_ipaddr, ele->peer_port_host_order);
+ if (stm->current_conn.current_evconn_ref == ele->conn)
+ {
+ who = sdscat(who, "\033[1m [current]\033[0m");
+ }
+ who = sdscat(who, "\r\n");
+ }
+ sdsIncrLen(who, -2); // delete last \r\n
+ struct monitor_reply *reply = monitor_reply_new_string("%s", who);
+ sdsfree(who);
+ return reply;
+}
+
+static int stm_builtin_cmd_register(struct stellar_monitor *stm)
+{
+ int ret = 0;
+ ret += monitor_register_cmd(stm, "show command", monitor_server_builtin_show_command_cb, "readonly", "[ brief|verbose ]", "show all registered commands info", (void *)stm);
+ assert(ret == 0);
+ ret += monitor_register_cmd(stm, "who", monitor_server_builtin_who_cb, "readonly", "<cr>", "show who is logged on", (void *)stm);
+ assert(ret == 0);
+ ret += monitor_register_cmd(stm, "ping", monitor_server_builtin_ping_cb, "readonly", "[message]", "ping the server", (void *)stm);
+ assert(ret == 0);
+ return ret;
+}
+
+struct monitor_connection *monitor_get_current_connection(struct stellar_monitor *monitor)
+{
+ if (__stm_libevevt_callback_thread_local_tid != pthread_self())
+ {
+ return NULL;
+ }
+ return &monitor->current_conn;
+}
+
+int monitor_get_peer_addr(struct monitor_connection *conn, char **peer_ip, uint16_t *peer_port)
+{
+ if (NULL == conn || conn->current_evconn_ref == NULL)
+ {
+ if (peer_ip)
+ {
+ *peer_ip = NULL;
+ }
+ if (peer_port)
+ {
+ *peer_port = 0;
+ }
+ return -1;
+ }
+ evhttp_connection_get_peer(conn->current_evconn_ref, peer_ip, peer_port);
+ return 0;
+}
+
+void monitor_free(struct stellar_monitor *stm)
+{
+ STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "free and exit\n");
+ stm_event_http_free(stm);
+ stm_stat_free(stm->stat);
+ stm_cmd_assistant_free(stm->aide);
+ stm_spinlock_free(stm->lock);
+ if (stm->rpc_ins_array)
+ {
+ for (int tid = 0; tid < stm->worker_thread_num; tid++)
+ {
+ monitor_rpc_free(stm->rpc_ins_array[tid]);
+ }
+ free(stm->rpc_ins_array);
+ }
+
+ __thread_local_stm = NULL;
+ FREE(stm->config->output_path);
+ FREE(stm->config);
+ FREE(stm);
+}
+
+struct stellar_monitor *monitor_module_to_monitor(struct module *monitor_module)
+{
+ if (monitor_module == NULL)
+ {
+ return NULL;
+ }
+ return (struct stellar_monitor *)module_get_ctx(monitor_module);
+}
+
+struct stellar_monitor *stellar_module_get_monitor(struct module_manager *mod_mgr)
+{
+ assert(mod_mgr);
+ struct module *monitor_mod = module_manager_get_module(mod_mgr, MONITOR_MODULE_NAME);
+ return monitor_module_to_monitor(monitor_mod);
+}
+
+void monitor_on_exit(struct module_manager *mod_mgr __attribute__((unused)), struct module *mod)
+{
+ if (mod)
+ {
+ struct stellar_monitor *stm = module_get_ctx(mod);
+ monitor_free(stm);
+ module_free(mod);
+ }
+}
+
+struct stellar_monitor *monitor_new(const char *toml_file, struct module_manager *mod_mgr, struct logger *logh)
+{
+ struct stellar_monitor *stm = (struct stellar_monitor *)calloc(1, sizeof(struct stellar_monitor));
+ stm->logger_ref = logh;
+ stm->mod_mgr_ref = mod_mgr;
+
+ struct stellar_monitor_config *config = stellar_monitor_config_new(toml_file);
+ if (NULL == config)
+ {
+ STELLAR_LOG_FATAL(logh, STM_LOG_MODULE_NAME, "get config failed!\n");
+ goto fail_exit;
+ }
+ stm->config = config;
+
+ stm->worker_thread_num = module_manager_get_max_thread_num(mod_mgr);
+
+ stm->lock = stm_spinlock_new();
+ stm->worker_thread_num = module_manager_get_max_thread_num(mod_mgr);
+ assert(stm->worker_thread_num > 0);
+ stm->gettime_cb = gettimeofday;
+ stm->aide = stm_cmd_assistant_new();
+ if (stm_event_http_init(stm) < 0)
+ {
+ STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "libevent http server init() failed!\n");
+ goto fail_exit;
+ }
+ stm->stat = stm_stat_init(stm);
+ stm_builtin_cmd_register(stm);
+ stm_save_thread_local_context(stm);
+ pthread_create(&stm->evt_main_loop_tid, NULL, stm_event_main_loop, (void *)stm);
+ sds config_print = stm_config_print(config);
+ STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "config: %s\n", config_print);
+ sdsfree(config_print);
+
+ stm->rpc_ins_array = (struct monitor_rpc **)calloc(stm->worker_thread_num, sizeof(struct monitor_rpc *));
+ for (int tid = 0; tid < stm->worker_thread_num; tid++)
+ {
+ stm->rpc_ins_array[tid] = monitor_rpc_new(stm, mod_mgr);
+ if (stm->rpc_ins_array[tid] == NULL)
+ {
+ STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "rpc init failed\n");
+ goto fail_exit;
+ }
+ }
+
+ stm_save_thread_local_context(stm);
+ return stm;
+
+fail_exit:
+ monitor_free(stm);
+ return NULL;
+}
+
+struct module *monitor_on_init(struct module_manager *mod_mgr)
+{
+ assert(mod_mgr);
+ const char *toml_file = module_manager_get_toml_path(mod_mgr);
+ assert(toml_file);
+ struct logger *logh = module_manager_get_logger(mod_mgr);
+ assert(logh);
+
+ struct stellar_monitor *stm = monitor_new(toml_file, mod_mgr, logh);
+ struct module *stm_mod = module_new(MONITOR_MODULE_NAME, (void *)stm);
+ if (stm_mod == NULL)
+ {
+ STELLAR_LOG_FATAL(logh, STM_LOG_MODULE_NAME, "moudule new '%s' fail\n", MONITOR_MODULE_NAME);
+ monitor_free(stm);
+ return NULL;
+ }
+
+ // show_session_enforcer_init(mod_mgr, stm);
+ // stm_pktdump_enforcer_init(mod_mgr, stm);
+
+ STELLAR_LOG_FATAL(logh, STM_LOG_MODULE_NAME, "init success\n");
+ return stm_mod;
+}
diff --git a/infra/monitor/monitor_spinlock.c b/infra/monitor/monitor_spinlock.c
new file mode 100644
index 0000000..9614803
--- /dev/null
+++ b/infra/monitor/monitor_spinlock.c
@@ -0,0 +1,72 @@
+// https://www.cs.utexas.edu/~pingali/CS378/2015sp/lectures/Spinlocks%20and%20Read-Write%20Locks.htm
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <pthread.h>
+
+#if 0 /* use gcc builtin function */
+struct stm_spinlock
+{
+ long value;
+};
+
+struct stm_spinlock *stm_spinlock_new(void)
+{
+ struct stm_spinlock *splock = (struct stm_spinlock *)calloc(1, sizeof(struct stm_spinlock));
+ return splock;
+}
+
+void stm_spinlock_lock(struct stm_spinlock *splock)
+{
+ while (__sync_lock_test_and_set(&splock->value, 1))
+ {
+ }
+}
+
+void stm_spinlock_unlock(struct stm_spinlock *splock)
+{
+ __sync_lock_release(&splock->value);
+}
+
+void stm_spinlock_free(struct stm_spinlock *splock)
+{
+ if (splock)
+ {
+ free(splock);
+ }
+}
+#else /* pthread spin lock */
+struct stm_spinlock
+{
+ pthread_spinlock_t lock_ins;
+};
+
+struct stm_spinlock *stm_spinlock_new(void)
+{
+ struct stm_spinlock *splock = (struct stm_spinlock *)calloc(1, sizeof(struct stm_spinlock));
+ pthread_spin_init(&splock->lock_ins, PTHREAD_PROCESS_PRIVATE);
+ return splock;
+}
+
+void stm_spinlock_lock(struct stm_spinlock *splock)
+{
+ pthread_spin_lock(&splock->lock_ins);
+}
+
+void stm_spinlock_unlock(struct stm_spinlock *splock)
+{
+ pthread_spin_unlock(&splock->lock_ins);
+}
+
+void stm_spinlock_free(struct stm_spinlock *splock)
+{
+ if (splock)
+ {
+ pthread_spin_destroy(&splock->lock_ins);
+ free(splock);
+ }
+}
+#endif \ No newline at end of file
diff --git a/infra/monitor/monitor_spinlock.h b/infra/monitor/monitor_spinlock.h
new file mode 100644
index 0000000..fb5acb2
--- /dev/null
+++ b/infra/monitor/monitor_spinlock.h
@@ -0,0 +1,8 @@
+#pragma once
+
+struct stm_spinlock;
+
+struct stm_spinlock *stm_spinlock_new(void);
+void stm_spinlock_lock(struct stm_spinlock *splock);
+void stm_spinlock_unlock(struct stm_spinlock *splock);
+void stm_spinlock_free(struct stm_spinlock *splock);
diff --git a/infra/monitor/monitor_stat.c b/infra/monitor/monitor_stat.c
new file mode 100644
index 0000000..2216b09
--- /dev/null
+++ b/infra/monitor/monitor_stat.c
@@ -0,0 +1,76 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+#include <evhttp.h>
+#include "monitor_private.h"
+#include <fieldstat/fieldstat_easy.h>
+
+static const char *stm_stat_field_name[] = {
+ "connection_new",
+ "connection_close",
+ "request_succ",
+ "request_err",
+ "response_succ",
+ "response_err",
+ NULL,
+};
+
+long long stm_get_stat_count(struct stm_stat *stat, enum stm_stat_type type)
+{
+ if ((int)type < 0 || type >= STM_STAT_MAX)
+ {
+ return 0;
+ }
+ return stat->counters[type].count;
+}
+
+long long stm_get_stat_bytes(struct stm_stat *stat, enum stm_stat_type type)
+{
+ if ((int)type < 0 || type >= STM_STAT_MAX)
+ {
+ return 0;
+ }
+ return stat->counters[type].bytes;
+}
+
+void stm_stat_update(struct stm_stat *stat, int thread_idx, enum stm_stat_type type, long long value)
+{
+ if ((int)type < 0 || type >= STM_STAT_MAX)
+ {
+ return;
+ }
+ fieldstat_easy_counter_incrby(stat->fs4_ins, thread_idx, stat->counters[type].counter_id, NULL, 0, value);
+}
+
+struct stm_stat *stm_stat_init(struct stellar_monitor *stm)
+{
+ const struct stellar_monitor_config *config = stm->config;
+ assert(sizeof(stm_stat_field_name) / sizeof(stm_stat_field_name[0]) == STM_STAT_MAX + 1);
+ struct stm_stat *stat = CALLOC(struct stm_stat, 1);
+ /* worker thread count + 1, reserved for libevent callback thread context */
+ stat->fs4_ins = fieldstat_easy_new(stm->worker_thread_num + 1, "monitor", NULL, 0);
+ for (int i = 0; stm_stat_field_name[i] != NULL; i++)
+ {
+ stat->counters[i].counter_id = fieldstat_easy_register_counter(stat->fs4_ins, stm_stat_field_name[i]);
+ }
+ fieldstat_easy_enable_auto_output(stat->fs4_ins, config->output_path, MAX(config->output_interval_ms / 1000, 1));
+ return stat;
+}
+
+void stm_stat_free(struct stm_stat *stat)
+{
+ if (NULL == stat)
+ {
+ return;
+ }
+ if (stat->fs4_ins)
+ {
+ fieldstat_easy_free(stat->fs4_ins);
+ }
+ FREE(stat);
+} \ No newline at end of file
diff --git a/infra/monitor/monitor_transaction.c b/infra/monitor/monitor_transaction.c
new file mode 100644
index 0000000..0f9aba8
--- /dev/null
+++ b/infra/monitor/monitor_transaction.c
@@ -0,0 +1,83 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <pthread.h>
+#include "uthash/utlist.h"
+#include "monitor_private.h"
+
+/******************************************** connection manager *****************************************/
+struct stm_connection_manager *stm_connection_insert(struct evhttp_connection *evconn)
+{
+ struct stellar_monitor *stm = stellar_monitor_get();
+ struct stm_connection_manager *conn_mgr = stm->connection_mgr;
+ struct stm_connection_manager *ele, *tmp;
+ DL_FOREACH_SAFE(conn_mgr, ele, tmp) // check if current connection already exist
+ {
+ if (ele->conn == evconn)
+ {
+ stm->gettime_cb(&ele->last_active_time, NULL);
+ return ele;
+ }
+ }
+
+ stm_stat_update(stm->stat, stm->worker_thread_num, STM_STAT_CLI_CONNECTION_NEW, 1);
+ struct stm_connection_manager *new_conn = (struct stm_connection_manager *)calloc(1, sizeof(struct stm_connection_manager));
+ char *tmp_ip_addr;
+ uint16_t tmp_port;
+ evhttp_connection_get_peer(evconn, &tmp_ip_addr, &tmp_port);
+ if (tmp_ip_addr)
+ {
+ strncpy(new_conn->peer_ipaddr, tmp_ip_addr, INET6_ADDRSTRLEN - 1);
+ }
+ new_conn->peer_port_host_order = tmp_port;
+ stm->gettime_cb(&new_conn->link_start_time, NULL);
+ new_conn->last_active_time = new_conn->link_start_time;
+ new_conn->conn = evconn;
+ DL_APPEND(stm->connection_mgr, new_conn);
+ return new_conn;
+}
+
+void stm_connection_delete(struct evhttp_connection *evconn)
+{
+ struct stellar_monitor *stm = stellar_monitor_get();
+ struct stm_connection_manager *conn_mgr = stm->connection_mgr;
+ struct stm_connection_manager *ele, *tmp;
+ DL_FOREACH_SAFE(conn_mgr, ele, tmp)
+ {
+ if (ele->conn == evconn)
+ {
+ DL_DELETE(conn_mgr, ele);
+ FREE(ele);
+ }
+ }
+ stm->connection_mgr = conn_mgr;
+}
+
+void stm_connection_update(struct stm_connection_manager *conn_mgr, const struct evhttp_connection *evconn)
+{
+ struct stellar_monitor *stm = stellar_monitor_get();
+ struct stm_connection_manager *ele, *tmp;
+ DL_FOREACH_SAFE(conn_mgr, ele, tmp)
+ {
+ if (ele->conn == evconn)
+ {
+ stm->gettime_cb(&ele->last_active_time, NULL);
+ }
+ }
+}
+
+const struct stm_connection_manager *stm_connection_search(const struct stm_connection_manager *conn_mgr_head, const struct evhttp_connection *evconn)
+{
+ const struct stm_connection_manager *ele, *tmp;
+ DL_FOREACH_SAFE(conn_mgr_head, ele, tmp)
+ {
+ if (ele->conn == evconn)
+ {
+ return ele;
+ }
+ }
+ return NULL;
+}
diff --git a/infra/monitor/monitor_utils.c b/infra/monitor/monitor_utils.c
new file mode 100644
index 0000000..5280feb
--- /dev/null
+++ b/infra/monitor/monitor_utils.c
@@ -0,0 +1,672 @@
+#include "sds/sds.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/in.h>
+#include <threads.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "monitor_private.h"
+#include "stellar/session.h"
+#include "tuple/tuple.h"
+
+ struct stm_key_value *stm_cmd_key_value_new(void)
+ {
+ struct stm_key_value *kv = (struct stm_key_value *)calloc(1, sizeof(struct stm_key_value));
+ kv->tuple_num = 0;
+ kv->tuple = NULL;
+ return kv;
+ }
+
+ void stm_cmd_key_value_append(struct stm_key_value **kv, const char *key, const char *value)
+ {
+ if (NULL == *kv)
+ {
+ *kv = stm_cmd_key_value_new();
+ }
+ struct stm_key_value *new_kv = *kv;
+ new_kv->tuple = (struct stm_key_value_tuple *)realloc(new_kv->tuple, (new_kv->tuple_num + 1) * sizeof(struct stm_key_value_tuple));
+ new_kv->tuple[new_kv->tuple_num].key = strdup(key);
+ new_kv->tuple[new_kv->tuple_num].value = strdup(value);
+ new_kv->tuple_num++;
+ *kv = new_kv;
+ }
+
+ void stm_cmd_key_value_free(struct stm_key_value *kv)
+ {
+ if (NULL == kv)
+ {
+ return;
+ }
+ for (int i = 0; i < kv->tuple_num; i++)
+ {
+ FREE(kv->tuple[i].key);
+ FREE(kv->tuple[i].value);
+ }
+ FREE(kv->tuple);
+ FREE(kv);
+ }
+
+ void stm_cmd_request_free(struct stm_cmd_request *req)
+ {
+ FREE(req);
+ }
+
+ int stm_strncasecmp_exactly(const char *s1, const char *s2, size_t n)
+ {
+ if (NULL == s1 || NULL == s2)
+ {
+ return -1;
+ }
+ size_t len1 = strlen(s1);
+ size_t len2 = strlen(s2);
+
+ if (len1 != len2 || len1 != n)
+ {
+ return -1;
+ }
+ return strncasecmp(s1, s2, n);
+ }
+
+ const char *stm_session_state_ntop(enum session_state state)
+ {
+ switch (state)
+ {
+ case SESSION_STATE_OPENING:
+ return "opening";
+ break;
+ case SESSION_STATE_ACTIVE:
+ return "active";
+ break;
+
+ case SESSION_STATE_CLOSING:
+ return "closing";
+ break;
+
+ default:
+ break;
+ }
+ return "unknown";
+ }
+
+ const char *stm_session_flow_dir_ntop(uint32_t dir)
+ {
+ if (SESSION_SEEN_C2S_FLOW == dir)
+ {
+ return "C2S";
+ }
+ else if (SESSION_SEEN_S2C_FLOW == dir)
+ {
+ return "S2C";
+ }
+ else if ((SESSION_SEEN_C2S_FLOW | SESSION_SEEN_S2C_FLOW) == dir)
+ {
+ return "BIDIRECTION";
+ }
+ return "UNKNOWN";
+ }
+
+ int stm_time_range_pton(const char *time_str, time_t now, time_t time_range[2])
+ {
+ const char *delim = "-";
+ char *save_ptr;
+ const char *time_str_min_example = "last-1-days";
+ if (NULL == time_str || NULL == time_range)
+ {
+ return -1;
+ }
+ if (strlen(time_str) < strlen(time_str_min_example))
+ {
+ return -1;
+ }
+ char *local_time_str = strdup(time_str);
+
+ char *fix_prefix = strtok_r(local_time_str, delim, &save_ptr);
+ if (stm_strncasecmp_exactly(fix_prefix, "last", 4) != 0)
+ {
+ free(local_time_str);
+ return -1;
+ }
+
+ char *number_string = strtok_r(NULL, delim, &save_ptr);
+ if (NULL == number_string)
+ {
+ free(local_time_str);
+ return -1;
+ }
+ long number = strtol(number_string, NULL, 10);
+ if (number <= 0 || number > 3600)
+ {
+ free(local_time_str);
+ return -1;
+ }
+
+ char *unit_string = strtok_r(NULL, delim, &save_ptr);
+ if (NULL == unit_string)
+ {
+ free(local_time_str);
+ return -1;
+ }
+ long long multiple_second = 1;
+ if (stm_strncasecmp_exactly(unit_string, "seconds", 7) == 0)
+ {
+ multiple_second = 1;
+ }
+ else if (stm_strncasecmp_exactly(unit_string, "minutes", 7) == 0)
+ {
+ multiple_second = 60;
+ }
+ else if (stm_strncasecmp_exactly(unit_string, "hours", 5) == 0)
+ {
+ multiple_second = 60 * 60;
+ }
+ else if (stm_strncasecmp_exactly(unit_string, "days", 4) == 0)
+ {
+ multiple_second = 60 * 60 * 24;
+ }
+ else
+ {
+ free(local_time_str);
+ return -1;
+ }
+
+ if ((long long)now < number * multiple_second)
+ {
+ time_range[0] = 0;
+ }
+ else
+ {
+ time_range[0] = now - number * multiple_second;
+ }
+ time_range[1] = now;
+ while (strtok_r(NULL, delim, &save_ptr))
+ {
+ }
+ free(local_time_str);
+ return 0;
+ }
+
+ int stm_time_in_range(time_t t, const time_t time_range[2])
+ {
+ if (NULL == time_range)
+ {
+ return 0;
+ }
+ if (t >= time_range[0] && t <= time_range[1])
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ uint32_t stm_inet_pton(const char *ipstr, void *ipv4_value, void *ipv6_value)
+ {
+ unsigned int tmp_ipv4;
+ struct in6_addr tmp_ipv6;
+ if (NULL == ipstr || NULL == ipv4_value || NULL == ipv6_value)
+ {
+ return 0;
+ }
+ if (inet_pton(AF_INET, ipstr, &tmp_ipv4) == 1)
+ {
+ memcpy(ipv4_value, &tmp_ipv4, sizeof(int));
+ return AF_INET;
+ }
+ if (inet_pton(AF_INET6, ipstr, &tmp_ipv6) == 1)
+ {
+ memcpy(ipv6_value, &tmp_ipv6, sizeof(struct in6_addr));
+ return AF_INET6;
+ }
+ return 0;
+ }
+
+ /**
+ * Calculate a 128-bit mask given a network prefix.
+ */
+ void in6_addr_mask(struct in6_addr *mask, uint8_t bits)
+ {
+ for (uint8_t i = 0; i < sizeof(mask->s6_addr); i++)
+ {
+ mask->s6_addr[i] = bits ? (uint8_t)0xFF << (8 - (bits > 8 ? 8 : bits)) : 0;
+
+ if (bits < 8)
+ bits = 0;
+ else
+ bits -= 8;
+ }
+ }
+
+ /**
+ * Calculate the first address in a network given a mask.
+ */
+ void in6_addr_network(struct in6_addr *network, const struct in6_addr *addr, const struct in6_addr *mask)
+ {
+ for (uint8_t i = 0; i < sizeof(network->s6_addr); i++)
+ {
+ network->s6_addr[i] = addr->s6_addr[i] & mask->s6_addr[i];
+ }
+ }
+
+ /**
+ * Calculate the last address in a network given a mask.
+ */
+ void in6_addr_end(struct in6_addr *end, const struct in6_addr *addr, const struct in6_addr *mask)
+ {
+ for (uint8_t i = 0; i < sizeof(end->s6_addr); i++)
+ {
+ end->s6_addr[i] = (addr->s6_addr[i] & mask->s6_addr[i]) | ~mask->s6_addr[i];
+ }
+ }
+
+ int stm_ipv4_cidr_to_range(uint32_t ipaddr, uint32_t ipmask, uint32_t iprange[2])
+ {
+ uint32_t network_addr = ipaddr & ipmask;
+ uint32_t broadcast_addr = (ipaddr & ipmask) | ~ipmask;
+ iprange[0] = network_addr;
+ iprange[1] = broadcast_addr;
+ return 0;
+ }
+
+ int stm_ipv6_cidr_to_range(const struct in6_addr *ipaddr, const struct in6_addr *ipmask, struct in6_addr iprange[2])
+ {
+ struct in6_addr network_addr = {};
+ struct in6_addr broadcast_addr = {};
+ in6_addr_network(&network_addr, ipaddr, ipmask);
+ in6_addr_end(&broadcast_addr, ipaddr, ipmask);
+ memcpy(&iprange[0], &network_addr, sizeof(struct in6_addr));
+ memcpy(&iprange[1], &broadcast_addr, sizeof(struct in6_addr));
+
+ return 0;
+ }
+
+ uint32_t stm_ip_cidr_pton(const char *ipcidr, void *ipv4_value, void *ipv4_mask, void *ipv6_value, void *ipv6_mask)
+ {
+ int ipver = 0;
+ const char *delim = "/";
+ char *save_ptr;
+
+ if (NULL == ipcidr || NULL == ipv4_value || NULL == ipv4_mask || NULL == ipv6_value || NULL == ipv6_mask)
+ {
+ return 0;
+ }
+
+ if (strchr(ipcidr, '/') == NULL)
+ {
+ return 0;
+ }
+ char *local_ip_cidr = strdup(ipcidr);
+
+ char *ipaddr = strtok_r(local_ip_cidr, delim, &save_ptr);
+ ipver = stm_inet_pton(ipaddr, ipv4_value, ipv6_value);
+ if (ipver != AF_INET && ipver != AF_INET6)
+ {
+ free(local_ip_cidr);
+ return 0;
+ }
+
+ char *cidr = strtok_r(NULL, delim, &save_ptr);
+ if (NULL == cidr)
+ {
+ free(local_ip_cidr);
+ return 0;
+ }
+ int cidr_num = atoi(cidr);
+ if (ipver == AF_INET)
+ {
+ if (cidr_num <= 0 || cidr_num > 32)
+ {
+ free(local_ip_cidr);
+ return 0;
+ }
+ uint32_t mask = 0;
+ for (int i = 0; i < cidr_num; i++)
+ {
+ mask |= (uint32_t)1 << (31 - i);
+ }
+ mask = ntohl(mask);
+ memcpy(ipv4_mask, &mask, sizeof(int));
+ }
+ else if (ipver == AF_INET6)
+ {
+ if (cidr_num <= 0 || cidr_num > 128)
+ {
+ free(local_ip_cidr);
+ return -1;
+ }
+ struct in6_addr mask = {};
+ for (int i = 0; i < cidr_num; i++)
+ {
+ mask.s6_addr[i / 8] |= 1 << (7 - i % 8);
+ }
+ memcpy(ipv6_mask, &mask, sizeof(struct in6_addr));
+ }
+
+ while (strtok_r(NULL, delim, &save_ptr))
+ ;
+
+ free(local_ip_cidr);
+ return ipver;
+ }
+
+ void stm_mem_fill_rand(char *buf, size_t len, unsigned char range_min, unsigned char range_max)
+ {
+ unsigned char *p = (unsigned char *)buf;
+ for (size_t i = 0; i < len; i++)
+ {
+ p[i] = (rand() % range_min + 1) + (range_max - range_min);
+ }
+ }
+
+ int stm_string_isdigit(const char *str)
+ {
+ if (NULL == str || strlen(str) == 0)
+ {
+ return 0;
+ }
+ for (size_t i = 0; i < strlen(str); i++)
+ {
+ if (!isdigit(str[i]))
+ {
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ /*
+ * input("hello, world!", "hello", "hi", &output) -> output = "hi, world!"
+ * return: the number of sub string have been replaced.
+ */
+ int stm_replace_str(const char *raw, const char *search, const char *replace, char **outline)
+ {
+ const char *p = NULL;
+ int search_len = strlen(search);
+ int replace_len = strlen(replace);
+ int raw_len = strlen(raw);
+
+ p = strstr(raw, search);
+ if (p == NULL)
+ {
+ return 0;
+ }
+ const char *remain_ptr = p + search_len;
+ char *new_line = (char *)calloc(1, replace_len + (raw_len - search_len) + 1);
+ strcpy(new_line, replace);
+ strcpy(new_line + replace_len, remain_ptr);
+
+ *outline = new_line;
+ return 1;
+ }
+
+ int stm_timeout(struct timeval start, struct timeval end, int timeout_sec)
+ {
+ return (end.tv_sec * 1000 * 1000 + end.tv_usec) - (start.tv_sec * 1000 * 1000 + start.tv_usec) >= timeout_sec * 1000 * 1000;
+ }
+
+ struct monitor_reply *monitor_reply_nil(void)
+ {
+ struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
+ reply->type = MONITOR_REPLY_NIL;
+ reply->http_code = HTTP_OK;
+ reply->http_reason = "OK";
+ return reply;
+ }
+
+ /* string should without "\r\n", will add "\r\n" in monitor_reply_to_string() */
+ struct monitor_reply *monitor_reply_new_string(const char *format, ...)
+ {
+ struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
+ reply->type = MONITOR_REPLY_STRING;
+ reply->http_code = HTTP_OK;
+ reply->http_reason = "OK";
+ va_list ap;
+ va_start(ap, format);
+ reply->str = sdscatvprintf(sdsempty(), format, ap);
+ reply->len = strlen(reply->str);
+ va_end(ap);
+ return reply;
+ }
+
+ /* string should without "\r\n", will add "\r\n" in monitor_reply_to_string() */
+ struct monitor_reply *monitor_reply_new_error(const char *format, ...)
+ {
+ struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
+ reply->type = MONITOR_REPLY_ERROR;
+ reply->http_code = HTTP_BADREQUEST;
+ reply->http_reason = "ERROR";
+ va_list ap;
+ va_start(ap, format);
+ reply->str = sdscatvprintf(sdsempty(), format, ap);
+ reply->len = strlen(reply->str);
+ va_end(ap);
+ return reply;
+ }
+
+ struct monitor_reply *monitor_reply_new_status(const char *format, ...)
+ {
+ struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
+ reply->type = MONITOR_REPLY_STATUS;
+ reply->http_code = HTTP_OK;
+ reply->http_reason = "OK";
+ va_list ap;
+ va_start(ap, format);
+ reply->str = sdscatvprintf(sdsempty(), format, ap);
+ reply->len = strlen(reply->str);
+ va_end(ap);
+ return reply;
+ }
+
+ struct monitor_reply *monitor_reply_new_integer(long long integer)
+ {
+ struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
+ reply->type = MONITOR_REPLY_INTEGER;
+ reply->http_code = HTTP_OK;
+ reply->http_reason = "OK";
+ reply->integer = integer;
+ return reply;
+ }
+
+ struct monitor_reply *monitor_reply_new_double(double dval)
+ {
+ struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
+ reply->type = MONITOR_REPLY_DOUBLE;
+ reply->http_code = HTTP_OK;
+ reply->http_reason = "OK";
+ reply->dval = dval;
+ return reply;
+ }
+
+ sds monitor_reply_to_string(const struct monitor_reply *reply)
+ {
+ sds res = sdsempty();
+ switch (reply->type)
+ {
+ case MONITOR_REPLY_INTEGER:
+ res = sdscatprintf(res, "(integer) %lld\r\n", reply->integer);
+ break;
+ case MONITOR_REPLY_DOUBLE:
+ res = sdscatprintf(res, "(double) %f\r\n", reply->dval);
+ break;
+ case MONITOR_REPLY_STRING:
+ case MONITOR_REPLY_STATUS:
+ res = sdscatlen(res, reply->str, reply->len);
+ res = sdscat(res, "\r\n");
+ break;
+ case MONITOR_REPLY_NIL:
+ res = sdscat(res, "(nil)\r\n");
+ break;
+ case MONITOR_REPLY_ERROR:
+ res = sdscatprintf(res, "(error) %s\r\n", reply->str);
+ break;
+ default:
+ break;
+ }
+ return res;
+ }
+
+ void monitor_reply_free(struct monitor_reply *reply)
+ {
+ switch (reply->type)
+ {
+ case MONITOR_REPLY_STRING:
+ case MONITOR_REPLY_ERROR:
+ case MONITOR_REPLY_STATUS:
+ sdsfree(reply->str);
+ reply->str = NULL;
+ break;
+ default:
+ break;
+ }
+ free(reply);
+ }
+
+ static struct monitor_cli_args *monitor_util_search_cmd_args(const char *opt_name, const struct monitor_cli_args cli_args[], size_t cli_args_array_size)
+ {
+ if (NULL == cli_args)
+ {
+ return NULL;
+ }
+ for (size_t i = 0; i < cli_args_array_size; i++)
+ {
+ if (stm_strncasecmp_exactly(opt_name, cli_args[i].short_opt, strlen(cli_args[i].short_opt)) == 0 || stm_strncasecmp_exactly(opt_name, cli_args[i].long_opt, strlen(cli_args[i].long_opt)) == 0)
+ {
+ return (struct monitor_cli_args *)&cli_args[i];
+ }
+ }
+ return NULL;
+ }
+
+ sds monitor_util_copy_arg_value(int argc, const char *argv[], int *copy_args_num)
+ {
+ sds res = sdsempty();
+ int num = 0;
+ for (int i = 0; i < argc; i++)
+ {
+ if ((strncmp(argv[i], "-", 1) == 0) || (strncmp(argv[i], "--", 2) == 0))
+ {
+ break;
+ }
+ else
+ {
+ res = sdscat(res, argv[i]);
+ if ((i + 1 < argc) && (strncmp(argv[i + 1], "-", 1) != 0) && (strncmp(argv[i + 1], "--", 2) != 0)) // not the last one
+ {
+ res = sdscat(res, " ");
+ }
+ num++;
+ }
+ }
+ *copy_args_num = num;
+ return res;
+ }
+
+ int monitor_util_parse_cmd_args(int argc, const char *argv[], struct monitor_cli_args cli_args[], size_t cli_args_array_size)
+ {
+ if (argc <= 0 || NULL == argv || NULL == cli_args)
+ {
+ return -1;
+ }
+ int parse_stage = 0; // 0: arg; 1: value
+ struct monitor_cli_args *one_arg = NULL;
+ for (int i = 1; i < argc;) // skip program self name
+ {
+ if (0 == parse_stage)
+ {
+ one_arg = monitor_util_search_cmd_args(argv[i], cli_args, cli_args_array_size);
+ if (NULL == one_arg)
+ {
+ fprintf(stderr, "unknown option: %s\n", argv[i]);
+ return -1;
+ }
+ if (one_arg->require_arg_value && (i + 1 >= argc))
+ {
+ fprintf(stderr, "option requires value: %s\n", argv[i]);
+ return -1;
+ }
+ parse_stage = 1;
+ i += 1;
+ }
+ else
+ {
+ if (NULL == one_arg)
+ {
+ fprintf(stderr, "unknown option: %s\n", argv[i]);
+ return -1;
+ }
+ int copy_args_num = 0;
+ if (one_arg->value_is_multi_words)
+ {
+ one_arg->value = monitor_util_copy_arg_value(argc - i, &argv[i], &copy_args_num);
+ }
+ else
+ {
+ one_arg->value = sdsnew(argv[i]);
+ copy_args_num = 1;
+ }
+ i += copy_args_num;
+ parse_stage = 0;
+ one_arg = NULL;
+ }
+ }
+ return 0;
+ }
+
+ sds stm_config_print(const struct stellar_monitor_config *config)
+ {
+ sds res = sdsempty();
+ res = sdscatprintf(res, "cli_request_timeout: %ds, ", config->cli_request_timeout);
+ res = sdscatprintf(res, "connection_idle_timeout: %ds, ", config->connection_idle_timeout);
+ res = sdscatprintf(res, "listen_ip_addr: %s, ", config->listen_ipaddr);
+ res = sdscatprintf(res, "listen_port %u, ", config->listen_port_host_order);
+ res = sdscatprintf(res, "data_link_bind_port: %u, ", config->data_link_bind_port_host_order);
+ res = sdscatprintf(res, "stat_output_path: %s, ", config->output_path);
+ res = sdscatprintf(res, "stat_output_interval_ms: %dms ", config->output_interval_ms);
+ return res;
+ }
+
+ char *stm_http_url_encode(const char *originalText)
+ {
+ // allocate memory for the worst possible case (all characters need to be encoded)
+ char *encodedText = (char *)malloc(sizeof(char) * strlen(originalText) * 3 + 1);
+ const char *hex = "0123456789abcdef";
+ int pos = 0;
+ for (size_t i = 0; i < strlen(originalText); i++)
+ {
+ if (('a' <= originalText[i] && originalText[i] <= 'z') || ('A' <= originalText[i] && originalText[i] <= 'Z') || ('0' <= originalText[i] && originalText[i] <= '9'))
+ {
+ encodedText[pos++] = originalText[i];
+ }
+ else if (originalText[i] == ' ')
+ {
+ encodedText[pos++] = '%';
+ encodedText[pos++] = hex[originalText[i] >> 4];
+ encodedText[pos++] = hex[originalText[i] & 15];
+ }
+ /* todo: other characters ? */
+ else
+ {
+ encodedText[pos++] = originalText[i];
+ }
+ }
+ encodedText[pos] = '\0';
+ return encodedText;
+ }
+
+ const char *stm_get0_readable_session_addr(const struct tuple6 *addr, char *addr_buf, size_t max_buf_len)
+ {
+ tuple4_to_str((struct tuple4 *)addr, addr_buf, max_buf_len);
+ return addr_buf;
+ }
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/infra/monitor/monitor_utils.h b/infra/monitor/monitor_utils.h
new file mode 100644
index 0000000..2ccecd2
--- /dev/null
+++ b/infra/monitor/monitor_utils.h
@@ -0,0 +1,30 @@
+#pragma once
+#include <stdint.h>
+#include <stddef.h>
+#include <time.h>
+#include <netinet/in.h>
+#include "stellar/session.h"
+#include "tuple/tuple.h"
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ int stm_replace_str(const char *raw, const char *search, const char *replace, char **outline);
+ int stm_strncasecmp_exactly(const char *s1, const char *s2, size_t n);
+ struct stm_cmd_reply *stm_build_reply_error(const char *format, ...);
+ struct stm_cmd_reply *stm_cmd_reply_dup(const struct stm_cmd_reply *src);
+ const char *stm_session_flow_dir_ntop(uint32_t dir);
+ int stm_time_range_pton(const char *time_str, time_t now, time_t time_range[2]);
+ int stm_time_in_range(time_t t, const time_t time_range[2]);
+ uint32_t stm_inet_pton(const char *ipstr, void *ipv4_value, void *ipv6_value);
+ uint32_t stm_ip_cidr_pton(const char *ipcidr, void *ipv4_value, void *ipv4_mask, void *ipv6_value, void *ipv6_mask);
+ int stm_ipv4_cidr_to_range(uint32_t ipaddr, uint32_t ipmask, uint32_t iprange[2]);
+ int stm_ipv6_cidr_to_range(const struct in6_addr *ipaddr, const struct in6_addr *ipmask, struct in6_addr iprange[2]);
+ void stm_mem_fill_rand(char *buf, size_t len, unsigned char range_min, unsigned char range_max);
+ int stm_string_isdigit(const char *str);
+ int stm_timeout(struct timeval start, struct timeval end, int timeout_sec);
+ const char *stm_session_state_ntop(enum session_state state);
+ const char *stm_get0_readable_session_addr(const struct tuple6 *addr, char *addr_buf, size_t max_buf_len);
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/infra/monitor/version.map b/infra/monitor/version.map
new file mode 100644
index 0000000..5958f4c
--- /dev/null
+++ b/infra/monitor/version.map
@@ -0,0 +1,10 @@
+VERS_3.0{
+global:
+ extern "C" {
+ stellar_monitor_*;
+ monitor_*;
+ stm_*;
+ stellar_module_get_monitor;
+ };
+ local: *;
+};
diff --git a/infra/packet_manager/packet_internal.h b/infra/packet_manager/packet_internal.h
index 1dbfab2..d9b3fe9 100644
--- a/infra/packet_manager/packet_internal.h
+++ b/infra/packet_manager/packet_internal.h
@@ -8,7 +8,7 @@ extern "C"
#include <stdbool.h>
#include <sys/queue.h>
-#include "tuple.h"
+#include "tuple/tuple.h"
#include "stellar/packet.h"
#define PACKET_MAX_LAYERS 32
diff --git a/infra/session_manager/session_manager.c b/infra/session_manager/session_manager.c
index 21fb779..3691805 100644
--- a/infra/session_manager/session_manager.c
+++ b/infra/session_manager/session_manager.c
@@ -593,5 +593,15 @@ void session_manager_on_thread_exit(struct module_manager *mod_mgr, int thread_i
{
struct session_manager *sess_mgr = module_get_ctx(mod);
assert(sess_mgr);
+ assert(thread_id < (int)sess_mgr->cfg->thread_num);
+
session_manager_clean(sess_mgr, thread_id);
+}
+
+// temp add for show session command
+struct session_manager_rte *session_manager_get_runtime(struct session_manager *sess_mgr, uint16_t thread_id)
+{
+ assert(sess_mgr);
+ assert(thread_id < sess_mgr->cfg->thread_num);
+ return sess_mgr->rte[thread_id];
} \ No newline at end of file
diff --git a/infra/session_manager/session_manager_rte.h b/infra/session_manager/session_manager_rte.h
index de8c09c..f04dceb 100644
--- a/infra/session_manager/session_manager_rte.h
+++ b/infra/session_manager/session_manager_rte.h
@@ -7,6 +7,7 @@ extern "C"
#include "tuple.h"
#include "stellar/session.h"
+#include "session_manager_cfg.h"
enum session_scan_flags
{
diff --git a/infra/version.map b/infra/version.map
index c66ef63..616f18b 100644
--- a/infra/version.map
+++ b/infra/version.map
@@ -74,5 +74,7 @@ global:
lpi_plus_exit;
lpi_plus_appid_subscribe;
+ monitor_on_init;
+ monitor_on_exit;
local: *;
};
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index a402bf4..11d1abc 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -5,4 +5,5 @@ add_subdirectory(lpi_plus)
#add_subdirectory(decoders/http)
#add_subdirectory(decoders/socks)
#add_subdirectory(decoders/stratum)
-#add_subdirectory(decoders/session_flags) \ No newline at end of file
+#add_subdirectory(decoders/session_flags)
+add_subdirectory(monitor) \ No newline at end of file
diff --git a/test/decoders/http/http_gtest_llhttp.cpp b/test/decoders/http/http_gtest_llhttp.cpp
new file mode 100644
index 0000000..58ff281
--- /dev/null
+++ b/test/decoders/http/http_gtest_llhttp.cpp
@@ -0,0 +1,507 @@
+
+#include <cstdint>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <gtest/gtest.h>
+#include "stellar/utils.h"
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <llhttp.h>
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef UNUSED
+#define UNUSED __attribute__((unused))
+#endif
+
+struct gtest_http_counter
+{
+ uint8_t flag_on_message_begin;
+ uint8_t flag_on_url;
+ uint8_t flag_on_method;
+ uint8_t flag_on_status;
+ uint8_t flag_on_version;
+ uint8_t flag_on_header_field;
+ uint8_t flag_on_header_field_complete;
+ uint8_t flag_on_header_value;
+ uint8_t flag_on_header_value_complete;
+ uint8_t flag_on_headers_complete;
+ uint8_t flag_on_body;
+ uint8_t flag_on_message_complete;
+};
+
+struct gtest_http_parm
+{
+ llhttp_t llhttp_parser;
+ llhttp_settings_t settings;
+ struct gtest_http_counter count;
+};
+
+static int on_message_begin(llhttp_t *parser)
+{
+ printf("Message begin cb\n");
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_message_begin++;
+ return 0;
+}
+
+static int on_url(llhttp_t *parser, const char *at, size_t length)
+{
+ printf("URI cb: %.*s\n", (int)length, at);
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_url++;
+ return 0;
+}
+
+static int on_method(llhttp_t *parser, const char *at, size_t length)
+{
+ printf("method cb: %.*s\n", (int)length, at);
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_method++;
+ return 0;
+}
+
+static int on_status(llhttp_t *parser, const char *at, size_t length)
+{
+ printf("status cb: %.*s\n", (int)length, at);
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_status++;
+ return 0;
+}
+
+static int on_version(llhttp_t *parser, const char *at, size_t length)
+{
+ printf("version cb: %.*s\n", (int)length, at);
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_version++;
+ return 0;
+}
+
+static int on_header_field(llhttp_t *parser, const char *at, size_t length)
+{
+ printf("Header field cb: %.*s\n", (int)length, at);
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_header_field++;
+ return 0;
+}
+
+static int on_header_field_complete(llhttp_t *parser)
+{
+ printf("Header field complete cb\n");
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_header_field_complete++;
+ return 0;
+}
+
+static int on_header_value(llhttp_t *parser, const char *at, size_t length)
+{
+ printf("Header value cb: %.*s\n", (int)length, at);
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_header_value++;
+ return 0;
+}
+
+static int on_header_value_complete(llhttp_t *parser)
+{
+ printf("Header value complete cb\n");
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_header_value_complete++;
+ return 0;
+}
+
+static int on_headers_complete(llhttp_t *parser)
+{
+ printf("All Headers complete cb\n");
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_headers_complete++;
+ return 0;
+}
+
+static int on_message_complete(llhttp_t *parser)
+{
+ printf("Message complete cb\n");
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_message_complete++;
+ return 0;
+}
+
+static int on_body(llhttp_t *parser, const char *at, size_t length)
+{
+ printf("on_body cb: %.*s\n", (int)length, at);
+ struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
+ para->count.flag_on_body++;
+ return 0;
+}
+
+/******************************** request test case *********************************/
+
+static int gtest_llhttp_init(llhttp_t *parser, llhttp_type_t type, llhttp_settings_t *settings)
+{
+ llhttp_settings_init(settings);
+ settings->on_message_begin = on_message_begin;
+ settings->on_url = on_url;
+ settings->on_version = on_version;
+ settings->on_status = on_status;
+ settings->on_method = on_method;
+ settings->on_header_field = on_header_field;
+ settings->on_header_field_complete = on_header_field_complete;
+ settings->on_header_value = on_header_value;
+ settings->on_header_value_complete = on_header_value_complete;
+ settings->on_headers_complete = on_headers_complete;
+ settings->on_message_complete = on_message_complete;
+ settings->on_body = on_body;
+
+ llhttp_init(parser, type, settings);
+ return 0;
+}
+
+TEST(HTTP_llhttp, request_base)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_url, 1);
+ ASSERT_EQ(para.count.flag_on_method, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 1);
+}
+
+TEST(HTTP_llhttp, request_dir_error)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_TRUE(lerr != HPE_OK);
+}
+
+TEST(HTTP_llhttp, request_uncompleted)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length:";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_url, 1);
+ ASSERT_EQ(para.count.flag_on_method, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 0);
+}
+
+TEST(HTTP_llhttp, request_hdr_pipeline)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example1.com\r\nContent-Length: 0\r\n\r\nGET /path/index.html HTTP/1.1\r\nHost: example2.com\r\nContent-Length: 0\r\n\r\nGET /path/index.html HTTP/1.1\r\nHost: example3.com\r\nContent-Length: 0\r\n\r\n";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 3);
+ ASSERT_EQ(para.count.flag_on_url, 3);
+ ASSERT_EQ(para.count.flag_on_method, 3);
+ ASSERT_EQ(para.count.flag_on_version, 3);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 3);
+ ASSERT_EQ(para.count.flag_on_message_complete, 3);
+}
+
+TEST(HTTP_llhttp, request_hdr_body_pipeline)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example1.com\r\nContent-Length: 1\r\n\r\nxGET /path/index.html HTTP/1.1\r\nHost: example2.com\r\nContent-Length: 2\r\n\r\nxxGET /path/index.html HTTP/1.1\r\nHost: example3.com\r\nContent-Length: 3\r\n\r\nxxx";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 3);
+ ASSERT_EQ(para.count.flag_on_url, 3);
+ ASSERT_EQ(para.count.flag_on_method, 3);
+ ASSERT_EQ(para.count.flag_on_version, 3);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 3);
+ ASSERT_EQ(para.count.flag_on_body, 3);
+ ASSERT_EQ(para.count.flag_on_message_complete, 3);
+}
+
+TEST(HTTP_llhttp, request_body_chunked)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_url, 1);
+ ASSERT_EQ(para.count.flag_on_method, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_body, 2); // 2 chunks
+ ASSERT_EQ(para.count.flag_on_message_complete, 1);
+}
+
+TEST(HTTP_llhttp, v11_GET_no_content_length_hdr_with_body)
+{
+ /*
+ * v1.1
+ * if no obvious content-length header, the body will be parsed as new header,
+ * and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error!
+ */
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n\r\n<html>some content<html/>";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_INVALID_METHOD);
+ ASSERT_EQ(para.count.flag_on_message_begin, 2);
+ ASSERT_EQ(para.count.flag_on_url, 1);
+ ASSERT_EQ(para.count.flag_on_method, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_body, 0);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 1);
+}
+
+TEST(HTTP_llhttp, v10_GET_no_content_length_hdr_with_body)
+{
+ /* v1.0
+ * if no obvious content-length header, the body will be not parsed!
+ * then raise a HPE_CLOSED_CONNECTION error!
+ */
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "GET /path/index.html HTTP/1.0\r\nHost: example.com\r\n\r\n\r\n<html>some content<html/>";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_CLOSED_CONNECTION);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_url, 1);
+ ASSERT_EQ(para.count.flag_on_method, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_body, 0);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 1);
+}
+
+TEST(HTTP_llhttp, v11_POST_no_content_length_hdr_with_body)
+{
+ /*
+ * v1.1
+ * if no obvious content-length header, the body will be parsed as new header,
+ * and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error!
+ */
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "POST /path/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n\r\n<html>some content<html/>";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_INVALID_METHOD);
+ ASSERT_EQ(para.count.flag_on_message_begin, 2);
+ ASSERT_EQ(para.count.flag_on_url, 1);
+ ASSERT_EQ(para.count.flag_on_method, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_body, 0);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 1);
+}
+
+TEST(HTTP_llhttp, v11_POST_no_len_has_type_with_body)
+{
+ /*
+ * v1.1 POST
+ * if no obvious content-length header, the body will be parsed as new header,
+ * and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error!
+ */
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "POST /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-type: text/html\r\n\r\n<html>some content<html/>";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_INVALID_METHOD);
+ ASSERT_EQ(para.count.flag_on_message_begin, 2);
+ ASSERT_EQ(para.count.flag_on_url, 1);
+ ASSERT_EQ(para.count.flag_on_method, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_body, 0);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 1);
+}
+
+TEST(HTTP_llhttp, v10_POST_no_len_has_type_with_body)
+{
+ /*
+ * v1.0 POST
+ * if no obvious content-length header, the body will be parsed as new header,
+ * and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error!
+ */
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *request = "POST /path/index.html HTTP/1.0\r\nHost: example.com\r\nContent-type: text/html\r\n\r\n<html>some content<html/>";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, request, strlen(request));
+ ASSERT_EQ(lerr, HPE_CLOSED_CONNECTION);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_url, 1);
+ ASSERT_EQ(para.count.flag_on_method, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_body, 0);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 1);
+}
+
+/******************************** response test case *********************************/
+TEST(HTTP_llhttp, response_base)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nhello,world";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_status, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 1);
+ ASSERT_EQ(para.count.flag_on_body, 1);
+}
+
+TEST(HTTP_llhttp, response_dir_error)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nhello,world";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_TRUE(lerr != HPE_OK);
+}
+
+TEST(HTTP_llhttp, response_uncompleted)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nxx";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_status, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_body, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 0);
+}
+
+TEST(HTTP_llhttp, response_hdr_pipeline)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\n";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 3);
+ ASSERT_EQ(para.count.flag_on_status, 3);
+ ASSERT_EQ(para.count.flag_on_version, 3);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 3);
+ ASSERT_EQ(para.count.flag_on_body, 0);
+ ASSERT_EQ(para.count.flag_on_message_complete, 3);
+}
+
+TEST(HTTP_llhttp, response_hdr_body_pipeline)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 1\r\n\r\nxHTTP/1.1 200 OK\r\nServer: nginx2\r\nContent-Type: text/html\r\nContent-Length: 2\r\n\r\nxxHTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 3\r\n\r\nxxx";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 3);
+ ASSERT_EQ(para.count.flag_on_status, 3);
+ ASSERT_EQ(para.count.flag_on_version, 3);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 3);
+ ASSERT_EQ(para.count.flag_on_body, 3);
+ ASSERT_EQ(para.count.flag_on_message_complete, 3);
+}
+
+TEST(HTTP_llhttp, response_no_len_no_type_body)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\n\r\nxxx";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_status, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_body, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed
+}
+
+TEST(HTTP_llhttp, response_no_len_has_type_body)
+{
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\n\r\nxxx";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_status, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_body, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed
+}
+
+TEST(HTTP_llhttp, response_no_len_no_type_pipeline)
+{
+ /*
+ * if no obvious content-length header, the body will be parsed until connection cloesd!
+ *
+ */
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\n\r\n";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_status, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_body, 1);
+ ASSERT_EQ(para.count.flag_on_message_complete, 0);
+}
+
+TEST(HTTP_llhttp, response_no_len_no_type_and_reset)
+{
+ /*
+ * if no obvious content-length header, the body will be parsed until connection cloesd!
+ *
+ */
+ struct gtest_http_parm para = {};
+ gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
+ const char *response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n";
+ enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+
+ lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+
+ lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+
+ llhttp_reset(&para.llhttp_parser);
+ memset(&para.count, 0, sizeof(para.count));
+ response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n";
+
+ lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
+ ASSERT_EQ(lerr, HPE_OK);
+
+ ASSERT_EQ(para.count.flag_on_message_begin, 1);
+ ASSERT_EQ(para.count.flag_on_status, 1);
+ ASSERT_EQ(para.count.flag_on_version, 1);
+ ASSERT_EQ(para.count.flag_on_headers_complete, 1);
+ ASSERT_EQ(para.count.flag_on_body, 0);
+ ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed if no content-length filed
+}
+
+int main(int argc, char const *argv[])
+{
+ ::testing::InitGoogleTest(&argc, (char **)argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/monitor/CMakeLists.txt b/test/monitor/CMakeLists.txt
new file mode 100644
index 0000000..5f447da
--- /dev/null
+++ b/test/monitor/CMakeLists.txt
@@ -0,0 +1,78 @@
+cmake_minimum_required (VERSION 2.8...3.10)
+
+include_directories(${CMAKE_SOURCE_DIR}/include/)
+include_directories(${CMAKE_SOURCE_DIR}/infra/)
+include_directories(${CMAKE_SOURCE_DIR}/deps/)
+
+set(MONITOR_SRC_DIR ${CMAKE_SOURCE_DIR}/infra/monitor)
+# set(MONITOR_DEPEND_LIB gtest pcap sds linenoise pthread toml libevent-static cjson-static fieldstat4 stellar_lib monitor)
+set(MONITOR_DEPEND_LIB gtest pcap pthread fieldstat4 )
+set(WHOLE_ARCHIVE_STATIC_LIB sds linenoise toml libevent-static cjson-static stellar_lib monitor session_manager tuple)
+
+add_executable(gtest_stm_sds gtest_sds.cpp)
+add_executable(gtest_stm_util gtest_monitor_util.cpp )
+# add_executable(gtest_stm_packet_dump_unit gtest_packet_dump_unit.cpp)
+# add_executable(gtest_stm_packet_dump gtest_packet_dump.cpp)
+add_executable(gtest_cmd_assistant gtest_cmd_assistant.cpp )
+add_executable(gtest_stm_spinlock gtest_spinlock.cpp )
+# add_executable(gtest_seek_layer gtest_seek_layer.cpp)
+add_executable(gtest_stm_server gtest_monitor_server.cpp)
+add_executable(gtest_stm_topk gtest_topk.cpp)
+add_executable(stm_server monitor_main.cpp)
+add_executable(gtest_stm_rpc gtest_rpc.cpp)
+set(MONITOR_TEST_FILE gtest_stm_sds gtest_stm_util gtest_cmd_assistant gtest_stm_spinlock gtest_stm_server gtest_stm_topk stm_server gtest_stm_rpc)
+foreach(tfile ${MONITOR_TEST_FILE})
+ target_link_libraries(${tfile} ${MONITOR_DEPEND_LIB} -Wl,--whole-archive ${WHOLE_ARCHIVE_STATIC_LIB} -Wl,--no-whole-archive)
+endforeach()
+
+set(MONITOR_TEST_RUN_DIR ${CMAKE_CURRENT_BINARY_DIR})
+add_test(NAME MONITOR_ENV_SETUP COMMAND sh -c "mkdir -p ${MONITOR_TEST_RUN_DIR}/conf &&
+ mkdir -p ${MONITOR_TEST_RUN_DIR}/plugin &&
+ mkdir -p ${MONITOR_TEST_RUN_DIR}/log &&
+ mkdir -p ${MONITOR_TEST_RUN_DIR}/pcap &&
+ cp ${CMAKE_SOURCE_DIR}/conf/stellar.toml ${MONITOR_TEST_RUN_DIR}/conf/stellar.toml &&
+ tomlq -t -i '.packet_io.mode=\"pcapfile\"' ${MONITOR_TEST_RUN_DIR}/conf/stellar.toml &&
+ tomlq -t -i '.packet_io.pcap_path=\"pcap/test.pcap\"' ${MONITOR_TEST_RUN_DIR}/conf/stellar.toml &&
+ rm -f ${MONITOR_TEST_RUN_DIR}/conf/log.toml &&
+ echo '[log]' >> ${MONITOR_TEST_RUN_DIR}/conf/log.toml &&
+ echo 'output = \"file\"' >> ${MONITOR_TEST_RUN_DIR}/conf/log.toml &&
+ echo 'level = \"DEBUG\"' >> ${MONITOR_TEST_RUN_DIR}/conf/log.toml &&
+ rm -f ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
+ cp ${CMAKE_BINARY_DIR}/tools/monitor/stellar-cli ${MONITOR_TEST_RUN_DIR}/stellar-cli &&
+ cp ${CMAKE_BINARY_DIR}/tools/monitor/tcpdump/src/tcpdump-build/stellar-dump ${MONITOR_TEST_RUN_DIR}/stellar-dump")
+
+add_test(NAME MONITOR_COPY_PCAP COMMAND sh -c "rm -f ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
+ cp ${CMAKE_SOURCE_DIR}/test/monitor/pcap/monitor_benchmark.pcap ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
+ md5sum ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap |grep 79d4c8e526f638b59b12b7defb5021d9")
+
+add_test(NAME MONITOR_TEST_SERVER COMMAND sh -c "./gtest_stm_server" WORKING_DIRECTORY ${GMONITOR_TEST_RUN_DIR})
+set_tests_properties(MONITOR_ENV_SETUP
+ PROPERTIES FIXTURES_SETUP STM_ENV_SETUP)
+set_tests_properties(MONITOR_TEST_SERVER
+ PROPERTIES FIXTURES_REQUIRED STM_ENV_SETUP)
+
+# add_test(NAME MONITOR_PKT_DUMP_COPY_PCAP COMMAND sh -c "rm -f ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
+# cp ${CMAKE_SOURCE_DIR}/test/monitor/pcap/monitor_packet_dump.pcap ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
+# md5sum ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap |grep eb7795612a389d71d0f82fef5737981d")
+# set_tests_properties(MONITOR_ENV_SETUP MONITOR_PKT_DUMP_COPY_PCAP
+# PROPERTIES FIXTURES_SETUP STM_GTEST_PKT_DUMP)
+# add_test(NAME MONITOR_TEST_PKT_DUMP COMMAND sh -c "./gtest_stm_packet_dump" WORKING_DIRECTORY ${MONITOR_TEST_RUN_DIR})
+# set_tests_properties(MONITOR_TEST_PKT_DUMP
+# PROPERTIES FIXTURES_REQUIRED STM_GTEST_PKT_DUMP)
+
+# add_test(NAME MONITOR_PKT_DUMP_TUNNEL_COPY_PCAP COMMAND sh -c "rm -f ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
+# cp ${CMAKE_SOURCE_DIR}/test/monitor/pcap/monitor_tunnel.pcap ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
+# md5sum ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap |grep 525dab2a04d02b36d7d21be16f51e1cd")
+# add_executable(gtest_stm_packet_dump_tunnel gtest_packet_dump_tunnel.cpp ${STM_DEP_SRC} )
+# target_link_libraries(gtest_stm_packet_dump_tunnel ${MONITOR_DEPEND_LIB} ${WHOLE_ARCHIVE_STATIC_LIB} )
+
+# set_tests_properties(MONITOR_ENV_SETUP MONITOR_PKT_DUMP_TUNNEL_COPY_PCAP
+# PROPERTIES FIXTURES_SETUP STM_GTEST_PKT_DUMP_TUNNEL)
+# add_test(NAME MONITOR_TEST_PKT_DUMP_TUNNEL COMMAND sh -c "./gtest_stm_packet_dump_tunnel" WORKING_DIRECTORY ${MONITOR_TEST_RUN_DIR})
+# set_tests_properties(MONITOR_TEST_PKT_DUMP_TUNNEL
+# PROPERTIES FIXTURES_REQUIRED STM_GTEST_PKT_DUMP_TUNNEL)
+
+include(GoogleTest)
+gtest_discover_tests(gtest_stm_util)
+gtest_discover_tests(gtest_cmd_assistant)
+# gtest_discover_tests(gtest_seek_layer) \ No newline at end of file
diff --git a/test/monitor/gtest_cmd_assistant.cpp b/test/monitor/gtest_cmd_assistant.cpp
new file mode 100644
index 0000000..e5247e1
--- /dev/null
+++ b/test/monitor/gtest_cmd_assistant.cpp
@@ -0,0 +1,252 @@
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <gtest/gtest.h>
+#include "monitor/monitor_cmd_assistant.h"
+#include "sds/sds.h"
+#include "linenoise/linenoise.h"
+
+static struct monitor_reply *test_cmd_cb(int argc, char *argv[], void *user_arg)
+{
+ (void)argc;
+ (void)argv;
+ (void)user_arg;
+ return NULL;
+}
+
+TEST(MONITOR_CMD_ASSISTANT, sds_compare)
+{
+ int cli_argc, register_cmd_argc;
+ const char *cli = "show";
+ const char *register_cmd = "show";
+ sds *cli_array = sdssplitargs(cli, &cli_argc);
+ sds *register_cmd_array = sdssplitargs(register_cmd, &register_cmd_argc);
+ EXPECT_EQ(0, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
+ sdsfreesplitres(cli_array, cli_argc);
+ sdsfreesplitres(register_cmd_array, register_cmd_argc);
+
+ cli = "s";
+ register_cmd = "show";
+ cli_array = sdssplitargs(cli, &cli_argc);
+ register_cmd_array = sdssplitargs(register_cmd, &register_cmd_argc);
+ EXPECT_EQ(-1, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
+ sdsfreesplitres(cli_array, cli_argc);
+ sdsfreesplitres(register_cmd_array, register_cmd_argc);
+
+ cli = "show";
+ register_cmd = "show session";
+ cli_array = sdssplitargs(cli, &cli_argc);
+ register_cmd_array = sdssplitargs(register_cmd, &register_cmd_argc);
+ EXPECT_EQ(-1, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
+ sdsfreesplitres(cli_array, cli_argc);
+ sdsfreesplitres(register_cmd_array, register_cmd_argc);
+
+ cli = "show s";
+ register_cmd = "show session";
+ cli_array = sdssplitargs(cli, &cli_argc);
+ register_cmd_array = sdssplitargs(register_cmd, &register_cmd_argc);
+ EXPECT_EQ(-1, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
+ sdsfreesplitres(cli_array, cli_argc);
+ sdsfreesplitres(register_cmd_array, register_cmd_argc);
+
+ cli = "show session xxx yyy zzz";
+ register_cmd = "show session";
+ cli_array = sdssplitargs(cli, &cli_argc);
+ register_cmd_array = sdssplitargs(register_cmd, &register_cmd_argc);
+ EXPECT_EQ(1, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
+ sdsfreesplitres(cli_array, cli_argc);
+ sdsfreesplitres(register_cmd_array, register_cmd_argc);
+}
+
+TEST(MONITOR_CMD_ASSISTANT, cmd_register)
+{
+ struct stm_cmd_assistant *cmd_aide = stm_cmd_assistant_new();
+ stm_cmd_assistant_register_cmd(cmd_aide, "get", (void *)"test_cmd_cb", (void *)"get args", "readonly", "key", "get xxx");
+ EXPECT_EQ((void *)"test_cmd_cb", stm_cmd_assistant_get_cb(cmd_aide, "get"));
+ EXPECT_EQ((void *)"get args", stm_cmd_assistant_get_user_arg(cmd_aide, "get"));
+
+ stm_cmd_assistant_register_cmd(cmd_aide, "show session", (void *)"show_session_cb", (void *)"show session args", "readonly", "id", "show session xxx");
+ EXPECT_EQ((void *)"show_session_cb", stm_cmd_assistant_get_cb(cmd_aide, "show session id 1"));
+ EXPECT_EQ((void *)"show session args", stm_cmd_assistant_get_user_arg(cmd_aide, "show session id 3"));
+
+ stm_cmd_assistant_register_cmd(cmd_aide, "show http", (void *)"show_http_cb", (void *)"show http args", "readonly", "id", "show http xxx");
+ EXPECT_EQ((void *)"show_http_cb", stm_cmd_assistant_get_cb(cmd_aide, "show http url a.b.com"));
+ EXPECT_EQ((void *)"show http args", stm_cmd_assistant_get_user_arg(cmd_aide, "show http url a.b.com"));
+
+ stm_cmd_assistant_register_cmd(cmd_aide, "show http stat", (void *)"show_http_stat_cb", (void *)"show http stat args", "readonly", "id", "show http stat xxx");
+ EXPECT_EQ((void *)"show_http_stat_cb", stm_cmd_assistant_get_cb(cmd_aide, "show http stat brief"));
+ EXPECT_EQ((void *)"show http stat args", stm_cmd_assistant_get_user_arg(cmd_aide, "show http stat brief"));
+
+ stm_cmd_assistant_register_cmd(cmd_aide, "show http stat brief limit", (void *)"show_http_stat_limit_cb", (void *)"show http stat limit args", "readonly", "id", "show http stat limit xxx");
+ EXPECT_EQ((void *)"show_http_stat_limit_cb", stm_cmd_assistant_get_cb(cmd_aide, "show http stat brief limit 1"));
+ EXPECT_EQ((void *)"show http stat limit args", stm_cmd_assistant_get_user_arg(cmd_aide, "show http stat brief limit 1"));
+
+ stm_cmd_assistant_free(cmd_aide);
+}
+
+TEST(MONITOR_CMD_ASSISTANT, serialize_dserialize)
+{
+ struct stm_cmd_assistant *cmd_aide = stm_cmd_assistant_new();
+ stm_cmd_assistant_register_cmd(cmd_aide, "get", (void *)test_cmd_cb, NULL, "readonly", "key", "get xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "set", (void *)test_cmd_cb, NULL, "write", "key value [NX] [EX seconds | PX milliseconds]", "set xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show session id", (void *)test_cmd_cb, NULL, "readonly", "value", "show session id xxx");
+
+ char *free_after_used = stm_cmd_assistant_serialize(cmd_aide);
+ printf("serialize : \n%s\n", free_after_used);
+
+ struct stm_cmd_assistant *cmd_aide_new = stm_cmd_assistant_new();
+ stm_cmd_assistant_dserialize(cmd_aide_new, free_after_used);
+ char *free_after_used2 = stm_cmd_assistant_serialize(cmd_aide_new);
+ printf("serialize : \n%s\n", free_after_used2);
+ ASSERT_STREQ(free_after_used, free_after_used2);
+
+ free(free_after_used);
+ free(free_after_used2);
+ stm_cmd_assistant_free(cmd_aide);
+ stm_cmd_assistant_free(cmd_aide_new);
+}
+
+struct gtest_cmd_completion_stat
+{
+ linenoiseCompletions *lc;
+ int expect_completion_num;
+ int match_num;
+ int not_match_num;
+ const char *expect_completion_str[128];
+};
+struct gtest_cmd_completion_stat g_expect_stat = {};
+
+static void gtest_cmd_completion_cb(void *arg, const char *candidate_completion)
+{
+ struct gtest_cmd_completion_stat *local_expect_stat = (struct gtest_cmd_completion_stat *)arg;
+ for (int i = 0; i < local_expect_stat->expect_completion_num; i++)
+ {
+ if (0 == strcasecmp(local_expect_stat->expect_completion_str[i], candidate_completion))
+ {
+ // printf("gtest completion cb match : %s\n", candidate_completion);
+ local_expect_stat->match_num++;
+ return;
+ }
+ }
+ local_expect_stat->not_match_num++;
+}
+
+void gtest_linenoise_completion_cb(const char *line, linenoiseCompletions *lc)
+{
+ g_expect_stat.lc = lc;
+ stm_cmd_assistant_input_line(stm_cmd_assistant_get(), line, (void *)&g_expect_stat);
+}
+
+TEST(MONITOR_CMD_ASSISTANT, cmd_completion)
+{
+ struct stm_cmd_assistant *cmd_aide = stm_cmd_assistant_new();
+ stm_cmd_assistant_register_cmd(cmd_aide, "get", (void *)test_cmd_cb, NULL, "readonly", "key", "get xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "set", (void *)test_cmd_cb, NULL, "write", "key value [NX] [EX seconds | PX milliseconds]", "set xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show session", (void *)test_cmd_cb, NULL, "readonly", "value", "show session xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show session id", (void *)test_cmd_cb, NULL, "readonly", "value", "show session id xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show http", (void *)test_cmd_cb, NULL, "readonly", "status ", "show http xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show ssl", (void *)test_cmd_cb, NULL, "readonly", "status", "show ssl xxx");
+
+ g_expect_stat.expect_completion_num = 4;
+ g_expect_stat.expect_completion_str[0] = "show session";
+ g_expect_stat.expect_completion_str[1] = "show session id";
+ g_expect_stat.expect_completion_str[2] = "show http";
+ g_expect_stat.expect_completion_str[3] = "show ssl";
+
+ stm_cmd_assistant_set_completion_cb(cmd_aide, gtest_cmd_completion_cb);
+
+#if 0 // linenoise not call completion callback if input is not stdin (freopn stdin to a local file)
+ linenoiseSetCompletionCallback(gtest_linenoise_completion_cb);
+ FILE *fake_stdin_file = fopen("./__fake_stdin_file", "w+");
+ fprintf(fake_stdin_file, "show");
+ fflush(fake_stdin_file);
+ fclose(fake_stdin_file);
+ FILE *raw_stdin_ptr = stdin;
+ ASSERT_TRUE(freopen("./__fake_stdin_file", "r", stdin) != NULL);
+
+ g_expect_stat.not_match_num = 0;
+ g_expect_stat.match_num = 0;
+ char *line = linenoise("cli> ");
+ printf("fake cli input line : %s\n", line);
+ free(line);
+ stdin = raw_stdin_ptr;
+#else
+ (void)stm_cmd_assistant_input_line(cmd_aide, "show", &g_expect_stat);
+#endif
+ EXPECT_EQ(0, g_expect_stat.not_match_num);
+ EXPECT_EQ(g_expect_stat.match_num, g_expect_stat.expect_completion_num);
+
+ g_expect_stat.not_match_num = 0;
+ g_expect_stat.match_num = 0;
+ stm_cmd_assistant_input_line(cmd_aide, "sh", &g_expect_stat);
+ EXPECT_EQ(0, g_expect_stat.not_match_num);
+ EXPECT_EQ(g_expect_stat.match_num, g_expect_stat.expect_completion_num);
+
+ g_expect_stat.not_match_num = 0;
+ g_expect_stat.match_num = 0;
+ stm_cmd_assistant_input_line(cmd_aide, "show h", &g_expect_stat);
+ EXPECT_EQ(0, g_expect_stat.not_match_num);
+ EXPECT_EQ(1, g_expect_stat.match_num);
+
+ g_expect_stat.not_match_num = 0;
+ g_expect_stat.match_num = 0;
+ stm_cmd_assistant_input_line(cmd_aide, "show s", &g_expect_stat);
+ EXPECT_EQ(0, g_expect_stat.not_match_num);
+ EXPECT_EQ(3, g_expect_stat.match_num);
+
+ g_expect_stat.not_match_num = 0;
+ g_expect_stat.match_num = 0;
+ stm_cmd_assistant_input_line(cmd_aide, "showxxx", &g_expect_stat);
+ EXPECT_EQ(0, g_expect_stat.match_num);
+
+ stm_cmd_assistant_free(cmd_aide);
+}
+
+char *line_noise_hints_callback(const char *line, int *color, int *bold)
+{
+ char *hints = (char *)stm_cmd_assistant_input_line_for_hints(stm_cmd_assistant_get(), line);
+ // all commands hints result preappend a blank space before hints
+ if (NULL == hints)
+ {
+ return NULL;
+ }
+ sds tmp = sdsnew(" ");
+ tmp = sdscat(tmp, hints);
+ *color = 90;
+ *bold = 0;
+ return tmp;
+}
+
+void line_noise_free_hints_callback(void *arg)
+{
+ sdsfree((sds)arg);
+}
+
+TEST(MONITOR_CMD_ASSISTANT, cmd_hints)
+{
+ struct stm_cmd_assistant *cmd_aide = stm_cmd_assistant_new();
+ stm_cmd_assistant_register_cmd(cmd_aide, "get", (void *)test_cmd_cb, NULL, "readonly", "key", "get xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "set", (void *)test_cmd_cb, NULL, "write", "key value [NX] [EX seconds | PX milliseconds]", "set xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show session", (void *)test_cmd_cb, NULL, "readonly", "value", "show session");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show session id", (void *)test_cmd_cb, NULL, "readonly", "id", "show session id xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show http", (void *)test_cmd_cb, NULL, "readonly", "status", "show http xxx");
+ stm_cmd_assistant_register_cmd(cmd_aide, "show ssl", (void *)test_cmd_cb, NULL, "readonly", "status", "show ssl xxx");
+
+ EXPECT_STREQ("key", stm_cmd_assistant_input_line_for_hints(cmd_aide, "get"));
+ EXPECT_STREQ("key value [NX] [EX seconds | PX milliseconds]", stm_cmd_assistant_input_line_for_hints(cmd_aide, "set"));
+ EXPECT_STREQ(NULL, stm_cmd_assistant_input_line_for_hints(cmd_aide, "xxx"));
+ EXPECT_STREQ(NULL, stm_cmd_assistant_input_line_for_hints(cmd_aide, "show"));
+ EXPECT_STREQ("value", stm_cmd_assistant_input_line_for_hints(cmd_aide, "show session"));
+ EXPECT_STREQ("id", stm_cmd_assistant_input_line_for_hints(cmd_aide, "show session id"));
+ EXPECT_STREQ("status", stm_cmd_assistant_input_line_for_hints(cmd_aide, "show http"));
+ EXPECT_STREQ("status", stm_cmd_assistant_input_line_for_hints(cmd_aide, "show ssl"));
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/test/monitor/gtest_monitor_server.cpp b/test/monitor/gtest_monitor_server.cpp
new file mode 100644
index 0000000..7251a4b
--- /dev/null
+++ b/test/monitor/gtest_monitor_server.cpp
@@ -0,0 +1,172 @@
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <gtest/gtest.h>
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "stellar/monitor.h"
+#include "stellar/stellar.h"
+#include "monitor/monitor_private.h"
+#ifdef __cplusplus
+}
+#endif
+
+static sds *stellar_cli_exec_cmd(const char *command_str, int *result_size)
+{
+ int ret;
+ sds *cmd_result_array = NULL;
+ char result[4096] = {};
+ FILE *fp = popen(command_str, "r");
+ if (NULL == fp)
+ {
+ return NULL;
+ }
+ ret = fread(result, 1, sizeof(result), fp);
+ if (ret > 0)
+ {
+ cmd_result_array = sdssplitlen(result, strlen(result), "\r\n", 1, result_size);
+ }
+ pclose(fp);
+ return cmd_result_array;
+}
+
+#if 0 // for TEST_F
+class MonitorServerMock : public testing::Test
+{
+public:
+ static struct stellar *st;
+ static pthread_t tid;
+ static int thread_count;
+ static int cmd_result_line_num;
+ static int cmd_array_size;
+ static sds *cmd_result_array;
+
+protected:
+ static void SetUpTestCase()
+ {
+ printf("Gtest Stm Server: Setup Test Case Env...\n");
+ st = stellar_new("./conf/stellar.toml");
+ assert(st != NULL);
+ stellar_run(st);
+ }
+ static void TearDownTestCase()
+ {
+ printf("Gtest Stm Server: Tear Down Test Case Env...\n");
+ stellar_free(st);
+ }
+};
+pthread_t MonitorServerMock::tid;
+int MonitorServerMock::thread_count;
+int MonitorServerMock::cmd_result_line_num;
+struct stellar *MonitorServerMock::st;
+int MonitorServerMock::cmd_array_size;
+sds *MonitorServerMock::cmd_result_array;
+#endif
+
+TEST(monitor_server, curl_ping)
+{
+ int cmd_array_size;
+ sds *cmd_result_array;
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ assert(st != NULL);
+ stellar_run(st);
+ cmd_result_array = stellar_cli_exec_cmd("curl --silent http://127.0.0.1:80/v1/stellar_monitor?raw_cmd=ping", &cmd_array_size);
+ ASSERT_TRUE(cmd_array_size >= 1);
+ ASSERT_TRUE(cmd_result_array != NULL);
+ EXPECT_STREQ("pong", cmd_result_array[0]);
+ sdsfreesplitres(cmd_result_array, cmd_array_size);
+ stellar_free(st);
+}
+
+TEST(monitor_server, curl_ping_messge)
+{
+ int cmd_array_size;
+ sds *cmd_result_array;
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ assert(st != NULL);
+ stellar_run(st);
+ cmd_result_array = stellar_cli_exec_cmd("curl --silent http://127.0.0.1:80/v1/stellar_monitor?raw_cmd=ping%20hello,world", &cmd_array_size);
+ ASSERT_TRUE(cmd_array_size >= 1);
+ ASSERT_TRUE(cmd_result_array != NULL);
+ EXPECT_STREQ("hello,world", cmd_result_array[0]);
+ sdsfreesplitres(cmd_result_array, cmd_array_size);
+ stellar_free(st);
+}
+
+TEST(monitor_server, stellar_cli_ping)
+{
+ int cmd_array_size;
+ sds *cmd_result_array;
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ assert(st != NULL);
+ stellar_run(st);
+ cmd_result_array = stellar_cli_exec_cmd("./stellar-cli -e ping", &cmd_array_size);
+ ASSERT_TRUE(cmd_array_size >= 1);
+ ASSERT_TRUE(cmd_result_array != NULL);
+ EXPECT_STREQ("pong", cmd_result_array[0]);
+ sdsfreesplitres(cmd_result_array, cmd_array_size);
+ stellar_free(st);
+}
+
+TEST(monitor_server, stellar_cli_ping_messge)
+{
+ int cmd_array_size;
+ sds *cmd_result_array;
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ assert(st != NULL);
+ stellar_run(st);
+ cmd_result_array = stellar_cli_exec_cmd("./stellar-cli -e ping hello,world", &cmd_array_size);
+ ASSERT_TRUE(cmd_array_size >= 1);
+ ASSERT_TRUE(cmd_result_array != NULL);
+ EXPECT_STREQ("hello,world", cmd_result_array[0]);
+ sdsfreesplitres(cmd_result_array, cmd_array_size);
+ stellar_free(st);
+}
+
+TEST(monitor_server, not_found)
+{
+ int cmd_array_size;
+ sds *cmd_result_array;
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ assert(st != NULL);
+ stellar_run(st);
+ cmd_result_array = stellar_cli_exec_cmd("./stellar-cli -e xxxxxxx", &cmd_array_size);
+ ASSERT_TRUE(cmd_array_size >= 1);
+ ASSERT_TRUE(cmd_result_array != NULL);
+ printf("cmd_result_array[0]: %s\n", cmd_result_array[0]);
+ EXPECT_TRUE(strstr(cmd_result_array[0], "ERR unknown command") != NULL);
+ sdsfreesplitres(cmd_result_array, cmd_array_size);
+ stellar_free(st);
+}
+
+TEST(monitor_server, invalid_args)
+{
+ int cmd_array_size;
+ sds *cmd_result_array;
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ assert(st != NULL);
+ stellar_run(st);
+ cmd_result_array = stellar_cli_exec_cmd("./stellar-cli -e ping x y z ", &cmd_array_size);
+ ASSERT_TRUE(cmd_array_size >= 1);
+ ASSERT_TRUE(cmd_result_array != NULL);
+ printf("cmd_result_array[0]: %s\n", cmd_result_array[0]);
+ EXPECT_TRUE(strstr(cmd_result_array[0], "ERR wrong number of arguments") != NULL);
+ sdsfreesplitres(cmd_result_array, cmd_array_size);
+ stellar_free(st);
+}
+
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+}
diff --git a/test/monitor/gtest_monitor_util.cpp b/test/monitor/gtest_monitor_util.cpp
new file mode 100644
index 0000000..e6deece
--- /dev/null
+++ b/test/monitor/gtest_monitor_util.cpp
@@ -0,0 +1,453 @@
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "monitor/monitor_private.h"
+#include "monitor/monitor_utils.h"
+#include "sds/sds.h"
+#ifdef __cplusplus
+}
+#endif
+
+
+TEST(MONITOR_UTIL, key_value_test)
+{
+ struct stm_key_value *kv = NULL;
+ char key_buf[16];
+ char value_buf[16];
+
+ for (int i = 0; i < 10; i++)
+ {
+ snprintf(key_buf, sizeof(key_buf), "key%d", i);
+ snprintf(value_buf, sizeof(value_buf), "value%d", i);
+ stm_cmd_key_value_append(&kv, key_buf, value_buf);
+ }
+
+ ASSERT_EQ(10, kv->tuple_num);
+ for (int i = 0; i < 10; i++)
+ {
+ snprintf(key_buf, sizeof(key_buf), "key%d", i);
+ snprintf(value_buf, sizeof(value_buf), "value%d", i);
+ ASSERT_STREQ(key_buf, kv->tuple[i].key);
+ ASSERT_STREQ(value_buf, kv->tuple[i].value);
+ }
+
+ stm_cmd_key_value_free(kv);
+}
+
+TEST(MONITOR_UTIL, stm_strncasecmp_exactly)
+{
+ ASSERT_EQ(0, stm_strncasecmp_exactly("abc", "abc", 3));
+ ASSERT_EQ(0, stm_strncasecmp_exactly("abc", "ABC", 3));
+ ASSERT_EQ(-1, stm_strncasecmp_exactly("abc", "ABCD", 3));
+ ASSERT_EQ(-1, stm_strncasecmp_exactly("abc", "ABCD", 4));
+ ASSERT_EQ(-1, stm_strncasecmp_exactly(NULL, "ABCD", 4));
+ ASSERT_EQ(-1, stm_strncasecmp_exactly("abc", NULL, 4));
+ ASSERT_EQ(-1, stm_strncasecmp_exactly("", NULL, 4));
+ ASSERT_EQ(0, stm_strncasecmp_exactly("", "", 0));
+}
+
+TEST(MONITOR_UTIL, time_range)
+{
+ time_t time_range[2];
+ ASSERT_EQ(-1, stm_time_range_pton(NULL, 0, NULL));
+ ASSERT_EQ(-1, stm_time_range_pton(NULL, 0, time_range));
+ ASSERT_EQ(-1, stm_time_range_pton("last-7-days", 0, NULL));
+
+ ASSERT_EQ(-1, stm_time_range_pton("last", 0, time_range));
+ ASSERT_EQ(-1, stm_time_range_pton("last-", 0, time_range));
+ ASSERT_EQ(-1, stm_time_range_pton("last-1", 0, time_range));
+ ASSERT_EQ(-1, stm_time_range_pton("last-1-", 0, time_range));
+ ASSERT_EQ(-1, stm_time_range_pton("last-1-d", 0, time_range));
+ ASSERT_EQ(-1, stm_time_range_pton("last-1-da", 0, time_range));
+ ASSERT_EQ(-1, stm_time_range_pton("last-1-day", 0, time_range));
+ ASSERT_EQ(0, stm_time_range_pton("last-1-days", 0, time_range));
+
+ time_t now = 10000000;
+ ASSERT_EQ(0, stm_time_range_pton("last-1-seconds", now, time_range));
+ ASSERT_EQ(now - 1, time_range[0]);
+ ASSERT_EQ(now, time_range[1]);
+
+ ASSERT_EQ(0, stm_time_range_pton("last-1-minutes", now, time_range));
+ ASSERT_EQ(now - 60, time_range[0]);
+ ASSERT_EQ(now, time_range[1]);
+
+ ASSERT_EQ(0, stm_time_range_pton("last-1-hours", now, time_range));
+ ASSERT_EQ(now - 60 * 60, time_range[0]);
+ ASSERT_EQ(now, time_range[1]);
+
+ ASSERT_EQ(0, stm_time_range_pton("last-1-days", now, time_range));
+ ASSERT_EQ(now - 24 * 60 * 60, time_range[0]);
+ ASSERT_EQ(now, time_range[1]);
+
+ now = 100;
+ ASSERT_EQ(0, stm_time_range_pton("last-999-days", now, time_range));
+ ASSERT_EQ(0, time_range[0]);
+ ASSERT_EQ(now, time_range[1]);
+
+ now = 10000;
+ ASSERT_EQ(0, stm_time_range_pton("last-1-seconds", now, time_range));
+ ASSERT_EQ(1, stm_time_in_range(now - 1, time_range));
+ ASSERT_EQ(0, stm_time_in_range(now - 10, time_range));
+ ASSERT_EQ(0, stm_time_in_range(now + 1, time_range));
+
+ ASSERT_EQ(0, stm_time_range_pton("last-1-minutes", now, time_range));
+ ASSERT_EQ(1, stm_time_in_range(now - 60, time_range));
+ ASSERT_EQ(0, stm_time_in_range(now - 61, time_range));
+ ASSERT_EQ(0, stm_time_in_range(now + 1, time_range));
+
+ ASSERT_EQ(0, stm_time_range_pton("last-1-hours", now, time_range));
+ ASSERT_EQ(1, stm_time_in_range(now - 60 * 60, time_range));
+ ASSERT_EQ(0, stm_time_in_range(now - 60 * 60 - 1, time_range));
+ ASSERT_EQ(0, stm_time_in_range(now + 1, time_range));
+}
+
+TEST(MONITOR_UTIL, inet_pton)
+{
+ unsigned int ipv4;
+ struct in6_addr ipv6;
+
+ ASSERT_EQ(0, stm_inet_pton(NULL, NULL, NULL));
+ ASSERT_EQ(0, stm_inet_pton("", NULL, NULL));
+ ASSERT_EQ(0, stm_inet_pton("1.1.1.1", NULL, &ipv6));
+ ASSERT_EQ(0, stm_inet_pton("1234::1", &ipv4, NULL));
+
+ ASSERT_EQ(AF_INET, stm_inet_pton("1.2.3.4", &ipv4, &ipv6));
+ ASSERT_EQ(0x04030201, ipv4);
+
+ ASSERT_EQ(AF_INET, stm_inet_pton("1.2.3.0", &ipv4, &ipv6));
+ ASSERT_EQ(0x00030201, ipv4);
+
+ struct in6_addr ipv6_2;
+ inet_pton(AF_INET6, "1234::1", &ipv6_2);
+ ASSERT_EQ(AF_INET6, stm_inet_pton("1234::1", &ipv4, &ipv6));
+ ASSERT_EQ(0, memcmp(&ipv6_2, &ipv6, sizeof(struct in6_addr)));
+
+ inet_pton(AF_INET6, "1234::0", &ipv6_2);
+ ASSERT_EQ(AF_INET6, stm_inet_pton("1234::0", &ipv4, &ipv6));
+ ASSERT_EQ(0, memcmp(&ipv6_2, &ipv6, sizeof(struct in6_addr)));
+}
+
+TEST(MONITOR_UTIL, ip_cidr_pton)
+{
+ unsigned int ipv4, maskv4;
+ struct in6_addr ipv6, maskv6;
+ struct in6_addr expect_maskv6;
+
+ ASSERT_EQ(0, stm_ip_cidr_pton(NULL, NULL, NULL, NULL, NULL));
+ ASSERT_EQ(0, stm_ip_cidr_pton("abcdefg", &ipv4, &maskv4, &ipv6, &maskv6));
+ ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1", &ipv4, &maskv4, &ipv6, &maskv6));
+ ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1/", &ipv4, &maskv4, &ipv6, &maskv6));
+ ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1/xx", &ipv4, &maskv4, &ipv6, &maskv6));
+
+ ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.1/8", &ipv4, &maskv4, &ipv6, &maskv6));
+ ASSERT_EQ(0xFF000000, ntohl(maskv4));
+ ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.1/16", &ipv4, &maskv4, &ipv6, &maskv6));
+ ASSERT_EQ(0xFFFF0000, ntohl(maskv4));
+ ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.1/24", &ipv4, &maskv4, &ipv6, &maskv6));
+ ASSERT_EQ(0xFFFFFF00, ntohl(maskv4));
+ ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.1/32", &ipv4, &maskv4, &ipv6, &maskv6));
+ ASSERT_EQ(0xFFFFFFFF, maskv4);
+ ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1/33", &ipv4, &maskv4, &ipv6, &maskv6));
+
+ /* IPV6 */
+ ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("::1/8", &ipv4, &maskv4, &ipv6, &maskv6));
+ memset(&expect_maskv6, 0, sizeof(struct in6_addr));
+ expect_maskv6.s6_addr[0] = 0xFF;
+ ASSERT_EQ(0, memcmp(&expect_maskv6, &maskv6, sizeof(struct in6_addr)));
+
+ ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("::1/32", &ipv4, &maskv4, &ipv6, &maskv6));
+ memset(&expect_maskv6, 0, sizeof(struct in6_addr));
+ expect_maskv6.s6_addr[0] = 0xFF;
+ expect_maskv6.s6_addr[1] = 0xFF;
+ expect_maskv6.s6_addr[2] = 0xFF;
+ expect_maskv6.s6_addr[3] = 0xFF;
+ ASSERT_EQ(0, memcmp(&expect_maskv6, &maskv6, sizeof(struct in6_addr)));
+
+ ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("::1/128", &ipv4, &maskv4, &ipv6, &maskv6));
+ memset(&expect_maskv6, 0xFF, sizeof(struct in6_addr));
+ ASSERT_EQ(0, memcmp(&expect_maskv6, &maskv6, sizeof(struct in6_addr)));
+
+ ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1/129", &ipv4, &maskv4, &ipv6, &maskv6));
+}
+
+TEST(MONITOR_UTIL, stm_ipv4_cidr_to_range)
+{
+ uint32_t ipaddr, ipmask;
+ uint32_t iprange[2];
+ inet_pton(AF_INET, "192.168.1.1", &ipaddr);
+ inet_pton(AF_INET, "255.0.0.0", &ipmask);
+ stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
+ ASSERT_EQ(iprange[0], ntohl(0xc0000000));
+ ASSERT_EQ(iprange[1], ntohl(0xc0FFFFFF));
+
+ inet_pton(AF_INET, "192.168.1.1", &ipaddr);
+ inet_pton(AF_INET, "255.255.0.0", &ipmask);
+ stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
+ ASSERT_EQ(iprange[0], ntohl(0xc0A80000));
+ ASSERT_EQ(iprange[1], ntohl(0xc0A8FFFF));
+
+ inet_pton(AF_INET, "192.168.1.1", &ipaddr);
+ inet_pton(AF_INET, "255.128.0.0", &ipmask);
+ stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
+ ASSERT_EQ(iprange[0], ntohl(0xc0800000));
+ ASSERT_EQ(iprange[1], ntohl(0xc0FFFFFF));
+
+ inet_pton(AF_INET, "192.168.1.1", &ipaddr);
+ inet_pton(AF_INET, "255.255.255.0", &ipmask);
+ stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
+ ASSERT_EQ(iprange[0], ntohl(0xc0A80100));
+ ASSERT_EQ(iprange[1], ntohl(0xc0A801FF));
+
+ inet_pton(AF_INET, "192.168.1.1", &ipaddr);
+ inet_pton(AF_INET, "255.255.255.255", &ipmask);
+ stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
+ ASSERT_EQ(iprange[0], ntohl(0xc0A80101));
+ ASSERT_EQ(iprange[1], ntohl(0xc0A80101));
+}
+
+TEST(MONITOR_UTIL, stm_ipv4_cidr_string_to_range)
+{
+ uint32_t ipv4, maskv4;
+ struct in6_addr ipv6, maskv6;
+ uint32_t ipv4_range[2];
+
+ ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.0/8", &ipv4, &maskv4, &ipv6, &maskv6));
+ stm_ipv4_cidr_to_range(ipv4, maskv4, ipv4_range);
+ ASSERT_EQ(ipv4_range[0], ntohl(0x01000000));
+ ASSERT_EQ(ipv4_range[1], ntohl(0x01FFFFFF));
+
+ ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.0/24", &ipv4, &maskv4, &ipv6, &maskv6));
+ stm_ipv4_cidr_to_range(ipv4, maskv4, ipv4_range);
+ ASSERT_EQ(ipv4_range[0], ntohl(0x01010100));
+ ASSERT_EQ(ipv4_range[1], ntohl(0x010101FF));
+
+ ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.2.3.4/32", &ipv4, &maskv4, &ipv6, &maskv6));
+ stm_ipv4_cidr_to_range(ipv4, maskv4, ipv4_range);
+ ASSERT_EQ(ipv4_range[0], ntohl(0x01020304));
+ ASSERT_EQ(ipv4_range[1], ntohl(0x01020304));
+}
+
+TEST(MONITOR_UTIL, stm_ipv6_cidr_string_to_range)
+{
+ uint32_t ipv4, maskv4;
+ struct in6_addr ipv6, maskv6;
+ struct in6_addr ipv6_range[2];
+ struct in6_addr expect_addr;
+
+ ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("1234::abcd/8", &ipv4, &maskv4, &ipv6, &maskv6));
+ stm_ipv6_cidr_to_range(&ipv6, &maskv6, ipv6_range);
+ inet_pton(AF_INET6, "1200::0", &expect_addr);
+ ASSERT_EQ(0, memcmp(&ipv6_range[0], &expect_addr, sizeof(struct in6_addr)));
+ inet_pton(AF_INET6, "12ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &expect_addr);
+ ASSERT_EQ(0, memcmp(&ipv6_range[1], &expect_addr, sizeof(struct in6_addr)));
+
+ ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("1234::abcd/16", &ipv4, &maskv4, &ipv6, &maskv6));
+ stm_ipv6_cidr_to_range(&ipv6, &maskv6, ipv6_range);
+ inet_pton(AF_INET6, "1234::0", &expect_addr);
+ ASSERT_EQ(0, memcmp(&ipv6_range[0], &expect_addr, sizeof(struct in6_addr)));
+ inet_pton(AF_INET6, "1234:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &expect_addr);
+ ASSERT_EQ(0, memcmp(&ipv6_range[1], &expect_addr, sizeof(struct in6_addr)));
+
+ ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("1234::abcd/128", &ipv4, &maskv4, &ipv6, &maskv6));
+ stm_ipv6_cidr_to_range(&ipv6, &maskv6, ipv6_range);
+ inet_pton(AF_INET6, "1234::abcd", &expect_addr);
+ ASSERT_EQ(0, memcmp(&ipv6_range[0], &expect_addr, sizeof(struct in6_addr)));
+ ASSERT_EQ(0, memcmp(&ipv6_range[1], &expect_addr, sizeof(struct in6_addr)));
+}
+
+TEST(MONITOR_UTIL, stm_ipv6_cidr_to_range)
+{
+ struct in6_addr ipaddr, ipmask;
+ struct in6_addr iprange[2];
+ struct in6_addr expect_range[2];
+
+ inet_pton(AF_INET6, "1234::abcd", &ipaddr);
+ inet_pton(AF_INET6, "ff00::", &ipmask); // 8bit mask
+ stm_ipv6_cidr_to_range(&ipaddr, &ipmask, iprange);
+ inet_pton(AF_INET6, "1200::", &expect_range[0]);
+ inet_pton(AF_INET6, "12ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &expect_range[1]);
+ ASSERT_EQ(0, memcmp(&expect_range[0], &iprange[0], sizeof(struct in6_addr)));
+ ASSERT_EQ(0, memcmp(&expect_range[1], &iprange[1], sizeof(struct in6_addr)));
+
+ inet_pton(AF_INET6, "1234::abcd", &ipaddr);
+ inet_pton(AF_INET6, "ffff::", &ipmask); // 16bit mask
+ stm_ipv6_cidr_to_range(&ipaddr, &ipmask, iprange);
+ inet_pton(AF_INET6, "1234::", &expect_range[0]);
+ inet_pton(AF_INET6, "1234:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &expect_range[1]);
+ ASSERT_EQ(0, memcmp(&expect_range[0], &iprange[0], sizeof(struct in6_addr)));
+ ASSERT_EQ(0, memcmp(&expect_range[1], &iprange[1], sizeof(struct in6_addr)));
+
+ inet_pton(AF_INET6, "1234::abcd", &ipaddr);
+ inet_pton(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &ipmask); // 128bit mask
+ stm_ipv6_cidr_to_range(&ipaddr, &ipmask, iprange);
+ inet_pton(AF_INET6, "1234::abcd", &expect_range[0]);
+ inet_pton(AF_INET6, "1234::abcd", &expect_range[1]);
+ ASSERT_EQ(0, memcmp(&expect_range[0], &iprange[0], sizeof(struct in6_addr)));
+ ASSERT_EQ(0, memcmp(&expect_range[1], &iprange[1], sizeof(struct in6_addr)));
+}
+
+TEST(MONITOR_UTIL, mem_rand)
+{
+ char buf[10000];
+ srand(time(NULL));
+ stm_mem_fill_rand(buf, sizeof(buf), 31, 61);
+ for (size_t i = 0; i < sizeof(buf); i++)
+ {
+ ASSERT_GE(buf[i], 31);
+ ASSERT_LE(buf[i], 61);
+ }
+}
+
+TEST(MONITOR_UTIL, timeout)
+{
+ struct timeval start, end;
+ start.tv_sec = 10000;
+ start.tv_usec = 0;
+
+ end = start;
+ ASSERT_EQ(0, stm_timeout(start, end, 1));
+ end.tv_sec += 1;
+ ASSERT_EQ(1, stm_timeout(start, end, 1));
+
+ end = start;
+ end.tv_usec += 999999;
+ ASSERT_EQ(0, stm_timeout(start, end, 1));
+
+ start.tv_sec = 10000;
+ start.tv_usec = 900 * 1000;
+ end.tv_sec = 10001;
+ end.tv_usec = 1;
+ ASSERT_EQ(0, stm_timeout(start, end, 1));
+ end.tv_usec += 999999;
+ ASSERT_EQ(1, stm_timeout(start, end, 1));
+}
+
+TEST(MONITOR_UTIL, parse_cli_arg)
+{
+ struct monitor_cli_args expect_args[] =
+ {
+ {"-i", "--ip", 1, 0, NULL},
+ {"-p", "--port", 1, 0, NULL},
+ {"-t", "--timeout", 1, 0, NULL},
+ {"-e", "--exec", 1, 1, NULL},
+ };
+
+ int argc = 0;
+ const char *cli_cmd = NULL;
+ sds *argv = NULL;
+
+ /* TEST: short options */
+ cli_cmd = "./monitor_cli -i 1.1.1.1 -p 8080 -t 30 -e a b c d e f g ";
+ argv = sdssplitargs((char *)cli_cmd, &argc);
+ EXPECT_EQ(0, monitor_util_parse_cmd_args(argc, (const char **)argv, expect_args, 4));
+ EXPECT_STREQ("1.1.1.1", expect_args[0].value);
+ EXPECT_STREQ("8080", expect_args[1].value);
+ EXPECT_STREQ("30", expect_args[2].value);
+ EXPECT_STREQ("a b c d e f g", expect_args[3].value);
+ sdsfree(expect_args[0].value);
+ expect_args[0].value = NULL;
+ sdsfree(expect_args[1].value);
+ expect_args[1].value = NULL;
+ sdsfree(expect_args[2].value);
+ expect_args[2].value = NULL;
+ sdsfree(expect_args[3].value);
+ expect_args[3].value = NULL;
+ sdsfreesplitres(argv, argc);
+
+ /* TEST: long options */
+ cli_cmd = "./monitor_cli --ip 111.111.111.111 --port 80808 --timeout 300 --exec a b c d e f g ";
+ argv = sdssplitargs((char *)cli_cmd, &argc);
+ EXPECT_EQ(0, monitor_util_parse_cmd_args(argc, (const char **)argv, expect_args, 4));
+ EXPECT_STREQ("111.111.111.111", expect_args[0].value);
+ EXPECT_STREQ("80808", expect_args[1].value);
+ EXPECT_STREQ("300", expect_args[2].value);
+ EXPECT_STREQ("a b c d e f g", expect_args[3].value);
+ sdsfree(expect_args[0].value);
+ expect_args[0].value = NULL;
+ sdsfree(expect_args[1].value);
+ expect_args[1].value = NULL;
+ sdsfree(expect_args[2].value);
+ expect_args[2].value = NULL;
+ sdsfree(expect_args[3].value);
+ expect_args[3].value = NULL;
+ sdsfreesplitres(argv, argc);
+
+ /* TEST: short options out of order */
+ cli_cmd = "./monitor_cli -e a b c d e f g -t 30 -i 1.1.1.1 -p 8080 ";
+ argv = sdssplitargs((char *)cli_cmd, &argc);
+ EXPECT_EQ(0, monitor_util_parse_cmd_args(argc, (const char **)argv, expect_args, 4));
+ EXPECT_STREQ("1.1.1.1", expect_args[0].value);
+ EXPECT_STREQ("8080", expect_args[1].value);
+ EXPECT_STREQ("30", expect_args[2].value);
+ EXPECT_STREQ("a b c d e f g", expect_args[3].value);
+ sdsfree(expect_args[0].value);
+ expect_args[0].value = NULL;
+ sdsfree(expect_args[1].value);
+ expect_args[1].value = NULL;
+ sdsfree(expect_args[2].value);
+ expect_args[2].value = NULL;
+ sdsfree(expect_args[3].value);
+ expect_args[3].value = NULL;
+ sdsfreesplitres(argv, argc);
+
+ /* TEST: short options with quotes */
+ cli_cmd = "./monitor_cli -i 1.1.1.1 -p 8080 -t 30 -e \"a b c d e f g\"";
+ argv = sdssplitargs((char *)cli_cmd, &argc);
+ EXPECT_EQ(0, monitor_util_parse_cmd_args(argc, (const char **)argv, expect_args, 4));
+ EXPECT_STREQ("1.1.1.1", expect_args[0].value);
+ EXPECT_STREQ("8080", expect_args[1].value);
+ EXPECT_STREQ("30", expect_args[2].value);
+ EXPECT_STREQ("a b c d e f g", expect_args[3].value);
+ sdsfree(expect_args[0].value);
+ expect_args[0].value = NULL;
+ sdsfree(expect_args[1].value);
+ expect_args[1].value = NULL;
+ sdsfree(expect_args[2].value);
+ expect_args[2].value = NULL;
+ sdsfree(expect_args[3].value);
+ expect_args[3].value = NULL;
+ sdsfreesplitres(argv, argc);
+}
+
+TEST(MONITOR_UTIL, reply)
+{
+ struct monitor_reply *reply;
+
+ reply = monitor_reply_nil();
+ sds reply_str = monitor_reply_to_string(reply);
+ EXPECT_STREQ(reply_str, "(nil)\r\n");
+ sdsfree(reply_str);
+ monitor_reply_free(reply);
+
+ reply = monitor_reply_new_string("hello, %s", "world");
+ reply_str = monitor_reply_to_string(reply);
+ EXPECT_STREQ(reply_str, "hello, world\r\n");
+ sdsfree(reply_str);
+ monitor_reply_free(reply);
+
+ reply = monitor_reply_new_integer(12345);
+ reply_str = monitor_reply_to_string(reply);
+ EXPECT_STREQ(reply_str, "(integer) 12345\r\n");
+ sdsfree(reply_str);
+ monitor_reply_free(reply);
+
+ reply = monitor_reply_new_double(123.456);
+ reply_str = monitor_reply_to_string(reply);
+ EXPECT_STREQ(reply_str, "(double) 123.456000\r\n");
+ sdsfree(reply_str);
+ monitor_reply_free(reply);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/test/monitor/gtest_packet_dump.cpp b/test/monitor/gtest_packet_dump.cpp
new file mode 100644
index 0000000..2a387b2
--- /dev/null
+++ b/test/monitor/gtest_packet_dump.cpp
@@ -0,0 +1,276 @@
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <pcap/pcap.h>
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "stellar/stellar.h"
+#include "monitor/monitor_private.h"
+#include "sds/sds.h"
+#ifdef __cplusplus
+}
+#endif
+
+#define TEST_PKT_DUMP_FILE_NAME "__gtest_stm_pkt_dump.pcap"
+
+struct async_cmd_args
+{
+ pthread_t tid;
+ const char *cmd;
+ int cmd_array_size;
+ sds *cmd_result_array;
+ int wait_thread_create_time_ms;
+};
+
+static sds *stellar_cli_exec_cmd(const char *command_str, int *result_size)
+{
+ int ret;
+ sds *cmd_result_array = NULL;
+ char result[4096] = {};
+ FILE *fp = popen(command_str, "r");
+ if (NULL == fp)
+ {
+ return NULL;
+ }
+ ret = fread(result, 1, sizeof(result), fp);
+ if (ret > 0)
+ {
+ cmd_result_array = sdssplitlen(result, strlen(result), "\r\n", 1, result_size);
+ }
+ pclose(fp);
+ return cmd_result_array;
+}
+
+static void *async_cmd_run_thread(void *arg)
+{
+ struct async_cmd_args *async_arg = (struct async_cmd_args *)arg;
+ async_arg->cmd_result_array = stellar_cli_exec_cmd(async_arg->cmd, &async_arg->cmd_array_size);
+ return NULL;
+}
+static void async_cmd_run(struct async_cmd_args *arg)
+{
+ pthread_create(&arg->tid, NULL, async_cmd_run_thread, arg);
+ usleep(arg->wait_thread_create_time_ms * 1000);
+}
+static void async_cmd_wait(struct async_cmd_args *async_arg)
+{
+ pthread_join(async_arg->tid, NULL);
+}
+
+// class MonitorServerMock : public testing::Test
+// {
+// public:
+// static struct stellar *st;
+// static pthread_t tid;
+// static int thread_count;
+// static int cmd_result_line_num;
+// static int cmd_array_size;
+// static sds *cmd_result_array;
+
+// protected:
+// static void SetUpTestCase()
+// {
+// printf("Gtest Stm Server: Setup Test Case Env...\n");
+// st = stellar_new("./conf/stellar.toml", "./plugin/spec.toml", "./conf/log.toml");
+// assert(st != NULL);
+// }
+// static void TearDownTestCase()
+// {
+// printf("Gtest Stm Server: Tear Down Test Case Env...\n");
+// stellar_free(st);
+// }
+// };
+// pthread_t MonitorServerMock::tid;
+// int MonitorServerMock::thread_count;
+// int MonitorServerMock::cmd_result_line_num;
+// struct stellar *MonitorServerMock::st;
+// int MonitorServerMock::cmd_array_size;
+// sds *MonitorServerMock::cmd_result_array;
+
+static int get_local_pcap_packet_number(const char *filename)
+{
+ char cmd_buf[256] = {};
+ snprintf(cmd_buf, sizeof(cmd_buf), "tcpdump -r %s -n -nn -t -q | wc -l", filename);
+ int cmd_res_size;
+ sds *cmd_res = stellar_cli_exec_cmd(cmd_buf, &cmd_res_size);
+
+ int pkt_num = 0;
+ for (int i = 0; i < cmd_res_size; i++)
+ {
+ if (strstr(cmd_res[i], "reading from file") != NULL)
+ {
+ continue;
+ }
+ if (strstr(cmd_res[i], "tcpdump") != NULL)
+ {
+ continue;
+ }
+ if (isdigit(cmd_res[i][0]))
+ {
+ pkt_num = atoi(cmd_res[i]);
+ }
+ }
+ sdsfreesplitres(cmd_res, cmd_res_size);
+ return pkt_num;
+}
+
+static int get_netstat_num(const char *cmd)
+{
+ int cmd_res_size;
+ sds *cmd_res = stellar_cli_exec_cmd(cmd, &cmd_res_size);
+
+ int pkt_num = 0;
+ for (int i = 0; i < cmd_res_size; i++)
+ {
+ if (isdigit(cmd_res[i][0]))
+ {
+ pkt_num = atoi(cmd_res[i]);
+ }
+ }
+ sdsfreesplitres(cmd_res, cmd_res_size);
+ return pkt_num;
+}
+
+TEST(MONITOR_PKT_DUMP, base)
+{
+ struct async_cmd_args async_arg = {};
+ async_arg.wait_thread_create_time_ms = 100;
+ char cmd_buf[256] = {};
+ remove(TEST_PKT_DUMP_FILE_NAME);
+ const int expect_cap_pkt_num = 11;
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ ASSERT_TRUE(st != NULL);
+ snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -c %d -s0 -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
+ async_arg.cmd = cmd_buf;
+ async_cmd_run(&async_arg);
+ stellar_run(st);
+ async_cmd_wait(&async_arg);
+ sdsfreesplitres(async_arg.cmd_result_array, async_arg.cmd_array_size);
+
+ int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
+ EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
+ stellar_free(st);
+}
+
+TEST(MONITOR_PKT_DUMP, arg_ip_port)
+{
+ struct async_cmd_args async_arg = {};
+ async_arg.wait_thread_create_time_ms = 100;
+ char cmd_buf[256] = {};
+ remove(TEST_PKT_DUMP_FILE_NAME);
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ ASSERT_TRUE(st != NULL);
+
+ const int expect_cap_pkt_num = 13;
+ snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -c %d -i 127.0.0.1 -P 80 -s0 -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
+ async_arg.cmd = cmd_buf;
+ async_cmd_run(&async_arg);
+ stellar_run(st);
+ async_cmd_wait(&async_arg);
+ sdsfreesplitres(async_arg.cmd_result_array, async_arg.cmd_array_size);
+
+ int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
+ EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
+ stellar_free(st);
+}
+
+TEST(MONITOR_PKT_DUMP, arg_bpf)
+{
+ struct async_cmd_args async_arg = {};
+ async_arg.wait_thread_create_time_ms = 100;
+ char cmd_buf[256] = {};
+ remove(TEST_PKT_DUMP_FILE_NAME);
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ ASSERT_TRUE(st != NULL);
+
+ const int expect_cap_pkt_num = 33;
+ snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -c %d host 172.16.0.203 and host 172.100.15.25 and udp port 1935 and udp port 52043 -s0 -U -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
+ async_arg.cmd = cmd_buf;
+ async_cmd_run(&async_arg);
+ stellar_run(st);
+ async_cmd_wait(&async_arg);
+ sdsfreesplitres(async_arg.cmd_result_array, async_arg.cmd_array_size);
+
+ int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
+ EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
+ stellar_free(st);
+}
+
+TEST(MONITOR_PKT_DUMP, all_args)
+{
+ struct async_cmd_args async_arg = {};
+ async_arg.wait_thread_create_time_ms = 100;
+ char cmd_buf[256] = {};
+ remove(TEST_PKT_DUMP_FILE_NAME);
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ ASSERT_TRUE(st != NULL);
+
+ const int expect_cap_pkt_num = 33;
+ snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -c %d -i 127.0.0.1 -P 80 -g host 172.16.0.203 and host 172.100.15.25 and udp port 1935 and udp port 52043 -s0 -U -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
+ async_arg.cmd = cmd_buf;
+ async_cmd_run(&async_arg);
+ stellar_run(st);
+ async_cmd_wait(&async_arg);
+ sdsfreesplitres(async_arg.cmd_result_array, async_arg.cmd_array_size);
+
+ int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
+ EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
+ stellar_free(st);
+}
+
+TEST(MONITOR_PKT_DUMP, too_many_clients)
+{
+#define MAX_CLIENT_NUM 1000
+ struct async_cmd_args async_arg[MAX_CLIENT_NUM] = {};
+ char cmd_buf[256] = {};
+ const int expect_cap_pkt_num = 1;
+ remove(TEST_PKT_DUMP_FILE_NAME);
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ ASSERT_TRUE(st != NULL);
+
+ snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -q -t -c 1 udp port 5005 and udp port 39627 -w /dev/null"); // the last udp packet
+
+ for (int i = 0; i < MAX_CLIENT_NUM; i++)
+ {
+ async_arg[i].wait_thread_create_time_ms = 0;
+ async_arg[i].cmd = cmd_buf;
+ async_cmd_run(&async_arg[i]);
+ }
+ struct async_cmd_args save_pcap_async_arg = {};
+ save_pcap_async_arg.wait_thread_create_time_ms = 100;
+ char save_pcap_cmd_buf[256] = {};
+ snprintf(save_pcap_cmd_buf, sizeof(save_pcap_cmd_buf), "./stellar-dump -n -nn -q -t -c %d udp port 5005 and udp port 39627 -s0 -U -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
+ save_pcap_async_arg.cmd = save_pcap_cmd_buf;
+ async_cmd_run(&save_pcap_async_arg);
+
+ /* todo: how do I make sure all stellar-dump are started? */
+ while (get_netstat_num("netstat -anup | grep stellar | wc -l") < MAX_CLIENT_NUM + 1)
+ {
+ printf("### wait for all stellar-dump starting...\n");
+ sleep(1);
+ }
+ stellar_run(st);
+
+ for (int i = 0; i < MAX_CLIENT_NUM; i++)
+ {
+ async_cmd_wait(&async_arg[i]);
+ sdsfreesplitres(async_arg[i].cmd_result_array, async_arg[i].cmd_array_size);
+ }
+ async_cmd_wait(&save_pcap_async_arg);
+ sdsfreesplitres(save_pcap_async_arg.cmd_result_array, save_pcap_async_arg.cmd_array_size);
+
+ int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
+ EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
+ stellar_free(st);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+}
diff --git a/test/monitor/gtest_packet_dump_tunnel.cpp b/test/monitor/gtest_packet_dump_tunnel.cpp
new file mode 100644
index 0000000..0b0d462
--- /dev/null
+++ b/test/monitor/gtest_packet_dump_tunnel.cpp
@@ -0,0 +1,140 @@
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <pcap/pcap.h>
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "monitor/monitor_private.h"
+#include "sds/sds.h"
+#include "stellar/stellar.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#define TEST_PKT_DUMP_FILE_NAME "__gtest_stm_pkt_dump.pcap"
+static pthread_t tid;
+static int cmd_result_array_size;
+static sds *cmd_result_array;
+
+static sds *stellar_cli_exec_cmd(const char *command_str, int *result_size)
+{
+ int ret;
+ sds *cmd_res_array = NULL;
+ char result[4096] = {};
+ FILE *fp = popen(command_str, "r");
+ if (NULL == fp)
+ {
+ return NULL;
+ }
+ ret = fread(result, 1, sizeof(result), fp);
+ if (ret > 0)
+ {
+ cmd_res_array = sdssplitlen(result, strlen(result), "\r\n", 1, result_size);
+ }
+ pclose(fp);
+ return cmd_res_array;
+}
+
+
+static void *async_cmd_run_thread(void *arg)
+{
+ cmd_result_array = stellar_cli_exec_cmd((char *)arg, &cmd_result_array_size);
+ return NULL;
+}
+static void async_cmd_run(char *cmd)
+{
+ pthread_create(&tid, NULL, async_cmd_run_thread, cmd);
+ usleep(100000);
+}
+static void async_cmd_wait(void)
+{
+ pthread_join(tid, NULL);
+}
+
+// class MonitorServerMock : public testing::Test
+// {
+// public:
+// static struct stellar *st;
+// static pthread_t tid;
+// static int thread_count;
+// static int cmd_result_line_num;
+// static int cmd_result_array_size;
+// static sds *cmd_result_array;
+
+// protected:
+// static void SetUpTestCase()
+// {
+// printf("Gtest Stm Server: Setup Test Case Env...\n");
+// st = stellar_new("./conf/stellar.toml");
+// assert(st != NULL);
+// }
+// static void TearDownTestCase()
+// {
+// printf("Gtest Stm Server: Tear Down Test Case Env...\n");
+// stellar_free(st);
+// }
+// };
+// pthread_t MonitorServerMock::tid;
+// int MonitorServerMock::thread_count;
+// int MonitorServerMock::cmd_result_line_num;
+// struct stellar *MonitorServerMock::st;
+// int MonitorServerMock::cmd_result_array_size;
+// sds *MonitorServerMock::cmd_result_array;
+
+static int get_local_pcap_packet_number(const char *filename)
+{
+ char cmd_buf[256] = {};
+ snprintf(cmd_buf, sizeof(cmd_buf), "tcpdump -r %s -n -nn -t -q | wc -l", filename);
+ int cmd_res_size;
+ sds *cmd_res = stellar_cli_exec_cmd(cmd_buf, &cmd_res_size);
+
+ int pkt_num = 0;
+ for (int i = 0; i < cmd_res_size; i++)
+ {
+ if (strstr(cmd_res[i], "reading from file") != NULL)
+ {
+ continue;
+ }
+ if (strstr(cmd_res[i], "tcpdump") != NULL)
+ {
+ continue;
+ }
+ if (isdigit(cmd_res[i][0]))
+ {
+ pkt_num = atoi(cmd_res[i]);
+ }
+ }
+ sdsfreesplitres(cmd_res, cmd_res_size);
+ return pkt_num;
+}
+
+TEST(MONITOR_PKT_DUMP_TUNNEL, arg_bpf)
+{
+ char cmd_buf[256] = {};
+ remove(TEST_PKT_DUMP_FILE_NAME);
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ ASSERT_TRUE(st != NULL);
+
+ const int expect_cap_pkt_num = 5;
+ snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -g -c %d udp port 53 or udp port 443 or udp port 8246 or udp port 6620 -s0 -U -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
+ async_cmd_run(cmd_buf);
+ stellar_run(st);
+ async_cmd_wait();
+ sdsfreesplitres(cmd_result_array, cmd_result_array_size);
+
+ int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
+ EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
+ stellar_free(st);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+}
diff --git a/test/monitor/gtest_packet_dump_unit.cpp b/test/monitor/gtest_packet_dump_unit.cpp
new file mode 100644
index 0000000..285294c
--- /dev/null
+++ b/test/monitor/gtest_packet_dump_unit.cpp
@@ -0,0 +1,229 @@
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <pcap/pcap.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "stellar/packet.h"
+#include "monitor/monitor_private.h"
+#include "sds/sds.h"
+#include "monitor/monitor_packet_dump.h"
+#ifdef __cplusplus
+}
+#endif
+
+#define TEST_PKT_DUMP_FILE_NAME "__gtest_stm_pkt_dump.pcap"
+
+/* mock */
+int packet_manager_subscribe(UNUSED struct packet_manager *pkt_mgr, UNUSED enum packet_stage stage, UNUSED on_packet_stage_callback *cb, UNUSED void *args)
+{
+ assert(0);
+ return 0;
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, parse_args)
+{
+ int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
+ struct stm_pktdump_task tmp_conn_parm = {};
+ const char *argv[] = {"./stellar_dump", "greedy", "threads", "1,2,3", "datalinkip", "1.2.3.4", "datalinkport", "54321", "bpf", "tcp", "port", "80"};
+ int argc = sizeof(argv) / sizeof(char *);
+ sds cli_bpf_str;
+ struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
+ ASSERT_EQ(NULL, reply);
+ EXPECT_EQ(1, tmp_conn_parm.greedy_mode);
+ EXPECT_EQ(0, thread_enable_flag[0]);
+ EXPECT_EQ(1, thread_enable_flag[1]);
+ EXPECT_EQ(1, thread_enable_flag[2]);
+ EXPECT_EQ(1, thread_enable_flag[3]);
+ EXPECT_EQ(0, thread_enable_flag[5]);
+ EXPECT_EQ(0, thread_enable_flag[7]);
+ EXPECT_EQ(0, thread_enable_flag[9]);
+ EXPECT_EQ(0x04030201, tmp_conn_parm.peer_data_ip_net_order);
+ EXPECT_EQ(htons(54321), tmp_conn_parm.peer_data_port_net_order);
+ EXPECT_STREQ(cli_bpf_str, "tcp port 80");
+ pcap_freecode(&tmp_conn_parm.bpf_filter);
+ sdsfree(cli_bpf_str);
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, parse_args_out_of_order)
+{
+ int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
+ struct stm_pktdump_task tmp_conn_parm = {};
+ const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "datalinkport", "54321", "bpf", "tcp", "port", "80", "greedy", "threads", "1,3,5,7,9"};
+ int argc = sizeof(argv) / sizeof(char *);
+ sds cli_bpf_str;
+ struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
+ ASSERT_EQ(NULL, reply);
+ EXPECT_EQ(1, tmp_conn_parm.greedy_mode);
+ EXPECT_EQ(0, thread_enable_flag[0]);
+ EXPECT_EQ(1, thread_enable_flag[1]);
+ EXPECT_EQ(0, thread_enable_flag[2]);
+ EXPECT_EQ(1, thread_enable_flag[3]);
+ EXPECT_EQ(1, thread_enable_flag[5]);
+ EXPECT_EQ(1, thread_enable_flag[7]);
+ EXPECT_EQ(1, thread_enable_flag[9]);
+ EXPECT_EQ(0, thread_enable_flag[10]);
+ EXPECT_EQ(0x04030201, tmp_conn_parm.peer_data_ip_net_order);
+ EXPECT_EQ(htons(54321), tmp_conn_parm.peer_data_port_net_order);
+ EXPECT_STREQ(cli_bpf_str, "tcp port 80 ");
+ pcap_freecode(&tmp_conn_parm.bpf_filter);
+ sdsfree(cli_bpf_str);
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, parse_args_error_ip)
+{
+ int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
+ struct stm_pktdump_task tmp_conn_parm = {};
+ const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.256", "datalinkport", "54321", "bpf", "tcp", "port", "80", "greedy", "threads", "1,3,5,7,9"};
+ int argc = sizeof(argv) / sizeof(char *);
+ sds cli_bpf_str = NULL;
+ struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
+ ASSERT_TRUE(reply != NULL);
+ pcap_freecode(&tmp_conn_parm.bpf_filter);
+ sdsfree(cli_bpf_str);
+ monitor_reply_free(reply);
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, parse_args_error_port)
+{
+ int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
+ struct stm_pktdump_task tmp_conn_parm = {};
+ const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "datalinkport", "654321", "bpf", "tcp", "port", "80", "greedy", "threads", "1,3,5,7,9"};
+ int argc = sizeof(argv) / sizeof(char *);
+ sds cli_bpf_str = NULL;
+ struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
+ ASSERT_TRUE(reply != NULL);
+ pcap_freecode(&tmp_conn_parm.bpf_filter);
+ sdsfree(cli_bpf_str);
+ monitor_reply_free(reply);
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, parse_args_no_port)
+{
+ int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
+ struct stm_pktdump_task tmp_conn_parm = {};
+ const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "bpf", "tcp", "port", "80", "greedy", "threads", "1,3,5,7,9"};
+ int argc = sizeof(argv) / sizeof(char *);
+ sds cli_bpf_str = NULL;
+ struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
+ ASSERT_TRUE(reply != NULL);
+ pcap_freecode(&tmp_conn_parm.bpf_filter);
+ sdsfree(cli_bpf_str);
+ monitor_reply_free(reply);
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, parse_args_error_bpf)
+{
+ int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
+ struct stm_pktdump_task tmp_conn_parm = {};
+ const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "datalinkport", "654321", "bpf", "tcp", "port", "xxx", "greedy", "threads", "1,3,5,7,9"};
+ int argc = sizeof(argv) / sizeof(char *);
+ sds cli_bpf_str = NULL;
+ struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
+ ASSERT_TRUE(reply != NULL);
+ pcap_freecode(&tmp_conn_parm.bpf_filter);
+ sdsfree(cli_bpf_str);
+ monitor_reply_free(reply);
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, parse_args_error_threads)
+{
+ int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
+ struct stm_pktdump_task tmp_conn_parm = {};
+ const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "datalinkport", "54321", "bpf", "tcp", "port", "xxx", "greedy", "threads", "1,3,5,7,999"};
+ int argc = sizeof(argv) / sizeof(char *);
+ sds cli_bpf_str = NULL;
+ struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
+ ASSERT_TRUE(reply != NULL);
+ pcap_freecode(&tmp_conn_parm.bpf_filter);
+ sdsfree(cli_bpf_str);
+ monitor_reply_free(reply);
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, pcap_compile_and_filter_ipv4)
+{
+ /* This is a IPv4 tcp SYN packet, 192.168.40.139:48662 -> 107.155.25.121:80 */
+ static const unsigned char packet_bytes[] = {
+ 0x48, 0x73, 0x97, 0x96, 0x38, 0x10, 0x00, 0x22,
+ 0x46, 0x2f, 0x35, 0xb4, 0x08, 0x00,
+ 0x45, 0x00, 0x00, 0x38, 0x0c, 0x1d, 0x40, 0x00, 0x40, 0x06,
+ 0xc0, 0x5b, 0xc0, 0xa8, 0x28, 0x8b, 0x6b, 0x9b,
+ 0x19, 0x79, 0xbe, 0x16, 0x00, 0x50, 0x7b, 0xf9,
+ 0x8b, 0x34, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02,
+ 0x72, 0x10, 0x6e, 0x72, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x2d, 0xbb,
+ 0x87, 0x29, 0x00, 0x00, 0x00, 0x00};
+
+ struct bpf_program bpf_bin;
+ pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_EN10MB, &bpf_bin, "tcp port 48662 and host 192.168.40.139 and host 107.155.25.121", 1, 0);
+ int match_from_eth = bpf_filter(bpf_bin.bf_insns, packet_bytes, sizeof(packet_bytes), sizeof(packet_bytes));
+ EXPECT_TRUE(match_from_eth != 0);
+ pcap_freecode(&bpf_bin);
+
+ pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_EN10MB, &bpf_bin, "tcp port 12345 and tcp port 54321", 1, 0);
+ int unmatch_from_eth = bpf_filter(bpf_bin.bf_insns, packet_bytes, sizeof(packet_bytes), sizeof(packet_bytes));
+ EXPECT_TRUE(unmatch_from_eth == 0);
+ pcap_freecode(&bpf_bin);
+
+ pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_RAW, &bpf_bin, "tcp port 48662 and host 192.168.40.139 and host 107.155.25.121", 1, 0);
+ int match_from_ip = bpf_filter(bpf_bin.bf_insns, packet_bytes + 14, sizeof(packet_bytes) - 14, sizeof(packet_bytes) - 14);
+ EXPECT_TRUE(match_from_ip != 0);
+ pcap_freecode(&bpf_bin);
+
+ pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_RAW, &bpf_bin, "host 1.2.3.4 and host 5.6.7.8", 1, 0);
+ int unmatch_from_ip = bpf_filter(bpf_bin.bf_insns, packet_bytes + 14, sizeof(packet_bytes) - 14, sizeof(packet_bytes) - 14);
+ EXPECT_TRUE(unmatch_from_ip == 0);
+ pcap_freecode(&bpf_bin);
+}
+
+TEST(MONITOR_PKT_DUMP_UNIT, pcap_compile_and_filter_ipv6)
+{
+ /* This is a IPv6 tcp SYN packet, 2001::192.168.40.134:37948 -> 2001::192.168.40.133:22 */
+ static const unsigned char packet_bytes[] = {
+ 0x00, 0x22, 0x46, 0x36, 0x51, 0x38, 0x00, 0x22,
+ 0x46, 0x36, 0x51, 0x3c, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x28, 0x06, 0x40, 0x20, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92,
+ 0x01, 0x68, 0x00, 0x40, 0x01, 0x34, 0x20, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92,
+ 0x01, 0x68, 0x00, 0x40, 0x01, 0x33, 0x94, 0x3c,
+ 0x00, 0x16, 0x09, 0x68, 0x7d, 0x85, 0x00, 0x00,
+ 0x00, 0x00, 0xa0, 0x02, 0x71, 0xac, 0xff, 0xec,
+ 0x00, 0x00, 0x02, 0x04, 0x07, 0x94, 0x04, 0x02,
+ 0x08, 0x0a, 0x16, 0xa8, 0x59, 0xc2, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x03, 0x03, 0x07};
+
+ struct bpf_program bpf_bin;
+ pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_EN10MB, &bpf_bin, "tcp port 37948 and host 2001::192.168:40:134 and host 2001::192:168:40:133", 1, 0);
+ int match_from_eth = bpf_filter(bpf_bin.bf_insns, packet_bytes, sizeof(packet_bytes), sizeof(packet_bytes));
+ EXPECT_TRUE(match_from_eth != 0);
+ pcap_freecode(&bpf_bin);
+
+ pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_EN10MB, &bpf_bin, "tcp port 12345 and tcp port 54321", 1, 0);
+ int unmatch_from_eth = bpf_filter(bpf_bin.bf_insns, packet_bytes, sizeof(packet_bytes), sizeof(packet_bytes));
+ EXPECT_TRUE(unmatch_from_eth == 0);
+ pcap_freecode(&bpf_bin);
+
+ pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_RAW, &bpf_bin, "tcp port 37948 and host 2001::192.168:40:134 and host 2001::192:168:40:133", 1, 0);
+ int match_from_ip = bpf_filter(bpf_bin.bf_insns, packet_bytes + 14, sizeof(packet_bytes) - 14, sizeof(packet_bytes) - 14);
+ EXPECT_TRUE(match_from_ip != 0);
+ pcap_freecode(&bpf_bin);
+
+ pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_RAW, &bpf_bin, "host 1.2.3.4 and host 5.6.7.8", 1, 0);
+ int unmatch_from_ip = bpf_filter(bpf_bin.bf_insns, packet_bytes + 14, sizeof(packet_bytes) - 14, sizeof(packet_bytes) - 14);
+ EXPECT_TRUE(unmatch_from_ip == 0);
+ pcap_freecode(&bpf_bin);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+}
diff --git a/test/monitor/gtest_ringbuf.cpp b/test/monitor/gtest_ringbuf.cpp
new file mode 100644
index 0000000..d05378f
--- /dev/null
+++ b/test/monitor/gtest_ringbuf.cpp
@@ -0,0 +1,194 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <gtest/gtest.h>
+#include "stellar/monitor.h"
+#include "monitor_ringbuf.h"
+
+#define TOTAL_TEST_NUM 100000
+#define BATCH_NUM 10
+#define MAX_PAYLOAD_LEN 2048
+#define MAGIC_NUM 0x1234ABCD
+static struct monitor_ringbuf_wrap *rbf_ctop;
+static struct monitor_ringbuf_wrap *rbf_ptoc;
+static const size_t ringbuf_size = MAX_PAYLOAD_LEN * 1024;
+
+static unsigned long long ctop_producer_len = 0, ctop_consumer_len = 0;
+static unsigned long long ptoc_producer_len = 0, ptoc_consumer_len = 0;
+
+struct test_ringbuf_data
+{
+ long long magic;
+ long long block_len; /* total length, include this header and paylaod */
+ char payload[MAX_PAYLOAD_LEN];
+};
+
+static int test_ringbuf_push_mode = 0; /* 0: easy push, 1: push by offset */
+
+static int ringbuf_push(int tid, struct monitor_ringbuf_wrap *rbf, struct test_ringbuf_data *payload)
+{
+ int ret = 0;
+ if (0 == test_ringbuf_push_mode)
+ {
+ ret = stm_ringbuf_easy_push(tid, rbf, payload, payload->block_len);
+ }
+ else
+ {
+ ssize_t offset = stm_ringbuf_stream_start(tid, rbf, payload->block_len);
+ if (offset < 0)
+ {
+ return -1;
+ }
+ stm_ringbuf_stream_append(tid, rbf, offset++, payload++, 1);
+ usleep(1);
+ stm_ringbuf_stream_append(tid, rbf, offset++, payload++, 1);
+ usleep(10);
+ stm_ringbuf_stream_append(tid, rbf, offset++, payload++, 1);
+ usleep(100);
+ stm_ringbuf_stream_append(tid, rbf, offset, payload, payload->block_len - 3);
+ stm_ringbuf_stream_finish(tid, rbf);
+ }
+ return ret;
+}
+
+static int ringbuf_producer(int tid, struct monitor_ringbuf_wrap *rbf, int batch)
+{
+ struct test_ringbuf_data t;
+ for (int i = 0; i < batch; i++)
+ {
+ t.magic = MAGIC_NUM;
+ t.block_len = rand() % (MAX_PAYLOAD_LEN - sizeof(long long) * 2) + sizeof(long long) * 2; /* variable-length */
+ while (ringbuf_push(tid, rbf, &t) < 0)
+ {
+ usleep(1);
+ }
+ if (rbf == rbf_ctop)
+ {
+ ctop_producer_len += t.block_len;
+ }
+ else
+ {
+ ptoc_producer_len += t.block_len;
+ }
+ }
+ return batch;
+}
+
+static int ringbuf_customer(int tid, struct monitor_ringbuf_wrap *rbf, int batch)
+{
+ (void)tid;
+ void *data;
+ size_t pop_len;
+ for (int i = 0; i < batch; i++)
+ {
+ while ((data = stm_ringbuf_pop(rbf, &pop_len)) == NULL)
+ {
+ ;
+ }
+
+ struct test_ringbuf_data *td = (struct test_ringbuf_data *)data;
+ if (td->magic != MAGIC_NUM)
+ {
+ stm_ringbuf_release(rbf, pop_len); /* maybe lost many messages */
+ return 0;
+ }
+ td->magic = random();
+ size_t rel_len = td->block_len;
+ if (rbf == rbf_ctop)
+ {
+ ctop_consumer_len += td->block_len;
+ }
+ else
+ {
+ ptoc_consumer_len += td->block_len;
+ }
+ td->block_len = 0;
+ stm_ringbuf_release(rbf, rel_len);
+ }
+ return batch;
+}
+
+static void *producer_thread(void *arg)
+{
+ (void)arg;
+ int ctop_num = 0;
+ int ptoc_num = 0;
+ for (int i = 0; i < TOTAL_TEST_NUM; i++)
+ {
+ /* only two threads, use fake fix thread id */
+ ctop_num += ringbuf_producer(0, rbf_ctop, BATCH_NUM);
+ ptoc_num += ringbuf_customer(0, rbf_ptoc, BATCH_NUM);
+ }
+ return NULL;
+}
+
+static void *consumer_thread(void *arg)
+{
+ (void)arg;
+ int ctop_num = 0;
+ int ptoc_num = 0;
+ for (int i = 0; i < TOTAL_TEST_NUM; i++)
+ {
+ /* only two threads, use fake fix thread id */
+ ptoc_num += ringbuf_producer(1, rbf_ptoc, BATCH_NUM);
+ ctop_num += ringbuf_customer(1, rbf_ctop, BATCH_NUM);
+ }
+ return NULL;
+}
+
+static int gtest_ringbuf(int mode)
+{
+ pthread_t pid[2];
+ test_ringbuf_push_mode = mode;
+ rbf_ctop = stm_ringbuf_wrap_new(2, ringbuf_size);
+ rbf_ptoc = stm_ringbuf_wrap_new(2, ringbuf_size);
+
+ pthread_create(&pid[0], NULL, producer_thread, NULL);
+ pthread_create(&pid[1], NULL, consumer_thread, NULL);
+
+ pthread_join(pid[0], NULL);
+ pthread_join(pid[1], NULL);
+
+ stm_ringbuf_get_statistics(rbf_ctop, NULL, &ctop_producer_len, NULL, &ctop_consumer_len);
+ stm_ringbuf_get_statistics(rbf_ptoc, NULL, &ptoc_producer_len, NULL, &ptoc_consumer_len);
+
+ printf("ctop push len:%llu, ctop pop len:%llu\n", ctop_producer_len, ctop_consumer_len);
+ printf("ptoc push len:%llu, ctop pop len:%llu\n", ptoc_producer_len, ptoc_consumer_len);
+ if (ctop_producer_len != ctop_consumer_len)
+ {
+ return -1;
+ }
+ if (ptoc_producer_len != ptoc_consumer_len)
+ {
+ return -1;
+ }
+
+ stm_ringbuf_wrap_free(rbf_ptoc);
+ stm_ringbuf_wrap_free(rbf_ctop);
+ pthread_cancel(pid[0]);
+ pthread_cancel(pid[1]);
+
+ return 0;
+}
+
+TEST(MONITOR_RINGBUF, easy)
+{
+ ASSERT_EQ(0, gtest_ringbuf(0));
+}
+
+TEST(MONITOR_RINGBUF, stream)
+{
+ ASSERT_EQ(0, gtest_ringbuf(0));
+}
+
+int main(int argc, char **argv)
+{
+ srand(time(NULL));
+
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/test/monitor/gtest_rpc.cpp b/test/monitor/gtest_rpc.cpp
new file mode 100644
index 0000000..74be611
--- /dev/null
+++ b/test/monitor/gtest_rpc.cpp
@@ -0,0 +1,68 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <gtest/gtest.h>
+#include "stellar/monitor.h"
+#include "monitor/monitor_private.h"
+#include "monitor/monitor_rpc.h"
+
+#define TEST_NUM 2000000
+static long g_worker_thread_run = 1;
+
+static void *phony_worker_thread(void *arg)
+{
+ struct monitor_rpc *rpc_ins = (struct monitor_rpc *)arg;
+ while (g_worker_thread_run)
+ {
+ stm_rpc_exec(0, rpc_ins);
+ }
+ return NULL;
+}
+
+static struct iovec on_worker_thread_cb(int worker_thread_idx, struct iovec req, void *user_args)
+{
+ EXPECT_EQ(0, worker_thread_idx);
+ pthread_t *phony_worker_thread_id = (pthread_t *)user_args;
+ EXPECT_EQ(*phony_worker_thread_id, pthread_self());
+
+ struct iovec resp = req;
+ resp.iov_base = (void *)"world";
+ resp.iov_len = 12345 + req.iov_len;
+ return resp;
+}
+
+TEST(MONITOR_RPC, base)
+{
+ srand(time(NULL));
+ pthread_t phony_worker_thread_id = 0;
+
+ struct monitor_rpc *rpc_ins = stm_rpc_new();
+ ASSERT_TRUE(rpc_ins != NULL);
+
+ pthread_create(&phony_worker_thread_id, NULL, phony_worker_thread, rpc_ins);
+ struct iovec req = {.iov_base = (void *)"hello", .iov_len = 5};
+ usleep(10000);
+
+ for (int i = 0; i < TEST_NUM; i++)
+ {
+ req.iov_len = rand() % TEST_NUM;
+ struct iovec resp = stm_rpc_call(rpc_ins, req, on_worker_thread_cb, &phony_worker_thread_id);
+ EXPECT_EQ(req.iov_len + 12345, resp.iov_len);
+ EXPECT_EQ(resp.iov_base, (void *)"world");
+ }
+ printf("rpc call num: %d\n", TEST_NUM);
+ g_worker_thread_run = 0;
+ pthread_cancel(phony_worker_thread_id);
+ pthread_join(phony_worker_thread_id, NULL);
+ stm_rpc_free(rpc_ins);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/test/monitor/gtest_sds.cpp b/test/monitor/gtest_sds.cpp
new file mode 100644
index 0000000..475982e
--- /dev/null
+++ b/test/monitor/gtest_sds.cpp
@@ -0,0 +1,152 @@
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <gtest/gtest.h>
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "sds/sds.h"
+#ifdef __cplusplus
+}
+#endif
+
+TEST(MONITOR_SDS, sds_new)
+{
+ sds s = sdsnew("hello, world!");
+ ASSERT_STREQ(s, "hello, world!");
+ ASSERT_EQ(sdslen(s), strlen("hello, world!"));
+ sdsfree(s);
+}
+
+TEST(MONITOR_SDS, sds_cat)
+{
+ sds s = sdsempty();
+ ASSERT_EQ(sdslen(s), 0);
+
+ s = sdscat(s, "h");
+ ASSERT_EQ(sdslen(s), 1);
+ ASSERT_STREQ(s, "h");
+
+ s = sdscat(s, "ello");
+ ASSERT_EQ(sdslen(s), 5);
+ ASSERT_STREQ(s, "hello");
+
+ sdsfree(s);
+}
+
+TEST(MONITOR_SDS, sds_cat_printf)
+{
+ sds s = sdsempty();
+ s = sdscatprintf(s, "%s", "hello");
+ ASSERT_STREQ(s, "hello");
+
+ s = sdscatprintf(s, "%s", ",world!");
+ ASSERT_STREQ(s, "hello,world!");
+
+ s = sdscatprintf(s, "%d", 200);
+ ASSERT_STREQ(s, "hello,world!200");
+
+ sdsfree(s);
+}
+
+TEST(MONITOR_SDS, sdstrim)
+{
+ sds s = sdsnew("hello, world!\r\n");
+ ASSERT_STRNE(s, "hello, world!");
+ sds s1 = sdstrim(s, "\r\n");
+ ASSERT_STREQ(s1, "hello, world!");
+ sdsfree(s);
+
+ s = sdsnew("xxxhello, world!xxx\r\n");
+ s1 = sdstrim(s, "xxx\r\n");
+ ASSERT_STREQ(s1, "hello, world!");
+ sdsfree(s);
+}
+
+TEST(MONITOR_SDS, sdsrange)
+{
+ sds s = sdsnew("1234567890");
+ ASSERT_STREQ(s, "1234567890");
+ ASSERT_EQ(10, sdslen(s));
+ sdsrange(s, 0, 1);
+ ASSERT_EQ(2, sdslen(s));
+ ASSERT_STREQ(s, "12");
+ sdsfree(s);
+}
+
+TEST(MONITOR_SDS, sdscpylen)
+{
+ sds s = sdsnew("1234567890");
+ ASSERT_EQ(10, sdslen(s));
+ sds s1 = sdscpylen(s, "abc", 3);
+ ASSERT_STRNE(s1, "1234567890abc");
+ sdsfree(s1);
+}
+
+TEST(MONITOR_SDS, sdssplitargs)
+{
+ int argc = 0;
+ sds *array = sdssplitargs("a b c d e f g", &argc);
+ ASSERT_EQ(argc, 7);
+ ASSERT_STREQ(array[0], "a");
+ ASSERT_STREQ(array[1], "b");
+ ASSERT_STREQ(array[2], "c");
+ ASSERT_STREQ(array[3], "d");
+ ASSERT_STREQ(array[4], "e");
+ ASSERT_STREQ(array[5], "f");
+ ASSERT_STREQ(array[6], "g");
+ sdsfreesplitres(array, argc);
+
+ array = sdssplitargs("show", &argc);
+ ASSERT_EQ(argc, 1);
+ ASSERT_STREQ(array[0], "show");
+ sdsfreesplitres(array, argc);
+
+ array = sdssplitargs("show ", &argc);
+ ASSERT_EQ(argc, 1);
+ ASSERT_STREQ(array[0], "show");
+ sdsfreesplitres(array, argc);
+
+ array = sdssplitargs("", &argc);
+ ASSERT_EQ(argc, 0);
+ sdsfreesplitres(array, argc);
+}
+
+TEST(MONITOR_SDS, sdssplitlen)
+{
+ int count = 0;
+ const char *sep = "&";
+ const char *request_url = "key1=value1&key2=value2&key3=value3";
+ sds *array = sdssplitlen(request_url, strlen(request_url), sep, 1, &count);
+ ASSERT_EQ(count, 3);
+ ASSERT_STREQ(array[0], "key1=value1");
+ ASSERT_STREQ(array[1], "key2=value2");
+ ASSERT_STREQ(array[2], "key3=value3");
+
+ int key1_cnt;
+ sds *key1_array = sdssplitlen(array[0], sdslen(array[0]), "=", 1, &key1_cnt);
+ ASSERT_EQ(key1_cnt, 2);
+ ASSERT_STREQ(key1_array[0], "key1");
+ ASSERT_STREQ(key1_array[1], "value1");
+ sdsfreesplitres(key1_array, key1_cnt);
+
+ sdsfreesplitres(array, count);
+}
+
+TEST(MONITOR_SDS, sdsIncrLen)
+{
+ sds s = sdsnew("hello, world!\r\n");
+ ASSERT_EQ(sdslen(s), 15);
+ sdsIncrLen(s, -2);
+ ASSERT_EQ(sdslen(s), 13);
+ EXPECT_STREQ(s, "hello, world!");
+ sdsfree(s);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/test/monitor/gtest_seek_layer.cpp b/test/monitor/gtest_seek_layer.cpp
new file mode 100644
index 0000000..ca4f38a
--- /dev/null
+++ b/test/monitor/gtest_seek_layer.cpp
@@ -0,0 +1,142 @@
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <pcap/pcap.h>
+#include <gtest/gtest.h>
+#include "stellar/packet.h"
+#include "monitor/monitor_private.h"
+#include "stellar/packet.h"
+#include "packet_manager/packet_parser.h"
+#include "packet_manager/packet_internal.h"
+
+/* mock */
+int packet_manager_subscribe(UNUSED struct packet_manager *pkt_mgr, UNUSED enum packet_stage stage, UNUSED on_packet_stage_callback *cb, UNUSED void *args)
+{
+ assert(0);
+ return 0;
+}
+
+static int check_layer(const struct packet *pkt, const enum layer_proto *layers, int expect_layer_cnt)
+{
+ int actual_pkt_layer = packet_get_layer_count(pkt);
+ if (actual_pkt_layer != expect_layer_cnt)
+ {
+ return -1;
+ }
+ for (int i = 0; i < expect_layer_cnt; i++)
+ {
+ const struct layer *layer = packet_get_layer_by_idx(pkt, i);
+ if (layer == NULL)
+ {
+ return -1;
+ }
+ if (layer->proto != layers[i])
+ {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+TEST(MONITOR_SEEK_LAYER, ethernet_ipv4_udp_dns)
+{
+ struct packet pkt = {};
+ /* a DNS query packet */
+ const unsigned char packet_bytes[] =
+ {
+ 0xe8, 0x1c, 0xba, 0xcc, 0x87, 0x21, 0x7c, 0x2a,
+ 0x31, 0x9f, 0x98, 0x2c, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3e, 0x4a, 0x41, 0x00, 0x00, 0x80, 0x11,
+ 0x26, 0x7a, 0xc0, 0xa8, 0x24, 0x67, 0x72, 0x72,
+ 0x72, 0x72, 0xd5, 0xbc, 0x00, 0x35, 0x00, 0x2a,
+ 0xd7, 0x33, 0xdf, 0x96, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x66,
+ 0x75, 0x77, 0x75, 0x04, 0x62, 0x64, 0x70, 0x66,
+ 0x03, 0x6f, 0x72, 0x67, 0x02, 0x63, 0x6e, 0x00,
+ 0x00, 0x01, 0x00, 0x01};
+
+ ASSERT_TRUE(packet_parse(&pkt, (char *)packet_bytes, sizeof(packet_bytes)) != NULL);
+ int pkt_layer = packet_get_layer_count(&pkt);
+ ASSERT_EQ(pkt_layer, 3);
+ enum layer_proto layers[3] = {LAYER_PROTO_ETHER, LAYER_PROTO_IPV4, LAYER_PROTO_UDP};
+ ASSERT_EQ(0, check_layer(&pkt, layers, 3));
+}
+
+TEST(MONITOR_SEEK_LAYER, ethernet_ipv4_gre_ipv4_gre_ipv4_udp_dns)
+{
+ struct packet pkt = {};
+ /* a DNS query packet */
+ const unsigned char packet_bytes[] = {
+ 0x02, 0x00, 0x00, 0x01, 0x02, 0x95, 0x00, 0x00,
+ 0xc3, 0x51, 0x05, 0x0f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x7c, 0x46, 0xdb, 0x00, 0x00, 0xfb, 0x2f,
+ 0xcc, 0x35, 0x73, 0x99, 0x4b, 0xf6, 0x7a, 0x70,
+ 0x72, 0x42, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x03, 0x84, 0x45, 0x00, 0x00, 0x60, 0xc8, 0xd5,
+ 0x00, 0x00, 0xff, 0x2f, 0xe9, 0x58, 0x0a, 0x96,
+ 0xfa, 0x0a, 0x0a, 0x96, 0xfa, 0x09, 0x00, 0x00,
+ 0x08, 0x00, 0x45, 0x00, 0x00, 0x48, 0xb2, 0xc6,
+ 0x00, 0x00, 0x3f, 0x11, 0x6a, 0x4b, 0x7a, 0x70,
+ 0x72, 0x49, 0x73, 0xee, 0xfd, 0xeb, 0x51, 0x13,
+ 0x00, 0x35, 0x00, 0x34, 0x3a, 0xbf, 0xb1, 0xba,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x75, 0x72, 0x77, 0x70, 0x75,
+ 0x6c, 0x65, 0x70, 0x03, 0x77, 0x77, 0x77, 0x09,
+ 0x64, 0x65, 0x73, 0x68, 0x65, 0x6e, 0x67, 0x32,
+ 0x38, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01,
+ 0x00, 0x01};
+
+ ASSERT_TRUE(packet_parse(&pkt, (char *)packet_bytes, sizeof(packet_bytes)) != NULL);
+ int pkt_layer = packet_get_layer_count(&pkt);
+ ASSERT_EQ(pkt_layer, 7);
+ enum layer_proto layers[7] = {LAYER_PROTO_ETHER, LAYER_PROTO_IPV4, LAYER_PROTO_GRE, LAYER_PROTO_IPV4, LAYER_PROTO_GRE, LAYER_PROTO_IPV4, LAYER_PROTO_UDP};
+ ASSERT_EQ(0, check_layer(&pkt, layers, 7));
+}
+
+extern "C" u_int stm_bpf_filter_greedy(const struct bpf_insn *bpf_dlt_raw, struct packet *pkt);
+TEST(MONITOR_SEEK_LAYER, ethernet_ipv4_gre_ipv4_gre_ipv4_udp_dns_bpf_filter_greedy)
+{
+ struct packet pkt = {};
+ /* a DNS query packet */
+ const unsigned char packet_bytes[] = {
+ 0x02, 0x00, 0x00, 0x01, 0x02, 0x95, 0x00, 0x00,
+ 0xc3, 0x51, 0x05, 0x0f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x7c, 0x46, 0xdb, 0x00, 0x00, 0xfb, 0x2f,
+ 0xcc, 0x35, 0x73, 0x99, 0x4b, 0xf6, 0x7a, 0x70,
+ 0x72, 0x42, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x03, 0x84, 0x45, 0x00, 0x00, 0x60, 0xc8, 0xd5,
+ 0x00, 0x00, 0xff, 0x2f, 0xe9, 0x58, 0x0a, 0x96,
+ 0xfa, 0x0a, 0x0a, 0x96, 0xfa, 0x09, 0x00, 0x00,
+ 0x08, 0x00, 0x45, 0x00, 0x00, 0x48, 0xb2, 0xc6,
+ 0x00, 0x00, 0x3f, 0x11, 0x6a, 0x4b, 0x7a, 0x70,
+ 0x72, 0x49, 0x73, 0xee, 0xfd, 0xeb, 0x51, 0x13,
+ 0x00, 0x35, 0x00, 0x34, 0x3a, 0xbf, 0xb1, 0xba,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x75, 0x72, 0x77, 0x70, 0x75,
+ 0x6c, 0x65, 0x70, 0x03, 0x77, 0x77, 0x77, 0x09,
+ 0x64, 0x65, 0x73, 0x68, 0x65, 0x6e, 0x67, 0x32,
+ 0x38, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01,
+ 0x00, 0x01};
+
+ ASSERT_TRUE(packet_parse(&pkt, (char *)packet_bytes, sizeof(packet_bytes)) != NULL);
+
+ struct bpf_program bpf_bin;
+ pcap_compile_nopcap(65535, DLT_RAW, &bpf_bin, "host 115.153.75.246 and host 122.112.114.66", 1, 0); // firset ipv4 layer
+ ASSERT_TRUE(0 != stm_bpf_filter_greedy(bpf_bin.bf_insns, &pkt));
+ pcap_freecode(&bpf_bin);
+
+ pcap_compile_nopcap(65535, DLT_RAW, &bpf_bin, "host 10.150.250.10 and host 10.150.250.9", 1, 0); // second ipv4 layer
+ ASSERT_TRUE(0 != stm_bpf_filter_greedy(bpf_bin.bf_insns, &pkt));
+ pcap_freecode(&bpf_bin);
+
+ pcap_compile_nopcap(65535, DLT_RAW, &bpf_bin, "host 122.112.114.73 and host 115.238.253.235", 1, 0); // third ipv4 layer
+ ASSERT_TRUE(0 != stm_bpf_filter_greedy(bpf_bin.bf_insns, &pkt));
+ pcap_freecode(&bpf_bin);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/test/monitor/gtest_spinlock.cpp b/test/monitor/gtest_spinlock.cpp
new file mode 100644
index 0000000..6bf2aa7
--- /dev/null
+++ b/test/monitor/gtest_spinlock.cpp
@@ -0,0 +1,49 @@
+#include <unistd.h>
+#include <pthread.h>
+#include <getopt.h>
+#include <gtest/gtest.h>
+#include "monitor/monitor_private.h"
+
+static volatile long sum = 0;
+static volatile long barrier = 1;
+#define CALC_NUM 10000000
+static void *calc_thread(void *arg)
+{
+ stm_spinlock *lock = (stm_spinlock *)arg;
+ (void)lock;
+ while (barrier)
+ ;
+ for (int i = 0; i < CALC_NUM; i++)
+ {
+ stm_spinlock_lock(lock);
+ sum++;
+ stm_spinlock_unlock(lock);
+ }
+
+ return NULL;
+}
+
+TEST(MONITOR_SPINLOCK, base)
+{
+ pthread_t pid;
+ stm_spinlock *lock = stm_spinlock_new();
+ pthread_create(&pid, NULL, calc_thread, (void *)lock);
+ usleep(5000);
+ barrier = 0;
+ for (int i = 0; i < CALC_NUM; i++)
+ {
+ stm_spinlock_lock(lock);
+ sum++;
+ stm_spinlock_unlock(lock);
+ }
+ pthread_join(pid, NULL);
+ stm_spinlock_free(lock);
+ EXPECT_EQ(sum, CALC_NUM * 2);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/test/monitor/gtest_topk.cpp b/test/monitor/gtest_topk.cpp
new file mode 100644
index 0000000..ec6c2f3
--- /dev/null
+++ b/test/monitor/gtest_topk.cpp
@@ -0,0 +1,535 @@
+#include <assert.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <sys/time.h>
+#include <gtest/gtest.h>
+#include <arpa/inet.h>
+// #include <fieldstat.h>
+#include <fieldstat/fieldstat_exporter.h>
+
+#define MAX_TOPK_CELL_NUM 100
+#define MAX_TOPK_METRIC_NUM 255
+#define SHOW_TOPK_RESULT_NUM 10
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define TOPK_DIMENSION_NUM 8
+static const char *g_topk_dimension[TOPK_DIMENSION_NUM] = {
+ "top_client_ip",
+ "top_server_ip",
+ "top_internal_ip",
+ "top_external_ip",
+ "top_server_fqdn",
+ "top_server_domain",
+ "top_client_country",
+ "top_server_country",
+};
+
+#define TOPK_RANK_BY_NUM 3
+static const char *g_topk_rank_by[TOPK_RANK_BY_NUM] = {
+ "sessions",
+ "packets",
+ "bytes",
+};
+
+#define TOPK_METRIC_NUM 3
+/* index corresponding to g_topk_rank_by[] */
+static const char *g_metric_name[TOPK_METRIC_NUM] = {
+ "sessions",
+ "pkts",
+ "bytes",
+};
+struct topk_t
+{
+ char name[128];
+ long long value;
+};
+
+static int g_topk_cubeid[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM] = {}; // [dimension][rank_by]
+static int g_topk_counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM] = {}; // [dimension][rank_by][metric]
+
+static struct fieldstat *g_fs4_ins = NULL;
+
+/* the biggest value index is 0 */
+static int qsort_topk_value_cmp_cb(const void *a, const void *b)
+{
+ struct topk_t *la = (struct topk_t *)a;
+ struct topk_t *lb = (struct topk_t *)b;
+ return (int)(lb->value - la->value);
+}
+
+// lijia temp add for test
+
+static void show_topk_stat(const char *dimension, const char *rank_by, struct topk_t *topk_result, size_t n_result, size_t topk)
+{
+
+ if (n_result)
+ {
+ printf("### top '%s' rank_by '%s' result %zu: \n", dimension, rank_by, topk);
+ qsort((void *)topk_result, n_result, sizeof(struct topk_t), qsort_topk_value_cmp_cb);
+ int show_num = MIN(topk, n_result);
+ for (int i = 0; i < show_num; i++)
+ {
+ printf("%2d) %16s\t%8lld\n", i + 1, topk_result[i].name, topk_result[i].value);
+ }
+ }
+}
+
+static int get_topk_cube_id(struct fieldstat *fs4_ins, const char *dimension, const char *rank_by)
+{
+ struct field find_cube_tmp[2] = {};
+ find_cube_tmp[0].key = "name";
+ find_cube_tmp[0].type = FIELD_VALUE_CSTRING;
+ find_cube_tmp[0].value_str = dimension;
+ find_cube_tmp[1].key = "rank_by";
+ find_cube_tmp[1].type = FIELD_VALUE_CSTRING;
+ find_cube_tmp[1].value_str = rank_by;
+ return fieldstat_find_cube(fs4_ins, find_cube_tmp, 2);
+}
+
+/*
+ dimensions: client_ip, server_ip, internal_ip, external_ip, server_fqdn, server_domain, client_country, server_country
+ rank_by: sessions, bytes, packets
+ metric: sessions, bytes, packets
+*/
+static int get_topk_rank_by(struct fieldstat *fs4_ins, const char *dimension, const char *rank_by, const char *metric_name, struct topk_t *topk_array, size_t max_array_num)
+{
+ int cube_id = get_topk_cube_id(fs4_ins, dimension, rank_by);
+ assert(cube_id >= 0);
+
+ int metric_id = fieldstat_cube_get_metric_id_by_name(fs4_ins, cube_id, metric_name);
+ assert(metric_id >= 0);
+
+ struct field_list *cell_dimensions_list = NULL;
+ size_t n_cell = 0;
+ fieldstat_cube_get_cells(fs4_ins, cube_id, &cell_dimensions_list, &n_cell);
+ if (NULL == cell_dimensions_list)
+ {
+ return 0;
+ }
+ size_t result_min = MIN(max_array_num, n_cell);
+ for (size_t ce = 0; ce < result_min; ce++)
+ {
+ strncpy(topk_array[ce].name, cell_dimensions_list[ce].field->value_str, sizeof(topk_array[ce].name));
+ fieldstat_counter_get(fs4_ins, cube_id, &cell_dimensions_list[ce], metric_id, &topk_array[ce].value);
+ }
+ fieldstat_field_list_arr_free(cell_dimensions_list, n_cell);
+ return result_min;
+}
+
+static void stm_topk_register_cube(struct fieldstat *fs4_ins, int cube_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM])
+{
+ struct field reg_cube_tmp[2] = {};
+ for (size_t i = 0; i < sizeof(g_topk_dimension) / sizeof(g_topk_dimension[0]); i++)
+ {
+ for (size_t j = 0; j < sizeof(g_topk_rank_by) / sizeof(g_topk_rank_by[0]); j++)
+ {
+ reg_cube_tmp[0].key = "name";
+ reg_cube_tmp[0].type = FIELD_VALUE_CSTRING;
+ reg_cube_tmp[0].value_str = g_topk_dimension[i];
+ reg_cube_tmp[1].key = "rank_by";
+ reg_cube_tmp[1].type = FIELD_VALUE_CSTRING;
+ reg_cube_tmp[1].value_str = g_topk_rank_by[j];
+ cube_id[i][j] = fieldstat_cube_create(fs4_ins, reg_cube_tmp, 2);
+ assert(cube_id[i][j] >= 0);
+ }
+ }
+}
+
+static void stm_topk_register_counter(struct fieldstat *fs4_ins, int cube_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM],
+ int counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM])
+{
+ for (size_t i = 0; i < sizeof(g_topk_dimension) / sizeof(g_topk_dimension[0]); i++)
+ {
+ for (size_t j = 0; j < sizeof(g_topk_rank_by) / sizeof(g_topk_rank_by[0]); j++)
+ {
+ for (size_t k = 0; k < sizeof(g_metric_name) / sizeof(char *); k++)
+ {
+ counter_id[i][j][k] = fieldstat_register_counter(fs4_ins, cube_id[i][j], g_metric_name[k]);
+ assert(counter_id[i][j][k] >= 0);
+ }
+ }
+ }
+}
+
+static void stm_topk_set_cube_sampling(struct fieldstat *fs4_ins, enum sampling_mode mode, int cube_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM],
+ int counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM])
+{
+ for (size_t i = 0; i < sizeof(g_topk_dimension) / sizeof(g_topk_dimension[0]); i++)
+ {
+ for (size_t j = 0; j < sizeof(g_topk_rank_by) / sizeof(g_topk_rank_by[0]); j++)
+ {
+ fieldstat_cube_set_sampling(fs4_ins, cube_id[i][j], mode, MAX_TOPK_CELL_NUM, counter_id[i][j][j]);
+ }
+ }
+}
+
+static int stm_topk_client_ip_rankby_sessions(void)
+{
+ struct field ip_tag = {};
+ ip_tag.key = "top_client_ip";
+ ip_tag.type = FIELD_VALUE_CSTRING;
+ unsigned int begin_ip_addr = 0x04030201;
+ struct topk_t benchmark_result[MAX_TOPK_METRIC_NUM];
+ long long max_value = 0;
+ memset(benchmark_result, 0, sizeof(benchmark_result));
+ for (int i = 0; i < MAX_TOPK_METRIC_NUM; i++)
+ {
+ benchmark_result[i].value = rand() % 10000;
+ if (benchmark_result[i].value > max_value)
+ {
+ max_value = benchmark_result[i].value;
+ }
+ unsigned int ip_addr_net = htonl(begin_ip_addr + i);
+ inet_ntop(AF_INET, &ip_addr_net, benchmark_result[i].name, sizeof(benchmark_result[i].name));
+ ip_tag.value_str = benchmark_result[i].name;
+ fieldstat_counter_incrby(g_fs4_ins, g_topk_cubeid[0][0], g_topk_counter_id[0][0][0], &ip_tag, 1, benchmark_result[i].value);
+ }
+
+ /*** query and verify ***/
+ struct topk_t fs4_topk_result[MAX_TOPK_CELL_NUM] = {};
+ int num = get_topk_rank_by(g_fs4_ins, "top_client_ip", "sessions", "sessions", fs4_topk_result, sizeof(fs4_topk_result) / sizeof(struct topk_t));
+ assert(num > 0);
+ printf("topk top_client_ip rankby sessions, max value:%lld\n", max_value);
+ show_topk_stat("top_client_ip", "sessions", fs4_topk_result, num, SHOW_TOPK_RESULT_NUM);
+ qsort((void *)benchmark_result, MAX_TOPK_METRIC_NUM, sizeof(struct topk_t), qsort_topk_value_cmp_cb);
+ assert(max_value == fs4_topk_result[0].value);
+ return memcmp(fs4_topk_result, &benchmark_result[0], SHOW_TOPK_RESULT_NUM);
+}
+
+static int stm_topk_server_ip_rankby_packets(void)
+{
+ struct field ip_tag = {};
+ ip_tag.key = "top_server_ip";
+ ip_tag.type = FIELD_VALUE_CSTRING;
+ unsigned int begin_ip_addr = 0x64640101;
+ long long max_value = 0;
+ struct topk_t benchmark_result[MAX_TOPK_METRIC_NUM];
+ memset(benchmark_result, 0, sizeof(benchmark_result));
+ for (int i = 0; i < MAX_TOPK_METRIC_NUM; i++)
+ {
+ benchmark_result[i].value = rand() % 10000;
+ if (benchmark_result[i].value > max_value)
+ {
+ max_value = benchmark_result[i].value;
+ }
+ unsigned int ip_addr_net = htonl(begin_ip_addr + i);
+ inet_ntop(AF_INET, &ip_addr_net, benchmark_result[i].name, sizeof(benchmark_result[i].name));
+ ip_tag.value_str = benchmark_result[i].name;
+ fieldstat_counter_incrby(g_fs4_ins, g_topk_cubeid[1][1], g_topk_counter_id[1][1][1], &ip_tag, 1, benchmark_result[i].value);
+ }
+
+ /*** query and verify ***/
+ struct topk_t fs4_topk_result[MAX_TOPK_CELL_NUM] = {};
+ int num = get_topk_rank_by(g_fs4_ins, "top_server_ip", "packets", "pkts", fs4_topk_result, sizeof(fs4_topk_result) / sizeof(struct topk_t));
+ assert(num > 0);
+ printf("topk top_server_ip rankby packets, max value:%lld\n", max_value);
+ show_topk_stat("top_server_ip", "packets", fs4_topk_result, num, SHOW_TOPK_RESULT_NUM);
+ qsort((void *)benchmark_result, MAX_TOPK_METRIC_NUM, sizeof(struct topk_t), qsort_topk_value_cmp_cb);
+ assert(max_value == fs4_topk_result[0].value);
+ return memcmp(fs4_topk_result, &benchmark_result[0], SHOW_TOPK_RESULT_NUM);
+}
+
+static int stm_topk_server_domain_rankby_bytes(void)
+{
+ struct field domain_tag = {};
+ domain_tag.key = "top_server_domain";
+ domain_tag.type = FIELD_VALUE_CSTRING;
+ struct topk_t benchmark_result[MAX_TOPK_METRIC_NUM];
+ memset(benchmark_result, 0, sizeof(benchmark_result));
+ for (int i = 0; i < MAX_TOPK_METRIC_NUM; i++)
+ {
+ snprintf(benchmark_result[i].name, sizeof(benchmark_result[i].name), "www.abcd.com");
+ }
+
+ for (int i = 0; i < MAX_TOPK_METRIC_NUM; i++)
+ {
+ benchmark_result[i].value = rand() % 10000;
+ benchmark_result[i].name[4] = rand() % 26 + 'a';
+ benchmark_result[i].name[5] = rand() % 26 + 'a';
+ benchmark_result[i].name[6] = rand() % 26 + 'a';
+ benchmark_result[i].name[7] = rand() % 26 + 'a';
+ domain_tag.value_str = benchmark_result[i].name;
+ fieldstat_counter_incrby(g_fs4_ins, g_topk_cubeid[5][2], g_topk_counter_id[5][2][2], &domain_tag, 1, benchmark_result[i].value);
+ }
+
+ /*** query and verify ***/
+ struct topk_t fs4_topk_result[SHOW_TOPK_RESULT_NUM] = {};
+ int num = get_topk_rank_by(g_fs4_ins, "top_server_domain", "bytes", "bytes", fs4_topk_result, sizeof(fs4_topk_result) / sizeof(struct topk_t));
+ assert(num > 0);
+ show_topk_stat("top_server_domain", "bytes", fs4_topk_result, num, SHOW_TOPK_RESULT_NUM);
+ qsort((void *)benchmark_result, MAX_TOPK_METRIC_NUM, sizeof(struct topk_t), qsort_topk_value_cmp_cb);
+ return memcmp(fs4_topk_result, &benchmark_result[0], SHOW_TOPK_RESULT_NUM);
+}
+
+class MONITOR_TOPK : public testing::Test
+{
+protected:
+ static void SetUpTestCase()
+ {
+ srand(time(NULL));
+ g_fs4_ins = fieldstat_new();
+ assert(g_fs4_ins != NULL);
+
+ stm_topk_register_cube(g_fs4_ins, g_topk_cubeid);
+ stm_topk_register_counter(g_fs4_ins, g_topk_cubeid, g_topk_counter_id);
+ stm_topk_set_cube_sampling(g_fs4_ins, SAMPLING_MODE_TOPK, g_topk_cubeid, g_topk_counter_id);
+ }
+ static void TearDownTestCase()
+ {
+ fieldstat_reset(g_fs4_ins);
+ fieldstat_free(g_fs4_ins);
+ }
+};
+
+static void generate_random_counter(struct fieldstat *fs4_ins, int cube_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM],
+ int counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM])
+{
+ struct field tag = {};
+ tag.type = FIELD_VALUE_CSTRING;
+ tag.value_str = "random";
+
+ for (size_t i = 0; i < sizeof(g_topk_dimension) / sizeof(g_topk_dimension[0]); i++)
+ {
+ for (size_t j = 0; j < sizeof(g_topk_rank_by) / sizeof(g_topk_rank_by[0]); j++)
+ {
+ for (size_t k = 0; k < sizeof(g_metric_name) / sizeof(char *); k++)
+ {
+ assert(g_topk_counter_id[i][j][k] >= 0);
+ tag.key = g_topk_dimension[i];
+ fieldstat_counter_incrby(fs4_ins, cube_id[i][j], counter_id[i][j][k], &tag, 1, random());
+ }
+ }
+ }
+}
+
+char **show_topk_get_metrics_name(struct fieldstat *show_fs4_ins, int *metric_array_num)
+{
+ int *cube_id_array = NULL;
+ int n_cube = 0;
+ fieldstat_get_cubes(show_fs4_ins, &cube_id_array, &n_cube);
+ if (NULL == cube_id_array)
+ {
+ return NULL;
+ }
+
+ struct field_list *cell_dimensions_list = NULL;
+ size_t n_cell = 0;
+ fieldstat_cube_get_cells(show_fs4_ins, cube_id_array[0], &cell_dimensions_list, &n_cell);
+ if (cell_dimensions_list == NULL)
+ {
+ return NULL;
+ }
+
+ /* get metric name of any valid cube_id */
+ int *metric_id_out;
+ size_t n_metric;
+ char **metric_array = NULL;
+ fieldstat_cell_get_metrics(show_fs4_ins, cube_id_array[0], cell_dimensions_list, &metric_id_out, &n_metric);
+ if (n_metric == 0)
+ {
+ fieldstat_field_list_arr_free(cell_dimensions_list, n_cell);
+ return NULL;
+ }
+ metric_array = (char **)malloc(sizeof(char *) * n_metric);
+ for (size_t j = 0; j < n_metric; j++)
+ {
+ metric_array[j] = strdup(fieldstat_metric_get_name(show_fs4_ins, cube_id_array[0], metric_id_out[j]));
+ }
+ fieldstat_field_list_arr_free(cell_dimensions_list, n_cell);
+
+ *metric_array_num = n_metric;
+ return metric_array;
+}
+
+TEST(MONITOR, show_all_valid_metrics)
+{
+ struct fieldstat *show_fs4_ins = fieldstat_new();
+ int show_topk_cubeid[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM] = {}; // [dimension][rank_by]
+ int show_topk_counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM] = {}; // [dimension][rank_by][metric]
+
+ stm_topk_register_cube(show_fs4_ins, show_topk_cubeid);
+ stm_topk_register_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
+ stm_topk_set_cube_sampling(show_fs4_ins, SAMPLING_MODE_TOPK, show_topk_cubeid, show_topk_counter_id);
+
+ generate_random_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
+ int metric_array_num = 0;
+ char **metric_array = show_topk_get_metrics_name(show_fs4_ins, &metric_array_num);
+ EXPECT_TRUE(metric_array_num > 0);
+ EXPECT_TRUE(metric_array != NULL);
+
+ for (int i = 0; i < metric_array_num; i++)
+ {
+ printf("metric name: %s\n", metric_array[i]);
+ free(metric_array[i]);
+ }
+ free(metric_array);
+
+ fieldstat_free(show_fs4_ins);
+}
+
+static int charchar_array_exist(char **dimension_array, int dimension_array_num, const char *expect_name)
+{
+ for (int i = 0; i < dimension_array_num && dimension_array[i] != NULL; i++)
+ {
+ if (0 == strcmp(dimension_array[i], expect_name))
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+char **show_topk_get_dimensions_name(struct fieldstat *show_fs4_ins, int *dimension_array_result)
+{
+ int *cube_id_array = NULL;
+ int n_cube = 0;
+ fieldstat_get_cubes(show_fs4_ins, &cube_id_array, &n_cube);
+ if (0 == n_cube)
+ {
+ return NULL;
+ }
+ char **dimension_array = NULL;
+ int dimension_array_num = 0;
+
+ struct field_list *cell_dimensions_list = NULL;
+ for (int i = 0; i < n_cube; i++)
+ {
+ size_t n_cell = 0;
+ fieldstat_cube_get_cells(show_fs4_ins, cube_id_array[i], &cell_dimensions_list, &n_cell);
+ if (NULL == cell_dimensions_list)
+ {
+ continue;
+ }
+ for (size_t j = 0; j < cell_dimensions_list->n_field; j++)
+ {
+ if (0 == charchar_array_exist(dimension_array, dimension_array_num, cell_dimensions_list->field[j].key))
+ {
+ dimension_array = (char **)realloc(dimension_array, sizeof(char *) * (dimension_array_num + 1));
+ dimension_array[dimension_array_num] = strdup(cell_dimensions_list->field[j].key);
+ dimension_array_num++;
+ }
+ }
+ fieldstat_field_list_arr_free(cell_dimensions_list, n_cell);
+ }
+
+ *dimension_array_result = dimension_array_num;
+ return dimension_array;
+}
+
+TEST(MONITOR, show_all_valid_dimensions)
+{
+ struct fieldstat *show_fs4_ins = fieldstat_new();
+ int show_topk_cubeid[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM] = {}; // [dimension][rank_by]
+ int show_topk_counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM] = {}; // [dimension][rank_by][metric]
+
+ stm_topk_register_cube(show_fs4_ins, show_topk_cubeid);
+ stm_topk_register_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
+ stm_topk_set_cube_sampling(show_fs4_ins, SAMPLING_MODE_TOPK, show_topk_cubeid, show_topk_counter_id);
+
+ generate_random_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
+
+ int dimension_array_num = 0;
+ char **dimension_array = show_topk_get_dimensions_name(show_fs4_ins, &dimension_array_num);
+ EXPECT_TRUE(dimension_array_num > 0);
+ EXPECT_TRUE(dimension_array != NULL);
+
+ for (int i = 0; i < dimension_array_num; i++)
+ {
+ printf("dimension name: %s\n", dimension_array[i]);
+ free(dimension_array[i]);
+ }
+ free(dimension_array);
+ fieldstat_free(show_fs4_ins);
+}
+
+char **show_topk_get_rankby_name(struct fieldstat *show_fs4_ins, int *rankby_array_result)
+{
+ int *cube_id_array = NULL;
+ int n_cube = 0;
+ fieldstat_get_cubes(show_fs4_ins, &cube_id_array, &n_cube);
+ if (NULL == cube_id_array)
+ {
+ return NULL;
+ }
+ char **rankby_array = NULL;
+ int rankby_array_num = 0;
+
+ for (int i = 0; i < n_cube; i++)
+ {
+ struct field_list *flist = fieldstat_cube_get_dimension(show_fs4_ins, cube_id_array[i]);
+ if (flist == NULL)
+ {
+ continue;
+ }
+ for (size_t j = 0; j < flist->n_field; j++)
+ {
+ if (0 == strcmp(flist->field[j].key, "rank_by"))
+ {
+ if (0 == charchar_array_exist(rankby_array, rankby_array_num, flist->field[j].value_str))
+ {
+ rankby_array = (char **)realloc(rankby_array, sizeof(char *) * (rankby_array_num + 1));
+ rankby_array[rankby_array_num] = strdup(flist->field[j].value_str);
+ rankby_array_num++;
+ }
+ }
+ }
+ fieldstat_field_list_arr_free(flist, 1);
+ }
+ *rankby_array_result = rankby_array_num;
+ return rankby_array;
+}
+
+TEST(MONITOR, show_all_valid_rankby)
+{
+ struct fieldstat *show_fs4_ins = fieldstat_new();
+ int show_topk_cubeid[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM] = {}; // [dimension][rank_by]
+ int show_topk_counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM] = {}; // [dimension][rank_by][metric]
+
+ stm_topk_register_cube(show_fs4_ins, show_topk_cubeid);
+ stm_topk_register_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
+ stm_topk_set_cube_sampling(show_fs4_ins, SAMPLING_MODE_TOPK, show_topk_cubeid, show_topk_counter_id);
+
+ generate_random_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
+
+ int rankby_array_num = 0;
+ char **rankby_array = show_topk_get_rankby_name(show_fs4_ins, &rankby_array_num);
+ EXPECT_TRUE(rankby_array_num > 0);
+ EXPECT_TRUE(rankby_array != NULL);
+
+ for (int i = 0; i < rankby_array_num; i++)
+ {
+ printf("rankby name: %s\n", rankby_array[i]);
+ free(rankby_array[i]);
+ }
+ free(rankby_array);
+
+ fieldstat_free(show_fs4_ins);
+}
+
+TEST_F(MONITOR_TOPK, show_client_ip_by_sessions)
+{
+ EXPECT_EQ(0, stm_topk_client_ip_rankby_sessions());
+}
+
+TEST_F(MONITOR_TOPK, show_server_ip_by_packets)
+{
+ EXPECT_EQ(0, stm_topk_server_ip_rankby_packets());
+}
+
+TEST_F(MONITOR_TOPK, show_server_domain_by_bytes)
+{
+ EXPECT_EQ(0, stm_topk_server_domain_rankby_bytes());
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/test/monitor/monitor_main.cpp b/test/monitor/monitor_main.cpp
new file mode 100644
index 0000000..6e31c7a
--- /dev/null
+++ b/test/monitor/monitor_main.cpp
@@ -0,0 +1,74 @@
+#include <netinet/in.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <gtest/gtest.h>
+#include "monitor/monitor_private.h"
+#include "stellar/packet.h"
+#include "stellar/stellar.h"
+
+/* main function to drives the stellar library to run,
+ using a delay plugin to increase the running time
+*/
+
+static int sleep_per_packet_ms = 0;
+static int sleep_before_run_ms = 0;
+
+static void delay_packet_cb(struct packet *pkt, enum packet_stage stage, void *plugin_env)
+{
+ (void)stage;
+ (void)pkt;
+ (void)plugin_env;
+ if (sleep_per_packet_ms)
+ {
+ fprintf(stderr, "packet dump, sleep %d ms....\n", sleep_per_packet_ms);
+ usleep(1000 * sleep_per_packet_ms);
+ }
+}
+int main(int argc, char const *argv[])
+{
+ if (argc > 1)
+ {
+ if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)
+ {
+ fprintf(stderr, "Usage: %s [sleep_before_run_ms] [sleep_per_packet_ms]\n", argv[0]);
+ exit(0);
+ }
+
+ sleep_before_run_ms = atoi(argv[1]);
+ if (argc >= 3)
+ {
+ sleep_per_packet_ms = atoi(argv[2]);
+ }
+ }
+ struct stellar *st = stellar_new("./conf/stellar.toml");
+ assert(st);
+ if (NULL == st)
+ {
+ fprintf(stderr, "stellar_new error!\n");
+ exit(1);
+ }
+ struct stellar_monitor *stm = stellar_monitor_get();
+ struct module *packet_mgr_mod = module_manager_get_module(stm->mod_mgr_ref, PACKET_MANAGER_MODULE_NAME);
+ struct packet_manager *pkt_mgr = module_to_packet_manager(packet_mgr_mod);
+ int ret = packet_manager_subscribe(pkt_mgr, PACKET_STAGE_INPUT, delay_packet_cb, stm);
+ if (ret < 0)
+ {
+ fprintf(stderr, "packet_manager_subscribe error!\n");
+ exit(1);
+ }
+
+ if (sleep_before_run_ms)
+ {
+ fprintf(stderr, "%s, sleep %d ms before run....\n", argv[0], sleep_before_run_ms);
+ usleep(1000 * sleep_before_run_ms);
+ }
+ fprintf(stderr, "%s starting...\r\n", argv[0]);
+ stellar_run(st);
+ stellar_free(st);
+ return 0;
+}
diff --git a/test/monitor/pcap/monitor_benchmark.pcap b/test/monitor/pcap/monitor_benchmark.pcap
new file mode 100644
index 0000000..c839536
--- /dev/null
+++ b/test/monitor/pcap/monitor_benchmark.pcap
Binary files differ
diff --git a/test/monitor/pcap/monitor_packet_dump.pcap b/test/monitor/pcap/monitor_packet_dump.pcap
new file mode 100644
index 0000000..6aa975d
--- /dev/null
+++ b/test/monitor/pcap/monitor_packet_dump.pcap
Binary files differ
diff --git a/test/monitor/pcap/monitor_tunnel.pcap b/test/monitor/pcap/monitor_tunnel.pcap
new file mode 100644
index 0000000..1492d7c
--- /dev/null
+++ b/test/monitor/pcap/monitor_tunnel.pcap
Binary files differ
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..0b17171
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(monitor)
diff --git a/tools/monitor/CMakeLists.txt b/tools/monitor/CMakeLists.txt
new file mode 100644
index 0000000..768dbff
--- /dev/null
+++ b/tools/monitor/CMakeLists.txt
@@ -0,0 +1,19 @@
+#like redis-cli, use hyphen('-') not underscore('_')
+add_executable(stellar-cli monitor_cli.c)
+target_link_libraries(stellar-cli libevent-static cjson-static linenoise sds ringbuf monitor)
+install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/stellar-cli DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ COMPONENT EXECUTABLE)
+
+# add_subdirectory(stellar-dump)
+
+#tcpdump patch
+set (STELLAR-DUMP-PATCH-FILE ${CMAKE_SOURCE_DIR}/tools/monitor/stellar-dump.patch)
+set (STELLAR-DUMP-PATCH-CMD ${CMAKE_SOURCE_DIR}/tools/monitor/stellar-dump-update.sh)
+ExternalProject_Add(tcpdump PREFIX tcpdump
+ URL ${CMAKE_SOURCE_DIR}/vendors/tcpdump-4.99.4.tar.gz
+ URL_MD5 4f2d4a8a5fab017e5ebda156bfc86378
+ PATCH_COMMAND sh -c "chmod +x ${STELLAR-DUMP-PATCH-CMD} && ${STELLAR-DUMP-PATCH-CMD} ${CMAKE_CURRENT_BINARY_DIR}/tcpdump/src/tcpdump ${STELLAR-DUMP-PATCH-FILE}"
+ CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DWITH_CRYPTO=OFF -DSTELLAR_SOURCE_DIR=${CMAKE_SOURCE_DIR} -DSTELLAR_BINARY_DIR=${CMAKE_BINARY_DIR} -DASAN_OPTION=${ASAN_OPTION}
+ )
+add_dependencies(tcpdump libevent)
+ExternalProject_Get_Property(tcpdump INSTALL_DIR)
+install(PROGRAMS ${INSTALL_DIR}/src/tcpdump-build/stellar-dump DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ COMPONENT EXECUTABLE)
diff --git a/tools/monitor/monitor_cli.c b/tools/monitor/monitor_cli.c
new file mode 100644
index 0000000..c16447b
--- /dev/null
+++ b/tools/monitor/monitor_cli.c
@@ -0,0 +1,557 @@
+#include <arpa/inet.h>
+#include <assert.h>
+#include <evhttp.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "stellar/monitor.h"
+#include "linenoise/linenoise.h"
+#include "monitor_cmd_assistant.h"
+#include "monitor_private.h"
+#include "monitor/monitor_utils.h"
+
+static char g_stellar_cli_prompt[128]; /* prompt pattern: cli@ip:port> */
+static const char *g_stellar_monitor_history_file = ".stellar_cli_history.txt";
+static const char *g_stellar_monitor_version = "stellar-cli v1.0";
+static int g_stm_cli_noninteractive = 0;
+static const char *g_stm_cli_noninteractive_cmd_line = NULL;
+static const char *g_stm_cli_ipaddr_str = STM_SERVER_LISTEN_IP;
+static unsigned short g_stm_cli_port_host = STM_SERVER_LISTEN_PORT;
+static struct libevent_http_client *g_evh_client;
+static struct stm_cmd_assistant *g_stm_cli_aide;
+static int g_stm_cli_connect_timeout = STM_REQUEST_TIMEOUT;
+
+static struct monitor_cli_args g_cli_args[4] = {
+ {"-i", "--ip", 1, 0, NULL},
+ {"-p", "--port", 1, 0, NULL},
+ {"-t", "--timeout", 1, 0, NULL},
+ {"-e", "--exec", 1, 1, NULL},
+};
+
+struct stm_cmd_parser
+{
+ sds raw_cmd_line; // need be free
+ int argc;
+ sds *argv;
+};
+
+struct stm_builtin_cmd_compose
+{
+ const char *cmd_name;
+ monitor_cmd_cb *cmd_cb;
+ const char *description;
+};
+
+struct libevent_http_client
+{
+ struct event_base *base;
+ struct evhttp_connection *conn;
+ struct evhttp_request *req;
+ enum stm_http_response_code response_code;
+ char *response_cstr;
+};
+
+static int (*g_response_handler)(struct libevent_http_client *evh_client);
+
+static void cli_evhttp_free(void)
+{
+ evhttp_connection_free(g_evh_client->conn);
+ event_base_free(g_evh_client->base);
+ FREE(g_evh_client);
+}
+
+static void stm_cli_args_free(struct stm_cmd_parser *cmd_args)
+{
+ if (NULL != cmd_args)
+ {
+ sdsfree(cmd_args->raw_cmd_line);
+ sdsfreesplitres(cmd_args->argv, cmd_args->argc);
+ FREE(cmd_args);
+ }
+}
+
+static struct monitor_reply *stm_cli_builtin_exit(UNUSED struct stellar_monitor *monitor, UNUSED int argc, UNUSED char *argv[], void *arg)
+{
+ for (size_t i = 0; i < sizeof(g_cli_args) / sizeof(struct monitor_cli_args); i++)
+ {
+ sdsfree(g_cli_args[i].value);
+ }
+ stm_cli_args_free((struct stm_cmd_parser *)arg);
+ stm_cmd_assistant_free(g_stm_cli_aide);
+ cli_evhttp_free();
+ exit(0);
+ return NULL;
+}
+
+static void signal_handler(int signo)
+{
+ if (signo == SIGINT)
+ {
+ stm_cli_builtin_exit(NULL, 0, NULL, NULL);
+ }
+}
+
+static struct monitor_reply *stm_cli_builtin_clear(UNUSED struct stellar_monitor *monitor, UNUSED int argc, UNUSED char *argv[], UNUSED void *arg)
+{
+ linenoiseClearScreen();
+ return NULL;
+}
+
+static struct stm_builtin_cmd_compose g_stm_cli_builtin_commands[] = {
+ {"q", stm_cli_builtin_exit, "cause the shell to exit"},
+ {"quit", stm_cli_builtin_exit, "cause the shell to exit"},
+ {"exit", stm_cli_builtin_exit, "cause the shell to exit"},
+ {"clear", stm_cli_builtin_clear, "clear the terminal screen"},
+ {NULL, NULL, NULL}};
+
+static void evhttp_conn_close_cb(UNUSED struct evhttp_connection *conn, UNUSED void *arg)
+{
+ snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt), "not connected>");
+}
+
+static void evhttp_request_error_cb(enum evhttp_request_error errnum, void *arg)
+{
+ (void)arg;
+ switch (errnum)
+ {
+ case EVREQ_HTTP_TIMEOUT:
+ g_evh_client->response_code = STM_HTTP_408_REQUEST_TIMEOUT;
+ break;
+ case EVREQ_HTTP_INVALID_HEADER:
+ g_evh_client->response_code = STM_HTTP_403_FORBIDDEN;
+ break;
+ case EVREQ_HTTP_DATA_TOO_LONG:
+ g_evh_client->response_code = STM_HTTP_413_PAYLOAD_TOO_LARGE;
+ break;
+ case EVREQ_HTTP_EOF:
+ break;
+ default:
+ break;
+ }
+}
+
+static void evhttp_response_cb(struct evhttp_request *req, void *arg)
+{
+ (void)arg;
+ if (req == NULL)
+ {
+ return;
+ }
+ struct evbuffer *input_buffer = evhttp_request_get_input_buffer(req);
+ size_t evbuf_len = evbuffer_get_length(input_buffer);
+ if (NULL == input_buffer || 0 == evbuf_len)
+ {
+ return;
+ }
+ g_evh_client->response_cstr = (char *)calloc(1, evbuf_len + 1);
+ evbuffer_remove(input_buffer, g_evh_client->response_cstr, evbuf_len);
+ g_evh_client->response_code = STM_HTTP_200_OK;
+ // terminate event_base_dispatch()
+ event_base_loopbreak(g_evh_client->base);
+}
+
+static struct libevent_http_client *evhttp_client_new(const char *server_ip, unsigned short server_port)
+{
+ struct libevent_http_client *evh_client =
+ (struct libevent_http_client *)calloc(1, sizeof(struct libevent_http_client));
+
+ evh_client->base = event_base_new();
+ evh_client->conn = evhttp_connection_base_new(evh_client->base, NULL,
+ server_ip, server_port);
+ evhttp_connection_set_timeout(evh_client->conn, g_stm_cli_connect_timeout);
+ return evh_client;
+}
+
+static int evhttp_client_request_new(struct libevent_http_client *evh_client)
+{
+ evh_client->req = evhttp_request_new(evhttp_response_cb, evh_client);
+ evhttp_request_set_error_cb(evh_client->req, evhttp_request_error_cb);
+ evhttp_connection_set_closecb(evh_client->conn, evhttp_conn_close_cb, evh_client->req);
+ evh_client->response_cstr = NULL;
+ evh_client->response_code = STM_HTTP_204_NO_CONTENT;
+ return 0;
+}
+
+static void evhttp_client_add_header(struct libevent_http_client *evh_client, const char *key, const char *value)
+{
+ struct evkeyvalq *output_headers = evhttp_request_get_output_headers(evh_client->req);
+ evhttp_add_header(output_headers, key, value);
+}
+
+static void evhttp_client_add_uri(struct libevent_http_client *evh_client,
+ enum evhttp_cmd_type type, const char *uri)
+{
+ evhttp_make_request(evh_client->conn, evh_client->req, type, uri);
+}
+
+static int default_response_handler(struct libevent_http_client *evh_client)
+{
+ if (evh_client->response_code != STM_HTTP_200_OK || evh_client->response_cstr == NULL)
+ {
+ snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt), "not connected>");
+ fprintf(stderr, "ERR failed to connect to %s:%u\n",
+ g_stm_cli_ipaddr_str, g_stm_cli_port_host);
+ return -1;
+ }
+
+ printf("%s", evh_client->response_cstr);
+ fflush(stdout);
+ FREE(evh_client->response_cstr);
+ snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt),
+ "cli@%s:%u>", g_stm_cli_ipaddr_str, g_stm_cli_port_host);
+ return 0;
+}
+
+static int command_json_parse_handler(struct libevent_http_client *evh_client)
+{
+ if (evh_client->response_code != STM_HTTP_200_OK || evh_client->response_cstr == NULL)
+ {
+ snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt), "not connected>");
+ fprintf(stderr, "ERR failed to connect to %s:%u\n",
+ g_stm_cli_ipaddr_str, g_stm_cli_port_host);
+ return -1;
+ }
+ if (stm_cmd_assistant_json_load(g_stm_cli_aide, evh_client->response_cstr) < 0)
+ {
+ fprintf(stderr, "ERR failed to synchronize command info with the monitor server\n");
+ return -1;
+ }
+ FREE(evh_client->response_cstr);
+ return 0;
+}
+
+static void stm_cli_usage(void)
+{
+ printf("%s\r\n", g_stellar_monitor_version);
+ printf("Usage:\r\n");
+ printf(" %s [OPTIONS] [ -e command [arg [arg ...]]]\r\n", "stellar-cli");
+ printf("\t%s %-6s %s\r\n", "-i", "--ip", "stellar monitor server ip address");
+ printf("\t%s %-6s %s\r\n", "-p", "--port", "stellar monitor server port");
+ printf("\t%s %-6s %s\r\n", "-e", "--exec", "non-interactive mode, exit after executing command");
+ printf("\t%s %-6s %s\r\n", "-t", "--timeout", "maximum time(sec) allowed for connecting to server");
+ exit(0);
+}
+
+static int stm_cli_exec_builtin_cmd(struct stm_cmd_parser *cmd_args)
+{
+ const char *cli_cmd_name = cmd_args->argv[0];
+ size_t raw_cmd_len = strlen(cli_cmd_name);
+ for (int i = 0; g_stm_cli_builtin_commands[i].cmd_name != NULL; i++)
+ {
+ if (stm_strncasecmp_exactly(g_stm_cli_builtin_commands[i].cmd_name,
+ cli_cmd_name, raw_cmd_len) == 0)
+ {
+ g_stm_cli_builtin_commands[i].cmd_cb(NULL, cmd_args->argc, cmd_args->argv, (void *)cmd_args);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static sds stm_cli_build_RESTful_url(struct stm_cmd_parser *cmd_args)
+{
+ sds url;
+ char restful_path[256] = {0};
+ snprintf(restful_path, sizeof(restful_path), "/%s/%s", STM_RESTFUL_VERSION, STM_RESTFUL_RESOURCE);
+ url = sdsempty();
+ url = sdscatprintf(url, "%s?%s=", restful_path, STM_RESTFUL_URI_CMD_KEY);
+ for (int i = 0; i < cmd_args->argc; i++)
+ {
+ url = sdscat(url, cmd_args->argv[i]);
+ if (i < cmd_args->argc - 1)
+ {
+ url = sdscat(url, " "); // add blank space
+ }
+ }
+ char *encoded_url_str = stm_http_url_encode(url);
+ sds encoded_url = sdsnew(encoded_url_str);
+ sdsfree(url);
+ free(encoded_url_str);
+ return encoded_url;
+}
+
+static int stm_cli_evhttp_run_cmd(struct stm_cmd_parser *cmd_args)
+{
+ evhttp_client_request_new(g_evh_client);
+ evhttp_client_add_header(g_evh_client, "Connection", "keep-alive");
+ evhttp_client_add_header(g_evh_client, "Content-Length", "0");
+ sds url = stm_cli_build_RESTful_url(cmd_args);
+ evhttp_client_add_uri(g_evh_client, EVHTTP_REQ_GET, url);
+ int ret = event_base_dispatch(g_evh_client->base);
+ sdsfree(url);
+ return ret;
+}
+
+/* call remote command use RESTful */
+static int stm_cli_exec_rpc_cmd(UNUSED const char *raw_cmd_line, struct stm_cmd_parser *cmd_args)
+{
+ int ret = stm_cli_evhttp_run_cmd(cmd_args);
+ g_response_handler(g_evh_client);
+ return ret;
+}
+
+static struct stm_cmd_parser *stm_cli_parse_cmd_line(const char *line)
+{
+ struct stm_cmd_parser *cmd_args =
+ (struct stm_cmd_parser *)calloc(1, sizeof(struct stm_cmd_parser));
+ cmd_args->raw_cmd_line = sdsnew(line);
+ cmd_args->argv = sdssplitargs(line, &cmd_args->argc);
+ return cmd_args;
+}
+
+static void stm_cli_exec_cmd(const char *raw_line)
+{
+ struct stm_cmd_parser *cmd_args = stm_cli_parse_cmd_line(raw_line);
+ if (stm_cli_exec_builtin_cmd(cmd_args))
+ {
+ goto fun_exit;
+ }
+ stm_cli_exec_rpc_cmd(raw_line, cmd_args);
+
+fun_exit:
+ stm_cli_args_free(cmd_args);
+ return;
+}
+
+static int stm_cli_builtin_help(const char *line)
+{
+ int argc = 0;
+ int is_help_cmd = 0;
+ sds *array = sdssplitargs(line, &argc);
+
+ if (argc != 1)
+ {
+ sdsfreesplitres(array, argc);
+ return 0;
+ }
+ if ((strcasecmp(array[argc - 1], "help") == 0) || (strcasecmp(array[argc - 1], "--help") == 0) || (strcasecmp(array[argc - 1], "-h") == 0) || (strcasecmp(array[argc - 1], "/?") == 0) || (strcasecmp(array[argc - 1], "?") == 0))
+ {
+ is_help_cmd = 1;
+ }
+ if (is_help_cmd == 1)
+ {
+ stm_cli_exec_cmd("show command brief");
+ }
+ sdsfreesplitres(array, argc);
+ return is_help_cmd;
+}
+
+static void stm_cli_register_builtin_cmd(void)
+{
+ for (int i = 0; g_stm_cli_builtin_commands[i].cmd_name != NULL; i++)
+ {
+ stm_cmd_assistant_register_cmd(g_stm_cli_aide,
+ g_stm_cli_builtin_commands[i].cmd_name,
+ g_stm_cli_builtin_commands[i].cmd_cb,
+ NULL, "readonly", "<cr>",
+ g_stm_cli_builtin_commands[i].description);
+ }
+}
+
+static void stm_cli_run(void)
+{
+ char *line;
+ /* Load history from file. The history file is just a plain text file
+ * where entries are separated by newlines. */
+ linenoiseHistoryLoad(g_stellar_monitor_history_file); /* Load the history at startup */
+ /* Non-interactive mode */
+ if (g_stm_cli_noninteractive)
+ {
+ stm_cli_exec_cmd(g_stm_cli_noninteractive_cmd_line);
+ exit(0);
+ }
+
+ /* Synchronize with the monitor server on boot up */
+ g_response_handler = command_json_parse_handler;
+ stm_cli_exec_cmd(STM_CLIENT_SERVER_SYNC_CMD);
+ g_response_handler = default_response_handler;
+
+ /* register builtin command after synchronization */
+ stm_cli_register_builtin_cmd();
+
+ /* Interactive mode */
+ while (1)
+ {
+ line = linenoise(g_stellar_cli_prompt);
+ if (line && strlen(line) > 0)
+ {
+ if (stm_cli_builtin_help(line) == 0)
+ {
+ stm_cli_exec_cmd(line);
+ }
+ fflush(stdout);
+ linenoiseHistoryAdd(line);
+ }
+ FREE(line);
+ }
+}
+
+static const char *stm_cli_short_options = "he:i:p:t:";
+static const struct option stm_cli_long_options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"ip", required_argument, NULL, 'i'},
+ {"port", required_argument, NULL, 'p'},
+ {"exec", required_argument, NULL, 'e'},
+ {"timeout", required_argument, NULL, 't'},
+};
+
+static int stm_cli_check_args(int argc, char *_argv[])
+{
+ int c, ret = 0;
+ char **argv_tmp = CALLOC(char *, argc + 1);
+ for (int i = 0; i < argc; i++)
+ {
+ argv_tmp[i] = _argv[i];
+ }
+ while (1)
+ {
+ c = getopt_long(argc, argv_tmp, stm_cli_short_options, stm_cli_long_options, NULL);
+ if (c == -1)
+ {
+ ret = 0;
+ break;
+ }
+ switch (c)
+ {
+ case 'h':
+ stm_cli_usage();
+ break;
+ case 'i':
+ case 'p':
+ case 'e':
+ case 't':
+ break;
+ case '?': /* invalid or unknown option */
+ ret = -1;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ }
+ FREE(argv_tmp);
+ return ret;
+}
+
+static int stm_cli_evhttp_init(void)
+{
+ g_evh_client = evhttp_client_new(g_stm_cli_ipaddr_str, g_stm_cli_port_host);
+ assert(g_evh_client != NULL);
+ return 0;
+}
+
+static void cli_linenoise_completion_cb(const char *line, linenoiseCompletions *lc)
+{
+ stm_cmd_assistant_input_line(g_stm_cli_aide, line, (void *)lc);
+}
+
+static char *cli_linenoise_hints_cb(const char *line, int *color, int *bold)
+{
+ char *hints = (char *)stm_cmd_assistant_input_line_for_hints(g_stm_cli_aide, line);
+ if (NULL == hints)
+ {
+ return NULL;
+ }
+ sds tmp = sdsnew(" "); // add a blank space before hints, easy to input the next command
+ tmp = sdscat(tmp, hints);
+ *color = STM_CLI_CMD_HINTS_COLOR;
+ *bold = STM_CLI_CMD_HINTS_BOLD;
+ return tmp;
+}
+
+static void cli_linenoise_free_hints_cb(void *arg)
+{
+ sdsfree((sds)arg);
+}
+
+void cli_assistant_completion_cb(void *arg, const char *candidate_completion)
+{
+ linenoiseCompletions *lc = (linenoiseCompletions *)arg;
+ linenoiseAddCompletion(lc, candidate_completion);
+}
+
+static int stm_assistant_init(void)
+{
+ g_stm_cli_aide = stm_cmd_assistant_new();
+ if (NULL == g_stm_cli_aide)
+ {
+ return -1;
+ }
+ /* Set the completion callback. This will be called every time the
+ * user uses the <tab> key. */
+ linenoiseSetCompletionCallback(cli_linenoise_completion_cb);
+ stm_cmd_assistant_set_completion_cb(g_stm_cli_aide, cli_assistant_completion_cb);
+ linenoiseSetHintsCallback(cli_linenoise_hints_cb);
+ linenoiseSetFreeHintsCallback(cli_linenoise_free_hints_cb);
+ return 0;
+}
+
+static int stm_cli_parse_args(int argc, char *argv[])
+{
+ if (stm_cli_check_args(argc, argv) < 0)
+ {
+ return -1;
+ }
+ if (monitor_util_parse_cmd_args(argc, (const char **)argv, g_cli_args,
+ sizeof(g_cli_args) / sizeof(struct monitor_cli_args)) < 0)
+ {
+ return -1;
+ }
+ if (g_cli_args[0].value != NULL)
+ {
+ g_stm_cli_ipaddr_str = g_cli_args[0].value;
+ }
+ int tmp_val;
+ if (g_cli_args[1].value != NULL)
+ {
+ tmp_val = atoi(g_cli_args[1].value);
+ if (tmp_val <= 0 || tmp_val > 65535)
+ {
+ fprintf(stderr, "invalid port: %s\n", g_cli_args[1].value);
+ return -1;
+ }
+ g_stm_cli_port_host = (unsigned short)tmp_val;
+ }
+ if (g_cli_args[2].value != NULL)
+ {
+ tmp_val = atoi(g_cli_args[2].value);
+ if (tmp_val <= 0)
+ {
+ fprintf(stderr, "invalid timeout: %s\n", g_cli_args[2].value);
+ return -1;
+ }
+ g_stm_cli_connect_timeout = tmp_val;
+ }
+ if (g_cli_args[3].value != NULL)
+ {
+ g_stm_cli_noninteractive = 1;
+ g_stm_cli_noninteractive_cmd_line = g_cli_args[3].value;
+ }
+ snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt),
+ "cli@%s:%u>", g_stm_cli_ipaddr_str, g_stm_cli_port_host);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ if (stm_cli_parse_args(argc, argv) < 0)
+ {
+ return -1;
+ }
+ if (stm_assistant_init() < 0)
+ {
+ return -1;
+ }
+ if (stm_cli_evhttp_init() < 0)
+ {
+ return -1;
+ }
+ g_response_handler = default_response_handler;
+ signal(SIGINT, signal_handler);
+ stm_cli_run();
+ return 0;
+}
diff --git a/tools/monitor/stellar-dump-update.sh b/tools/monitor/stellar-dump-update.sh
new file mode 100755
index 0000000..13ff5b7
--- /dev/null
+++ b/tools/monitor/stellar-dump-update.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+SOURCE_DIR=$1
+PATCH_FILE=$2
+PATCH_STATUS=`head $SOURCE_DIR/tcpdump.c | grep "PATCH FOR STELLAR-DUMP"`
+
+if [ -z "$PATCH_STATUS" ]
+then
+ echo "starting patch tcpdump..."
+ patch -N -p1 -d $SOURCE_DIR -i $PATCH_FILE
+else
+ echo "alread patched, skip... "
+fi
diff --git a/tools/monitor/stellar-dump.patch b/tools/monitor/stellar-dump.patch
new file mode 100644
index 0000000..986149a
--- /dev/null
+++ b/tools/monitor/stellar-dump.patch
@@ -0,0 +1,11400 @@
+diff -uNr tcpdump-tcpdump-4.99.4/addrtoname.c tcpdump-for-stellar/addrtoname.c
+--- tcpdump-tcpdump-4.99.4/addrtoname.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/addrtoname.c 2024-10-30 17:34:05.502498703 +0800
+@@ -1323,8 +1323,8 @@
+ {
+ static char buf[128];
+ snprintf(buf, sizeof(buf), "vlan %u, p %u%s",
+- tci & 0xfff,
+- tci >> 13,
++ (uint16_t)(tci & 0xfff),
++ (uint16_t)(tci >> 13),
+ (tci & 0x1000) ? ", DEI" : "");
+ return buf;
+ }
+diff -uNr tcpdump-tcpdump-4.99.4/addrtostr.c tcpdump-for-stellar/addrtostr.c
+--- tcpdump-tcpdump-4.99.4/addrtostr.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/addrtostr.c 2024-10-30 17:34:05.502498703 +0800
+@@ -51,52 +51,62 @@
+ */
+
+ #ifndef IN6ADDRSZ
+-#define IN6ADDRSZ 16 /* IPv6 T_AAAA */
++#define IN6ADDRSZ 16 /* IPv6 T_AAAA */
+ #endif
+
+ #ifndef INT16SZ
+-#define INT16SZ 2 /* word size */
++#define INT16SZ 2 /* word size */
+ #endif
+
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wall"
+ const char *
+-addrtostr (const void *src, char *dst, size_t size)
++addrtostr(const void *src, char *dst, size_t size)
+ {
+- const u_char *srcaddr = (const u_char *)src;
+- const char digits[] = "0123456789";
+- int i;
+- const char *orig_dst = dst;
+-
+- if (size < INET_ADDRSTRLEN) {
+- errno = ENOSPC;
+- return NULL;
+- }
+- for (i = 0; i < 4; ++i) {
+- int n = *srcaddr++;
+- int non_zerop = 0;
+-
+- if (non_zerop || n / 100 > 0) {
+- *dst++ = digits[n / 100];
+- n %= 100;
+- non_zerop = 1;
+- }
+- if (non_zerop || n / 10 > 0) {
+- *dst++ = digits[n / 10];
+- n %= 10;
+- non_zerop = 1;
+- }
+- *dst++ = digits[n];
+- if (i != 3)
+- *dst++ = '.';
++ const u_char *srcaddr = (const u_char *)src;
++ const char digits[] = "0123456789";
++ int i;
++ const char *orig_dst = dst;
++
++ if (size < INET_ADDRSTRLEN)
++ {
++ errno = ENOSPC;
++ return NULL;
++ }
++ for (i = 0; i < 4; ++i)
++ {
++ int n = *srcaddr++;
++ int non_zerop = 0;
++#if 0
++ if (non_zerop || n / 100 > 0)
++#else
++ if (n / 100 > 0) //for cppcheck
++#endif
++ {
++ *dst++ = digits[n / 100];
++ n %= 100;
++ non_zerop = 1;
+ }
+- *dst++ = '\0';
+- return orig_dst;
++ if (non_zerop || n / 10 > 0)
++ {
++ *dst++ = digits[n / 10];
++ n %= 10;
++ non_zerop = 1;
++ }
++ *dst++ = digits[n];
++ if (i != 3)
++ *dst++ = '.';
++ }
++ *dst++ = '\0';
++ return orig_dst;
+ }
++#pragma GCC diagnostic pop
+
+ /*
+ * Convert IPv6 binary address into presentation (printable) format.
+ */
+ const char *
+-addrtostr6 (const void *src, char *dst, size_t size)
++addrtostr6(const void *src, char *dst, size_t size)
+ {
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+@@ -109,57 +119,60 @@
+ char *dp;
+ size_t space_left, added_space;
+ int snprintfed;
+- struct {
++ struct
++ {
+ int base;
+ int len;
+ } best, cur;
+- uint16_t words [IN6ADDRSZ / INT16SZ];
+- int i;
++ uint16_t words[IN6ADDRSZ / INT16SZ];
++ int i;
+
+ /* Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+- words[i] = (srcaddr[2*i] << 8) | srcaddr[2*i + 1];
++ words[i] = (srcaddr[2 * i] << 8) | srcaddr[2 * i + 1];
+
+ best.len = 0;
+ best.base = -1;
+ cur.len = 0;
+- cur.base = -1;
++ cur.base = -1;
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ {
+ if (words[i] == 0)
+ {
+ if (cur.base == -1)
+- cur.base = i, cur.len = 1;
+- else cur.len++;
++ cur.base = i, cur.len = 1;
++ else
++ cur.len++;
+ }
+ else if (cur.base != -1)
+ {
+ if (best.base == -1 || cur.len > best.len)
+- best = cur;
++ best = cur;
+ cur.base = -1;
+ }
+ }
+ if ((cur.base != -1) && (best.base == -1 || cur.len > best.len))
+- best = cur;
++ best = cur;
+ if (best.base != -1 && best.len < 2)
+- best.base = -1;
++ best.base = -1;
+
+ /* Format the result.
+ */
+ dp = dst;
+ space_left = size;
+-#define APPEND_CHAR(c) \
+- { \
+- if (space_left == 0) { \
+- errno = ENOSPC; \
+- return (NULL); \
+- } \
+- *dp++ = c; \
+- space_left--; \
+- }
++#define APPEND_CHAR(c) \
++ { \
++ if (space_left == 0) \
++ { \
++ errno = ENOSPC; \
++ return (NULL); \
++ } \
++ *dp++ = c; \
++ space_left--; \
++ }
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ {
+ /* Are we inside the best run of 0x00's?
+@@ -167,21 +180,21 @@
+ if (best.base != -1 && i >= best.base && i < (best.base + best.len))
+ {
+ if (i == best.base)
+- APPEND_CHAR(':');
++ APPEND_CHAR(':');
+ continue;
+ }
+
+ /* Are we following an initial run of 0x00s or any real hex?
+ */
+ if (i != 0)
+- APPEND_CHAR(':');
++ APPEND_CHAR(':');
+
+ /* Is this address an encapsulated IPv4?
+ */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
+ {
+- if (!addrtostr(srcaddr+12, dp, space_left))
++ if (!addrtostr(srcaddr + 12, dp, space_left))
+ {
+ errno = ENOSPC;
+ return (NULL);
+@@ -191,13 +204,13 @@
+ space_left -= added_space;
+ break;
+ }
+- snprintfed = snprintf (dp, space_left, "%x", words[i]);
++ snprintfed = snprintf(dp, space_left, "%x", words[i]);
+ if (snprintfed < 0)
+- return (NULL);
+- if ((size_t) snprintfed >= space_left)
++ return (NULL);
++ if ((size_t)snprintfed >= space_left)
+ {
+- errno = ENOSPC;
+- return (NULL);
++ errno = ENOSPC;
++ return (NULL);
+ }
+ dp += snprintfed;
+ space_left -= snprintfed;
+@@ -206,7 +219,7 @@
+ /* Was it a trailing run of 0x00's?
+ */
+ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
+- APPEND_CHAR(':');
++ APPEND_CHAR(':');
+ APPEND_CHAR('\0');
+
+ return (dst);
+diff -uNr tcpdump-tcpdump-4.99.4/checksum.c tcpdump-for-stellar/checksum.c
+--- tcpdump-tcpdump-4.99.4/checksum.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/checksum.c 2024-10-30 17:34:05.522498702 +0800
+@@ -37,75 +37,73 @@
+
+ crc_table = []
+ for i in range(256):
+- accum = i << 2
+- for j in range(8):
+- accum <<= 1
+- if accum & 0x400:
+- accum ^= 0x633
+- crc_table.append(accum)
++ accum = i << 2
++ for j in range(8):
++ accum <<= 1
++ if accum & 0x400:
++ accum ^= 0x633
++ crc_table.append(accum)
+
+ for i in range(len(crc_table)/8):
+- for j in range(8):
+- sys.stdout.write("0x%04x, " % crc_table[i*8+j])
+- sys.stdout.write("\n")
++ for j in range(8):
++ sys.stdout.write("0x%04x, " % crc_table[i*8+j])
++ sys.stdout.write("\n")
+
+ */
+ static const uint16_t crc10_table[256] =
+-{
+- 0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
+- 0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
+- 0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
+- 0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
+- 0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
+- 0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
+- 0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
+- 0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
+- 0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
+- 0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
+- 0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
+- 0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
+- 0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
+- 0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
+- 0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
+- 0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
+- 0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
+- 0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
+- 0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
+- 0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
+- 0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
+- 0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
+- 0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
+- 0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
+- 0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
+- 0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
+- 0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
+- 0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
+- 0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
+- 0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
+- 0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
+- 0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
+-};
++ {
++ 0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
++ 0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
++ 0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
++ 0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
++ 0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
++ 0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
++ 0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
++ 0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
++ 0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
++ 0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
++ 0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
++ 0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
++ 0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
++ 0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
++ 0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
++ 0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
++ 0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
++ 0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
++ 0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
++ 0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
++ 0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
++ 0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
++ 0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
++ 0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
++ 0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
++ 0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
++ 0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
++ 0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
++ 0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
++ 0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
++ 0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
++ 0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1};
+
+-static void
+-init_crc10_table(void)
++static int init_crc10_table(void)
+ {
+ #define CRC10_POLYNOMIAL 0x633
+ int i, j;
+ uint16_t accum;
+ uint16_t verify_crc10_table[256];
+
+- for ( i = 0; i < 256; i++ )
++ for (i = 0; i < 256; i++)
+ {
+- accum = ((unsigned short) i << 2);
+- for ( j = 0; j < 8; j++ )
++ accum = ((unsigned short)i << 2);
++ for (j = 0; j < 8; j++)
+ {
+- if ((accum <<= 1) & 0x400) accum ^= CRC10_POLYNOMIAL;
++ if ((accum <<= 1) & 0x400)
++ accum ^= CRC10_POLYNOMIAL;
+ }
+ verify_crc10_table[i] = accum;
+ }
+- assert(memcmp(verify_crc10_table,
+- crc10_table,
+- sizeof(verify_crc10_table)) == 0);
++ assert(memcmp(verify_crc10_table, crc10_table, sizeof(verify_crc10_table)) == 0);
++ return memcmp(verify_crc10_table, crc10_table, sizeof(verify_crc10_table));
+ #undef CRC10_POLYNOMIAL
+ }
+
+@@ -114,21 +112,18 @@
+ {
+ int i;
+
+- for ( i = 0; i < length; i++ )
++ for (i = 0; i < length; i++)
+ {
+- accum = ((accum << 8) & 0x3ff)
+- ^ crc10_table[( accum >> 2) & 0xff]
+- ^ *p++;
++ accum = ((accum << 8) & 0x3ff) ^ crc10_table[(accum >> 2) & 0xff] ^ *p++;
+ }
+ return accum;
+ }
+
+ /* precompute checksum tables */
+-void
+-init_checksum(void) {
++void init_checksum(void)
++{
+
+ init_crc10_table();
+-
+ }
+
+ /*
+@@ -136,7 +131,7 @@
+ * The checksum field of the passed PDU does not need to be reset to zero.
+ */
+ uint16_t
+-create_osi_cksum (const uint8_t *pptr, int checksum_offset, int length)
++create_osi_cksum(const uint8_t *pptr, int checksum_offset, int length)
+ {
+
+ int x;
+@@ -150,15 +145,19 @@
+ c0 = 0;
+ c1 = 0;
+
+- for (idx = 0; idx < length; idx++) {
++ for (idx = 0; idx < length; idx++)
++ {
+ /*
+ * Ignore the contents of the checksum field.
+ */
+ if (idx == checksum_offset ||
+- idx == checksum_offset+1) {
++ idx == checksum_offset + 1)
++ {
+ c1 += c0;
+ pptr++;
+- } else {
++ }
++ else
++ {
+ c0 = c0 + *(pptr++);
+ c1 += c0;
+ }
+@@ -167,20 +166,23 @@
+ c0 = c0 % 255;
+ c1 = c1 % 255;
+
+- mul = (length - checksum_offset)*(c0);
++ mul = (length - checksum_offset) * (c0);
+
+ x = mul - c0 - c1;
+ y = c1 - mul - 1;
+
+- if ( y >= 0 ) y++;
+- if ( x < 0 ) x--;
++ if (y >= 0)
++ y++;
++ if (x < 0)
++ x--;
+
+ x %= 255;
+ y %= 255;
+
+-
+- if (x == 0) x = 255;
+- if (y == 0) y = 255;
++ if (x == 0)
++ x = 255;
++ if (y == 0)
++ y = 255;
+
+ y &= 0x00FF;
+ checksum = ((x << 8) | y);
+diff -uNr tcpdump-tcpdump-4.99.4/CMakeLists.txt tcpdump-for-stellar/CMakeLists.txt
+--- tcpdump-tcpdump-4.99.4/CMakeLists.txt 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/CMakeLists.txt 2024-10-30 17:34:05.478498703 +0800
+@@ -82,7 +82,7 @@
+ #
+ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+
+-set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
++set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
+
+ #
+ # OK, this is a royal pain.
+@@ -206,11 +206,26 @@
+ # Parameters
+ ###################################################################
+
+-option(WITH_SMI "Build with libsmi, if available" ON)
+-option(WITH_CRYPTO "Build with OpenSSL/libressl libcrypto, if available" ON)
++option(WITH_SMI "Build with libsmi, if available" OFF)
++option(WITH_CRYPTO "Build with OpenSSL/libressl libcrypto, if available" OFF)
+ option(WITH_CAPSICUM "Build with Capsicum security functions, if available" ON)
+ option(WITH_CAP_NG "Use libcap-ng, if available" ON)
+ option(ENABLE_SMB "Build with the SMB dissector" OFF)
++option(STELLAR_SOURCE_DIR"stellar src root dir" "")
++option(STELLAR_BINARY_DIR "stellar build root dir" "")
++option(ASAN_OPTION "asan" OFF)
++
++if(ASAN_OPTION MATCHES "ADDRESS")
++ set(CMAKE_C_FLAGS "${CMAKADDRESS} -g -DCMAKE_BUILD_TYPE=Debug -fsanitize=address -fno-omit-frame-pointer")
++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -DCMAKE_BUILD_TYPE=Debug -fsanitize=address -fno-omit-frame-pointer")
++ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
++ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
++elseif(ASAN_OPTION MATCHES "THREAD")
++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -DCMAKE_BUILD_TYPE=Debug -fsanitize=thread -fno-omit-frame-pointer")
++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -DCMAKE_BUILD_TYPE=Debug -fsanitize=thread -fno-omit-frame-pointer")
++ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
++ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
++endif()
+
+ #
+ # String parameters. Neither of them are set, initially; only if the
+@@ -1235,6 +1250,15 @@
+ set(TCPDUMP_SOURCE_LIST_C ${TCPDUMP_SOURCE_LIST_C} missing/dlnames.c)
+ endif((NOT HAVE_PCAP_DATALINK_NAME_TO_VAL) OR (NOT HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION))
+
++set(TCPDUMP_SOURCE_LIST_C ${TCPDUMP_SOURCE_LIST_C} stellar_dump_ctrl_link.c)
++set(TCPDUMP_SOURCE_LIST_C ${TCPDUMP_SOURCE_LIST_C} stellar_dump_data_link.c)
++include_directories(${STELLAR_SOURCE_DIR}/include/)
++include_directories(${STELLAR_SOURCE_DIR}/infra/)
++include_directories(${STELLAR_BINARY_DIR}/vendors/libevent/include/)
++
++link_directories(${STELLAR_BINARY_DIR}/vendors/libevent/lib/)
++link_directories(${STELLAR_BINARY_DIR}/infra/packet_manager/)
++
+ set(PROJECT_SOURCE_LIST_C ${NETDISSECT_SOURCE_LIST_C} ${TCPDUMP_SOURCE_LIST_C})
+
+ file(GLOB PROJECT_SOURCE_LIST_H
+@@ -1297,11 +1321,17 @@
+ # Register targets
+ ######################################
+
+-add_executable(tcpdump ${TCPDUMP_SOURCE_LIST_C})
++# add_executable(tcpdump ${TCPDUMP_SOURCE_LIST_C})
++add_library(tcpdump_stellar ${TCPDUMP_SOURCE_LIST_C})
+ if(NOT C_ADDITIONAL_FLAGS STREQUAL "")
+- set_target_properties(tcpdump PROPERTIES COMPILE_FLAGS ${C_ADDITIONAL_FLAGS})
++ set_target_properties(tcpdump_stellar PROPERTIES COMPILE_FLAGS ${C_ADDITIONAL_FLAGS})
+ endif()
+-target_link_libraries(tcpdump netdissect ${TCPDUMP_LINK_LIBRARIES})
++target_link_libraries(tcpdump_stellar netdissect ${TCPDUMP_LINK_LIBRARIES})
++
++##like redis-cli, use hyphen('-') not underscore('_') to separate words
++add_executable(stellar-dump main.c)
++target_link_libraries(stellar-dump tcpdump_stellar pthread packet_manager)
++target_link_libraries(stellar-dump ${STELLAR_BINARY_DIR}/vendors/libevent/lib/libevent.a)
+
+ ######################################
+ # Write out the config.h file
+@@ -1325,7 +1355,7 @@
+ if(WIN32)
+ # XXX TODO where to install on Windows?
+ else(WIN32)
+- install(TARGETS tcpdump DESTINATION bin)
++ install(TARGETS tcpdump_stellar DESTINATION bin)
+ endif(WIN32)
+
+ # On UN*X, and on Windows when not using MSVC, process man pages and
+@@ -1340,7 +1370,7 @@
+ set(MAN1 "")
+ foreach(TEMPLATE_MANPAGE ${MAN1_EXPAND})
+ string(REPLACE ".in" "" MANPAGE ${TEMPLATE_MANPAGE})
+- configure_file(${CMAKE_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY)
++ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY)
+ set(MAN1 ${MAN1} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE})
+ endforeach(TEMPLATE_MANPAGE)
+ install(FILES ${MAN1} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+@@ -1361,11 +1391,11 @@
+ # rule run tests/TESTrun with it, because just trying to run the TESTrun
+ # script as a command won't work on Windows.
+ #
+-find_program(PERL perl)
+-if(PERL)
+- message(STATUS "Found perl at ${PERL}")
+- add_custom_target(check
+- COMMAND ${PERL} ${CMAKE_SOURCE_DIR}/tests/TESTrun)
+-else()
+- message(STATUS "Didn't find perl")
+-endif()
++# find_program(PERL perl)
++# if(PERL)
++# message(STATUS "Found perl at ${PERL}")
++# add_custom_target(check
++# COMMAND ${PERL} ${CMAKE_SOURCE_DIR}/tests/TESTrun)
++# else()
++# message(STATUS "Didn't find perl")
++# endif()
+diff -uNr tcpdump-tcpdump-4.99.4/ftmacros.h tcpdump-for-stellar/ftmacros.h
+--- tcpdump-tcpdump-4.99.4/ftmacros.h 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/ftmacros.h 2024-10-30 17:34:05.573498702 +0800
+@@ -101,7 +101,9 @@
+ * this might, for example, be GNU/HURD or one of Debian's kFreeBSD
+ * OSes ("GNU/FreeBSD").
+ */
++ #ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
++ #endif
+
+ /*
+ * We turn on both _DEFAULT_SOURCE and _BSD_SOURCE to try to get
+diff -uNr tcpdump-tcpdump-4.99.4/.gitattributes tcpdump-for-stellar/.gitattributes
+--- tcpdump-tcpdump-4.99.4/.gitattributes 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/.gitattributes 1970-01-01 08:00:00.000000000 +0800
+@@ -1,9 +0,0 @@
+-# Auto detect text files and perform LF normalization
+-* text=auto
+-
+-# Normalize line endings to LF on checkin and
+-# prevents conversion to CRLF when the file is checked out
+-tests/*.out text eol=lf
+-
+-# things that only make sense on github.com
+-.github export-ignore
+diff -uNr tcpdump-tcpdump-4.99.4/.gitignore tcpdump-for-stellar/.gitignore
+--- tcpdump-tcpdump-4.99.4/.gitignore 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/.gitignore 1970-01-01 08:00:00.000000000 +0800
+@@ -1,41 +0,0 @@
+-*.orig
+-*.rej
+-Makefile
+-*~
+-*.o
+-libnetdissect.a
+-config.h
+-config.log
+-config.cache
+-config.status
+-.devel
+-os-proto.h
+-stamp-h
+-stamp-h.in
+-tcpdump
+-tcpdump.1
+-tcpdump-*.tar.gz
+-failure-outputs.txt
+-autom4te.cache/
+-*.VC.db
+-*.VC.opendb
+-ALL_BUILD.vcxproj*
+-ZERO_CHECK.vcxproj*
+-check.vcxproj*
+-netdissect.vcxproj*
+-tcpdump.vcxproj*
+-uninstall.vcxproj*
+-CMakeCache.txt
+-CMakeFiles/
+-Debug/
+-Release/
+-MinSizeRel/
+-RelWithDebInfo/
+-cmake_install.cmake
+-cmake_uninstall.cmake
+-netdissect.dir/
+-tcpdump.dir/
+-tcpdump.sln
+-.vs/
+-.passed
+-.failed
+diff -uNr tcpdump-tcpdump-4.99.4/main.c tcpdump-for-stellar/main.c
+--- tcpdump-tcpdump-4.99.4/main.c 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/main.c 2024-10-30 17:34:06.260498697 +0800
+@@ -0,0 +1,10 @@
++#include <stdio.h>
++#include <pcap/pcap.h>
++#include "netdissect.h"
++
++extern int stellar_dump(int argc, char **argv);
++
++int main(int argc, char const *argv[])
++{
++ return stellar_dump(argc, (char **)argv);
++}
+diff -uNr tcpdump-tcpdump-4.99.4/netdissect.h tcpdump-for-stellar/netdissect.h
+--- tcpdump-tcpdump-4.99.4/netdissect.h 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/netdissect.h 2024-10-30 17:34:05.601498702 +0800
+@@ -21,17 +21,17 @@
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+-
+ #ifndef netdissect_h
+ #define netdissect_h
+
+ #ifdef HAVE_OS_PROTO_H
+ #include "os-proto.h"
+ #endif
++#include <time.h>
+ #include <sys/types.h>
+ #include <setjmp.h>
+ #include "status-exit-codes.h"
+-#include "funcattrs.h" /* for PRINTFLIKE_FUNCPTR() */
++#include "funcattrs.h" /* for PRINTFLIKE_FUNCPTR() */
+ #include "diag-control.h" /* for ND_UNREACHABLE */
+
+ /*
+@@ -63,7 +63,7 @@
+ typedef unsigned char nd_int32_t[4];
+ typedef unsigned char nd_int64_t[8];
+
+-#define FMAXINT (4294967296.0) /* floating point rep. of MAXINT */
++#define FMAXINT (4294967296.0) /* floating point rep. of MAXINT */
+
+ /*
+ * Use this for IPv4 addresses and netmasks.
+@@ -101,7 +101,7 @@
+ /*
+ * Use this for MAC addresses.
+ */
+-#define MAC_ADDR_LEN 6U /* length of MAC addresses */
++#define MAC_ADDR_LEN 6U /* length of MAC addresses */
+ typedef unsigned char nd_mac_addr[MAC_ADDR_LEN];
+
+ /*
+@@ -113,33 +113,34 @@
+ * Round up x to a multiple of y; y must be a power of 2.
+ */
+ #ifndef roundup2
+-#define roundup2(x, y) (((x)+((u_int)((y)-1)))&(~((u_int)((y)-1))))
++#define roundup2(x, y) (((x) + ((u_int)((y) - 1))) & (~((u_int)((y) - 1))))
+ #endif
+
+ #include <stdarg.h>
+ #include <pcap.h>
+
+-#include "ip.h" /* struct ip for nextproto4_cksum() */
+-#include "ip6.h" /* struct ip6 for nextproto6_cksum() */
++#include "tcpdump_ip.h" /* struct ip for nextproto4_cksum() */
++#include "tcpdump_ip6.h" /* struct ip6 for nextproto6_cksum() */
+
+ #ifndef HAVE_STRLCAT
+-extern size_t strlcat (char *, const char *, size_t);
++extern size_t strlcat(char *, const char *, size_t);
+ #endif
+ #ifndef HAVE_STRLCPY
+-extern size_t strlcpy (char *, const char *, size_t);
++extern size_t strlcpy(char *, const char *, size_t);
+ #endif
+
+ #ifndef HAVE_STRDUP
+-extern char *strdup (const char *str);
++extern char *strdup(const char *str);
+ #endif
+
+ #ifndef HAVE_STRSEP
+ extern char *strsep(char **, const char *);
+ #endif
+
+-struct tok {
+- u_int v; /* value */
+- const char *s; /* string */
++struct tok
++{
++ u_int v; /* value */
++ const char *s; /* string */
+ };
+
+ /* tok2str is deprecated */
+@@ -165,7 +166,7 @@
+
+ #define IF_PRINTER_ARGS (netdissect_options *, const struct pcap_pkthdr *, const u_char *)
+
+-typedef void (*if_printer) IF_PRINTER_ARGS;
++typedef void(*if_printer) IF_PRINTER_ARGS;
+
+ /*
+ * In case the data in a buffer needs to be processed by being decrypted,
+@@ -186,52 +187,54 @@
+ * on the stack with null buffer pointer, meaning there's nothing to
+ * free.
+ */
+-struct netdissect_saved_packet_info {
+- u_char *ndspi_buffer; /* pointer to allocated buffer data */
+- const u_char *ndspi_packetp; /* saved beginning of data */
+- const u_char *ndspi_snapend; /* saved end of data */
+- struct netdissect_saved_packet_info *ndspi_prev; /* previous buffer on the stack */
++struct netdissect_saved_packet_info
++{
++ u_char *ndspi_buffer; /* pointer to allocated buffer data */
++ const u_char *ndspi_packetp; /* saved beginning of data */
++ const u_char *ndspi_snapend; /* saved end of data */
++ struct netdissect_saved_packet_info *ndspi_prev; /* previous buffer on the stack */
+ };
+
+ /* 'val' value(s) for longjmp */
+ #define ND_TRUNCATED 1
+
+-struct netdissect_options {
+- int ndo_bflag; /* print 4 byte ASes in ASDOT notation */
+- int ndo_eflag; /* print ethernet header */
+- int ndo_fflag; /* don't translate "foreign" IP address */
+- int ndo_Kflag; /* don't check IP, TCP or UDP checksums */
+- int ndo_nflag; /* leave addresses as numbers */
+- int ndo_Nflag; /* remove domains from printed host names */
+- int ndo_qflag; /* quick (shorter) output */
+- int ndo_Sflag; /* print raw TCP sequence numbers */
+- int ndo_tflag; /* print packet arrival time */
+- int ndo_uflag; /* Print undecoded NFS handles */
+- int ndo_vflag; /* verbosity level */
+- int ndo_xflag; /* print packet in hex */
+- int ndo_Xflag; /* print packet in hex/ASCII */
+- int ndo_Aflag; /* print packet only in ASCII observing TAB,
+- * LF, CR and SPACE as graphical chars
+- */
+- int ndo_Hflag; /* dissect 802.11s draft mesh standard */
+- const char *ndo_protocol; /* protocol */
+- jmp_buf ndo_early_end; /* jmp_buf for setjmp()/longjmp() */
+- void *ndo_last_mem_p; /* pointer to the last allocated memory chunk */
+- int ndo_packet_number; /* print a packet number in the beginning of line */
++struct netdissect_options
++{
++ int ndo_bflag; /* print 4 byte ASes in ASDOT notation */
++ int ndo_eflag; /* print ethernet header */
++ int ndo_fflag; /* don't translate "foreign" IP address */
++ int ndo_Kflag; /* don't check IP, TCP or UDP checksums */
++ int ndo_nflag; /* leave addresses as numbers */
++ int ndo_Nflag; /* remove domains from printed host names */
++ int ndo_qflag; /* quick (shorter) output */
++ int ndo_Sflag; /* print raw TCP sequence numbers */
++ int ndo_tflag; /* print packet arrival time */
++ int ndo_uflag; /* Print undecoded NFS handles */
++ int ndo_vflag; /* verbosity level */
++ int ndo_xflag; /* print packet in hex */
++ int ndo_Xflag; /* print packet in hex/ASCII */
++ int ndo_Aflag; /* print packet only in ASCII observing TAB,
++ * LF, CR and SPACE as graphical chars
++ */
++ int ndo_Hflag; /* dissect 802.11s draft mesh standard */
++ const char *ndo_protocol; /* protocol */
++ jmp_buf ndo_early_end; /* jmp_buf for setjmp()/longjmp() */
++ void *ndo_last_mem_p; /* pointer to the last allocated memory chunk */
++ int ndo_packet_number; /* print a packet number in the beginning of line */
+ int ndo_suppress_default_print; /* don't use default_print() for unknown packet types */
+- int ndo_tstamp_precision; /* requested time stamp precision */
+- const char *program_name; /* Name of the program using the library */
++ int ndo_tstamp_precision; /* requested time stamp precision */
++ const char *program_name; /* Name of the program using the library */
+
+ char *ndo_espsecret;
+- struct sa_list *ndo_sa_list_head; /* used by print-esp.c */
++ struct sa_list *ndo_sa_list_head; /* used by print-esp.c */
+ struct sa_list *ndo_sa_default;
+
+- char *ndo_sigsecret; /* Signature verification secret key */
++ char *ndo_sigsecret; /* Signature verification secret key */
+
+- int ndo_packettype; /* as specified by -T */
++ int ndo_packettype; /* as specified by -T */
+
+- int ndo_snaplen;
+- int ndo_ll_hdr_len; /* link-layer header length */
++ int ndo_snaplen;
++ int ndo_ll_hdr_len; /* link-layer header length */
+
+ /*global pointers to beginning and end of current packet (during printing) */
+ const u_char *ndo_packetp;
+@@ -245,21 +248,21 @@
+
+ /* pointer to void function to output stuff */
+ void (*ndo_default_print)(netdissect_options *,
+- const u_char *bp, u_int length);
++ const u_char *bp, u_int length);
+
+ /* pointer to function to do regular output */
+- int (*ndo_printf)(netdissect_options *,
+- const char *fmt, ...)
+- PRINTFLIKE_FUNCPTR(2, 3);
++ int (*ndo_printf)(netdissect_options *,
++ const char *fmt, ...)
++ PRINTFLIKE_FUNCPTR(2, 3);
+ /* pointer to function to output errors */
+ void NORETURN_FUNCPTR (*ndo_error)(netdissect_options *,
+- status_exit_codes_t status,
+- const char *fmt, ...)
+- PRINTFLIKE_FUNCPTR(3, 4);
++ status_exit_codes_t status,
++ const char *fmt, ...)
++ PRINTFLIKE_FUNCPTR(3, 4);
+ /* pointer to function to output warnings */
+ void (*ndo_warning)(netdissect_options *,
+- const char *fmt, ...)
+- PRINTFLIKE_FUNCPTR(2, 3);
++ const char *fmt, ...)
++ PRINTFLIKE_FUNCPTR(2, 3);
+ };
+
+ extern WARN_UNUSED_RESULT int nd_push_buffer(netdissect_options *, u_char *, const u_char *, const u_int);
+@@ -271,43 +274,40 @@
+ static inline NORETURN void
+ nd_trunc_longjmp(netdissect_options *ndo)
+ {
+- longjmp(ndo->ndo_early_end, ND_TRUNCATED);
++ longjmp(ndo->ndo_early_end, ND_TRUNCATED);
+ #ifdef _AIX
+- /*
+- * In AIX <setjmp.h> decorates longjmp() with "#pragma leaves", which tells
+- * XL C that the function is noreturn, but GCC remains unaware of that and
+- * yields a "'noreturn' function does return" warning.
+- */
+- ND_UNREACHABLE
++ /*
++ * In AIX <setjmp.h> decorates longjmp() with "#pragma leaves", which tells
++ * XL C that the function is noreturn, but GCC remains unaware of that and
++ * yields a "'noreturn' function does return" warning.
++ */
++ ND_UNREACHABLE
+ #endif /* _AIX */
+ }
+
+-#define PT_VAT 1 /* Visual Audio Tool */
+-#define PT_WB 2 /* distributed White Board */
+-#define PT_RPC 3 /* Remote Procedure Call */
+-#define PT_RTP 4 /* Real-Time Applications protocol */
+-#define PT_RTCP 5 /* Real-Time Applications control protocol */
+-#define PT_SNMP 6 /* Simple Network Management Protocol */
+-#define PT_CNFP 7 /* Cisco NetFlow protocol */
+-#define PT_TFTP 8 /* trivial file transfer protocol */
+-#define PT_AODV 9 /* Ad-hoc On-demand Distance Vector Protocol */
+-#define PT_CARP 10 /* Common Address Redundancy Protocol */
+-#define PT_RADIUS 11 /* RADIUS authentication Protocol */
+-#define PT_ZMTP1 12 /* ZeroMQ Message Transport Protocol 1.0 */
+-#define PT_VXLAN 13 /* Virtual eXtensible Local Area Network */
+-#define PT_PGM 14 /* [UDP-encapsulated] Pragmatic General Multicast */
+-#define PT_PGM_ZMTP1 15 /* ZMTP/1.0 inside PGM (native or UDP-encapsulated) */
+-#define PT_LMP 16 /* Link Management Protocol */
+-#define PT_RESP 17 /* RESP */
+-#define PT_PTP 18 /* PTP */
+-#define PT_SOMEIP 19 /* Autosar SOME/IP Protocol */
+-#define PT_DOMAIN 20 /* Domain Name System (DNS) */
+-
+-#define ND_MIN(a,b) ((a)>(b)?(b):(a))
+-#define ND_MAX(a,b) ((b)>(a)?(b):(a))
++#define PT_VAT 1 /* Visual Audio Tool */
++#define PT_WB 2 /* distributed White Board */
++#define PT_RPC 3 /* Remote Procedure Call */
++#define PT_RTP 4 /* Real-Time Applications protocol */
++#define PT_RTCP 5 /* Real-Time Applications control protocol */
++#define PT_SNMP 6 /* Simple Network Management Protocol */
++#define PT_CNFP 7 /* Cisco NetFlow protocol */
++#define PT_TFTP 8 /* trivial file transfer protocol */
++#define PT_AODV 9 /* Ad-hoc On-demand Distance Vector Protocol */
++#define PT_CARP 10 /* Common Address Redundancy Protocol */
++#define PT_RADIUS 11 /* RADIUS authentication Protocol */
++#define PT_ZMTP1 12 /* ZeroMQ Message Transport Protocol 1.0 */
++#define PT_VXLAN 13 /* Virtual eXtensible Local Area Network */
++#define PT_PGM 14 /* [UDP-encapsulated] Pragmatic General Multicast */
++#define PT_PGM_ZMTP1 15 /* ZMTP/1.0 inside PGM (native or UDP-encapsulated) */
++#define PT_LMP 16 /* Link Management Protocol */
++#define PT_RESP 17 /* RESP */
++#define PT_PTP 18 /* PTP */
++#define PT_SOMEIP 19 /* Autosar SOME/IP Protocol */
++#define PT_DOMAIN 20 /* Domain Name System (DNS) */
+
+-/* For source or destination ports tests (UDP, TCP, ...) */
+-#define IS_SRC_OR_DST_PORT(p) (sport == (p) || dport == (p))
++#define ND_MIN(a, b) ((a) > (b) ? (b) : (a))
++#define ND_MAX(a, b) ((b) > (a) ? (b) : (a))
+
+ /*
+ * Maximum snapshot length. This should be enough to capture the full
+@@ -336,7 +336,7 @@
+ * let libpcap choose a snapshot length; newer versions may choose a bigger
+ * value than 262144 for D-Bus, for example.
+ */
+-#define MAXIMUM_SNAPLEN 262144
++#define MAXIMUM_SNAPLEN 262144
+
+ /*
+ * True if "l" bytes from "p" were captured.
+@@ -365,19 +365,23 @@
+ */
+ #define IS_NOT_NEGATIVE(x) (((x) > 0) || ((x) == 0))
+
+-#define ND_TTEST_LEN(p, l) \
+- (IS_NOT_NEGATIVE(l) && \
+- ((uintptr_t)ndo->ndo_snapend - (l) <= (uintptr_t)ndo->ndo_snapend && \
+- (uintptr_t)(p) <= (uintptr_t)ndo->ndo_snapend - (l)))
++#define ND_TTEST_LEN(p, l) \
++ (IS_NOT_NEGATIVE(l) && \
++ ((uintptr_t)ndo->ndo_snapend - (l) <= (uintptr_t)ndo->ndo_snapend && \
++ (uintptr_t)(p) <= (uintptr_t)ndo->ndo_snapend - (l)))
+
+ /* True if "*(p)" was captured */
+ #define ND_TTEST_SIZE(p) ND_TTEST_LEN(p, sizeof(*(p)))
+
+ /* Bail out if "l" bytes from "p" were not captured */
+ #ifdef ND_LONGJMP_FROM_TCHECK
+-#define ND_TCHECK_LEN(p, l) if (!ND_TTEST_LEN(p, l)) nd_trunc_longjmp(ndo)
++#define ND_TCHECK_LEN(p, l) \
++ if (!ND_TTEST_LEN(p, l)) \
++ nd_trunc_longjmp(ndo)
+ #else
+-#define ND_TCHECK_LEN(p, l) if (!ND_TTEST_LEN(p, l)) goto trunc
++#define ND_TCHECK_LEN(p, l) \
++ if (!ND_TTEST_LEN(p, l)) \
++ goto trunc
+ #endif
+
+ /* Bail out if "*(p)" was not captured */
+@@ -395,26 +399,28 @@
+ #define ND_BYTES_AVAILABLE_AFTER(p) ND_BYTES_BETWEEN(ndo->ndo_snapend, (p))
+
+ /* Check length < minimum for invalid packet with a custom message, format %u */
+-#define ND_LCHECKMSG_U(length, minimum, what) \
+-if ((length) < (minimum)) { \
+-ND_PRINT(" [%s %u < %u]", (what), (length), (minimum)); \
+-goto invalid; \
+-}
++#define ND_LCHECKMSG_U(length, minimum, what) \
++ if ((length) < (minimum)) \
++ { \
++ ND_PRINT(" [%s %u < %u]", (what), (length), (minimum)); \
++ goto invalid; \
++ }
+
+ /* Check length < minimum for invalid packet with #length message, format %u */
+ #define ND_LCHECK_U(length, minimum) \
+-ND_LCHECKMSG_U((length), (minimum), (#length))
++ ND_LCHECKMSG_U((length), (minimum), (#length))
+
+ /* Check length < minimum for invalid packet with a custom message, format %zu */
+-#define ND_LCHECKMSG_ZU(length, minimum, what) \
+-if ((length) < (minimum)) { \
+-ND_PRINT(" [%s %u < %zu]", (what), (length), (minimum)); \
+-goto invalid; \
+-}
++#define ND_LCHECKMSG_ZU(length, minimum, what) \
++ if ((length) < (minimum)) \
++ { \
++ ND_PRINT(" [%s %u < %zu]", (what), (length), (minimum)); \
++ goto invalid; \
++ }
+
+ /* Check length < minimum for invalid packet with #length message, format %zu */
+ #define ND_LCHECK_ZU(length, minimum) \
+-ND_LCHECKMSG_ZU((length), (minimum), (#length))
++ ND_LCHECKMSG_ZU((length), (minimum), (#length))
+
+ #define ND_PRINT(...) (ndo->ndo_printf)(ndo, __VA_ARGS__)
+ #define ND_DEFAULTPRINT(ap, length) (*ndo->ndo_default_print)(ndo, ap, length)
+@@ -424,7 +430,7 @@
+ extern void unsigned_relts_print(netdissect_options *, uint32_t);
+
+ extern const char *nd_format_time(char *buf, size_t bufsize,
+- const char *format, const struct tm *timeptr);
++ const char *format, const struct tm *timeptr);
+
+ extern void fn_print_char(netdissect_options *, u_char);
+ extern void fn_print_str(netdissect_options *, const u_char *);
+@@ -435,16 +441,16 @@
+ /*
+ * Flags for txtproto_print().
+ */
+-#define RESP_CODE_SECOND_TOKEN 0x00000001 /* response code is second token in response line */
++#define RESP_CODE_SECOND_TOKEN 0x00000001 /* response code is second token in response line */
+
+ extern void txtproto_print(netdissect_options *, const u_char *, u_int,
+- const char **, u_int);
++ const char **, u_int);
+
+-#if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \
+- (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__)) || \
+- (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \
++#if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \
++ (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__)) || \
++ (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \
+ (defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64)) || \
+- (defined(__s390__) || defined(__s390x__) || defined(__zarch__)) || \
++ (defined(__s390__) || defined(__s390x__) || defined(__zarch__)) || \
+ defined(__vax__)
+ /*
+ * The processor natively handles unaligned loads, so just use memcpy()
+@@ -461,8 +467,8 @@
+ * XXX - do we need to test for both 32-bit and 64-bit versions of those
+ * architectures in all cases?
+ */
+-#define UNALIGNED_MEMCPY(p, q, l) memcpy((p), (q), (l))
+-#define UNALIGNED_MEMCMP(p, q, l) memcmp((p), (q), (l))
++#define UNALIGNED_MEMCPY(p, q, l) memcpy((p), (q), (l))
++#define UNALIGNED_MEMCMP(p, q, l) memcmp((p), (q), (l))
+ #else
+ /*
+ * The processor doesn't natively handle unaligned loads,
+@@ -477,28 +483,32 @@
+ */
+ extern void unaligned_memcpy(void *, const void *, size_t);
+ extern int unaligned_memcmp(const void *, const void *, size_t);
+-#define UNALIGNED_MEMCPY(p, q, l) unaligned_memcpy((p), (q), (l))
+-#define UNALIGNED_MEMCMP(p, q, l) unaligned_memcmp((p), (q), (l))
++#define UNALIGNED_MEMCPY(p, q, l) unaligned_memcpy((p), (q), (l))
++#define UNALIGNED_MEMCMP(p, q, l) unaligned_memcmp((p), (q), (l))
+ #endif
+
+ #define PLURAL_SUFFIX(n) \
+- (((n) != 1) ? "s" : "")
++ (((n) != 1) ? "s" : "")
+
+ extern const char *tok2strary_internal(const char **, int, const char *, int);
+-#define tok2strary(a,f,i) tok2strary_internal(a, sizeof(a)/sizeof(a[0]),f,i)
++#define tok2strary(a, f, i) tok2strary_internal(a, sizeof(a) / sizeof(a[0]), f, i)
+
+ struct uint_tokary
+ {
+- u_int uintval;
+- const struct tok *tokary;
++ u_int uintval;
++ const struct tok *tokary;
+ };
+
+ extern const struct tok *uint2tokary_internal(const struct uint_tokary[], const size_t, const u_int);
+-#define uint2tokary(a, i) uint2tokary_internal(a, sizeof(a)/sizeof(a[0]), i)
++#define uint2tokary(a, i) uint2tokary_internal(a, sizeof(a) / sizeof(a[0]), i)
+
+ extern if_printer lookup_printer(int);
+
+-#define ND_DEBUG {printf(" [%s:%d %s] ", __FILE__, __LINE__, __func__); fflush(stdout);}
++#define ND_DEBUG \
++ { \
++ printf(" [%s:%d %s] ", __FILE__, __LINE__, __func__); \
++ fflush(stdout); \
++ }
+
+ /* The DLT printer routines */
+
+@@ -577,9 +587,10 @@
+ * This contains a pointer to an address and a pointer to a routine
+ * to which we pass that pointer in order to get a string.
+ */
+-struct lladdr_info {
+- const char *(*addr_string)(netdissect_options *, const u_char *);
+- const u_char *addr;
++struct lladdr_info
++{
++ const char *(*addr_string)(netdissect_options *, const u_char *);
++ const u_char *addr;
+ };
+
+ /* The printer routines. */
+@@ -589,7 +600,7 @@
+ extern void ahcp_print(netdissect_options *, const u_char *, u_int);
+ extern void aodv_print(netdissect_options *, const u_char *, u_int, int);
+ extern void aoe_print(netdissect_options *, const u_char *, const u_int);
+-extern int arista_ethertype_print(netdissect_options *,const u_char *, u_int);
++extern int arista_ethertype_print(netdissect_options *, const u_char *, u_int);
+ extern void arp_print(netdissect_options *, const u_char *, u_int, u_int);
+ extern void ascii_print(netdissect_options *, const u_char *, u_int);
+ extern void atalk_print(netdissect_options *, const u_char *, u_int);
+@@ -668,8 +679,8 @@
+ extern void lwres_print(netdissect_options *, const u_char *, u_int);
+ extern void m3ua_print(netdissect_options *, const u_char *, const u_int);
+ extern int macsec_print(netdissect_options *, const u_char **,
+- u_int *, u_int *, u_int *, const struct lladdr_info *,
+- const struct lladdr_info *);
++ u_int *, u_int *, u_int *, const struct lladdr_info *,
++ const struct lladdr_info *);
+ extern u_int mfr_print(netdissect_options *, const u_char *, u_int);
+ extern void mobile_print(netdissect_options *, const u_char *, u_int);
+ extern int mobility_print(netdissect_options *, const u_char *, const u_char *);
+@@ -756,9 +767,10 @@
+ extern uint16_t verify_crc10_cksum(uint16_t, const u_char *, int);
+ extern uint16_t create_osi_cksum(const uint8_t *, int, int);
+
+-struct cksum_vec {
+- const uint8_t *ptr;
+- int len;
++struct cksum_vec
++{
++ const uint8_t *ptr;
++ int len;
+ };
+ extern uint16_t in_cksum(const struct cksum_vec *, int);
+ extern uint16_t in_cksum_shouldbe(uint16_t, uint16_t);
+@@ -766,10 +778,10 @@
+ /* IP protocol demuxing routines */
+ extern void ip_demux_print(netdissect_options *, const u_char *, u_int, u_int, int, u_int, uint8_t, const u_char *);
+
+-extern uint16_t nextproto4_cksum(netdissect_options *, const struct ip *, const uint8_t *, u_int, u_int, uint8_t);
++extern uint16_t nextproto4_cksum(netdissect_options *, const struct tcpdump_ip *, const uint8_t *, u_int, u_int, uint8_t);
+
+ /* in print-ip6.c */
+-extern uint16_t nextproto6_cksum(netdissect_options *, const struct ip6_hdr *, const uint8_t *, u_int, u_int, uint8_t);
++extern uint16_t nextproto6_cksum(netdissect_options *, const struct tcpdump_ip6_hdr *, const uint8_t *, u_int, u_int, uint8_t);
+
+ /* Utilities */
+ extern void nd_print_trunc(netdissect_options *);
+@@ -787,8 +799,8 @@
+
+ extern void esp_decodesecret_print(netdissect_options *);
+ extern int esp_decrypt_buffer_by_ikev2_print(netdissect_options *, int,
+- const u_char spii[8],
+- const u_char spir[8],
+- const u_char *, const u_char *);
++ const u_char spii[8],
++ const u_char spir[8],
++ const u_char *, const u_char *);
+
+ #endif /* netdissect_h */
+diff -uNr tcpdump-tcpdump-4.99.4/netdissect-stdinc.h tcpdump-for-stellar/netdissect-stdinc.h
+--- tcpdump-tcpdump-4.99.4/netdissect-stdinc.h 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/netdissect-stdinc.h 2024-10-30 17:34:05.601498702 +0800
+@@ -35,9 +35,9 @@
+ * various items as needed, to isolate most of netdissect's platform
+ * differences to this one file.
+ */
+-
+-#ifndef netdissect_stdinc_h
+-#define netdissect_stdinc_h
++#pragma once
++// #ifndef netdissect_stdinc_h
++// #define netdissect_stdinc_h
+
+ #include "ftmacros.h"
+
+@@ -379,4 +379,4 @@
+ # define ND_FALL_THROUGH
+ #endif /* __has_attribute(fallthrough) */
+
+-#endif /* netdissect_stdinc_h */
++// #endif /* netdissect_stdinc_h */
+diff -uNr tcpdump-tcpdump-4.99.4/packet_parser.c tcpdump-for-stellar/packet_parser.c
+--- tcpdump-tcpdump-4.99.4/packet_parser.c 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/packet_parser.c 2024-10-30 17:34:06.264498697 +0800
+@@ -0,0 +1,980 @@
++#include <string.h>
++#include <stdlib.h>
++#include <netinet/icmp6.h>
++#include <netinet/ip_icmp.h>
++#include <linux/ppp_defs.h>
++
++#include "log_internal.h"
++#include "packet_helper.h"
++#include "packet_internal.h"
++#include "packet_parser.h"
++
++#define PACKET_PARSE_LOG_DEBUG(format, ...) // STELLAR_LOG_DEBUG(__thread_local_logger, "packet parse", format, ##__VA_ARGS__)
++#define PACKET_PARSE_LOG_ERROR(format, ...) STELLAR_LOG_ERROR(__thread_local_logger, "packet parse", format, ##__VA_ARGS__)
++#define PACKET_PARSE_LOG_WARN(format, ...) STELLAR_LOG_WARN(__thread_local_logger, "packet parse", format, ##__VA_ARGS__)
++
++#define PACKET_LOG_DATA_INSUFFICIENCY(pkt, layer) \
++ { \
++ PACKET_PARSE_LOG_WARN("pkt: %p, layer: %s, data insufficiency", \
++ (pkt), layer_proto_to_str(layer)); \
++ }
++
++#define PACKET_LOG_UNSUPPORT_PROTO(pkt, layer, next_proto) \
++ { \
++ PACKET_PARSE_LOG_WARN("pkt: %p, layer: %s, unsupport next proto: %d", \
++ (pkt), layer_proto_to_str(layer), (next_proto)); \
++ }
++
++#define PACKET_LOG_UNSUPPORT_ETHPROTO(pkt, next_proto) \
++ { \
++ PACKET_PARSE_LOG_WARN("pkt: %p, layer: L3, unsupport next proto: %d %s", \
++ (pkt), (next_proto), eth_proto_to_str(next_proto)); \
++ }
++
++#define PACKET_LOG_UNSUPPORT_IPPROTO(pkt, next_proto) \
++ { \
++ PACKET_PARSE_LOG_WARN("pkt: %p, layer: L4, unsupport next proto: %d %s", \
++ (pkt), (next_proto), ip_proto_to_str(next_proto)); \
++ }
++
++/******************************************************************************
++ * Static API
++ ******************************************************************************/
++
++static inline struct layer_private *get_free_layer(struct packet *pkt);
++
++// 数据链路层
++static inline const char *parse_ether(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_pweth(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_ppp(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_l2tpv2_over_udp(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_l2tpv3_over_udp(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_l2tpv3_over_ip(struct packet *pkt, const char *data, uint16_t len);
++
++// 数据链路层 -- 隧道
++static inline const char *parse_vlan(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_pppoe_ses(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_mpls(struct packet *pkt, const char *data, uint16_t len);
++// 网络层
++static inline const char *parse_ipv4(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_ipv6(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_auth(struct packet *pkt, const char *data, uint16_t len);
++// 网络层 -- 隧道
++static inline const char *parse_gre(struct packet *pkt, const char *data, uint16_t len);
++// 传输层
++static inline const char *parse_udp(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_tcp(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_icmp(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_icmp6(struct packet *pkt, const char *data, uint16_t len);
++// 传输层 -- 隧道
++static inline const char *parse_vxlan(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_gtp_u(struct packet *pkt, const char *data, uint16_t len);
++static inline const char *parse_gtp_c(struct packet *pkt, const char *data, uint16_t len);
++// L3/L4
++static inline const char *parse_l3(struct packet *pkt, uint16_t next_proto, const char *data, uint16_t len);
++static inline const char *parse_l4(struct packet *pkt, uint8_t next_proto, const char *data, uint16_t len);
++
++/******************************************************************************
++ * Private API -- Utils
++ ******************************************************************************/
++
++static inline struct layer_private *get_free_layer(struct packet *pkt)
++{
++ if (pkt->layers_used >= pkt->layers_size)
++ {
++ return NULL;
++ }
++
++ return &pkt->layers[pkt->layers_used];
++}
++
++#define SET_LAYER(_pkt, _layer, _proto, _hdr_len, _data, _len, _trim) \
++ { \
++ (_layer)->proto = (_proto); \
++ (_layer)->hdr_offset = (_pkt)->data_len - (_pkt)->trim_len - (_len); \
++ (_layer)->hdr_ptr = (_data); \
++ (_layer)->hdr_len = (_hdr_len); \
++ (_layer)->pld_ptr = (_data) + (_hdr_len); \
++ (_layer)->pld_len = (_len) - (_hdr_len) - (_trim); \
++ (_pkt)->trim_len += (_trim); \
++ (_pkt)->layers_used++; \
++ PACKET_PARSE_LOG_DEBUG("layer[%d/%d]: %s, header: {offset: %d, ptr: %p, len: %d}, payload: {ptr: %p, len: %d}", \
++ (_pkt)->layers_used - 1, (_pkt)->layers_size, layer_proto_to_str((_proto)), \
++ (_layer)->hdr_offset, (_layer)->hdr_ptr, (_layer)->hdr_len, (_layer)->pld_ptr, (_layer)->pld_len); \
++ }
++
++/******************************************************************************
++ * Private API -- Parses
++ ******************************************************************************/
++
++static inline const char *parse_ether(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < sizeof(struct ethhdr)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_ETHER);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ uint16_t next_proto = eth_hdr_get_proto((const struct ethhdr *)data);
++ SET_LAYER(pkt, layer, LAYER_PROTO_ETHER, sizeof(struct ethhdr), data, len, 0);
++
++ return parse_l3(pkt, next_proto, layer->pld_ptr, layer->pld_len);
++}
++
++static inline const char *parse_pweth(struct packet *pkt, const char *data, uint16_t len)
++{
++ /*
++ * PW Ethernet Control Word
++ * 0 1 2 3
++ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * |0 0 0 0| Reserved | Sequence Number |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * Reference: https://tools.ietf.org/html/rfc4448
++ */
++ if (unlikely(len < 4))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_PWETH);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_PWETH, 4, data, len, 0);
++
++ return parse_ether(pkt, layer->pld_ptr, layer->pld_len);
++}
++
++static inline int is_ppp_proto(uint16_t proto)
++{
++ // /usr/include/linux/ppp_defs.h.html
++ switch (proto)
++ {
++ case PPP_IP: /* Internet Protocol */
++ case PPP_AT: /* AppleTalk Protocol */
++ case PPP_IPX: /* IPX protocol */
++ case PPP_VJC_COMP: /* VJ compressed TCP */
++ case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */
++ case PPP_MP: /* Multilink protocol */
++ case PPP_IPV6: /* Internet Protocol Version 6 */
++ case PPP_COMPFRAG: /* fragment compressed below bundle */
++ case PPP_COMP: /* compressed packet */
++ case PPP_MPLS_UC: /* Multi Protocol Label Switching - Unicast */
++ case PPP_MPLS_MC: /* Multi Protocol Label Switching - Multicast */
++ case PPP_IPCP: /* IP Control Protocol */
++ case PPP_ATCP: /* AppleTalk Control Protocol */
++ case PPP_IPXCP: /* IPX Control Protocol */
++ case PPP_IPV6CP: /* IPv6 Control Protocol */
++ case PPP_CCPFRAG: /* CCP at link level (below MP bundle) */
++ // case PPP_CCP: /* Compression Control Protocol */ (same as PPP_CCPFRAG)
++ case PPP_MPLSCP: /* MPLS Control Protocol */
++ case PPP_LCP: /* Link Control Protocol */
++ case PPP_PAP: /* Password Authentication Protocol */
++ case PPP_LQR: /* Link Quality Report protocol */
++ case PPP_CHAP: /* Cryptographic Handshake Auth. Protocol */
++ case PPP_CBCP: /* Callback Control Protocol */
++ return 1;
++ default:
++ return 0;
++ }
++}
++
++static inline const char *parse_ppp(struct packet *pkt, const char *data, uint16_t len)
++{
++ /*
++ * https://datatracker.ietf.org/doc/html/rfc1661#section-2
++ * +----------+-------------+---------+
++ * | Protocol | Information | Padding |
++ * | 8/16 bits| * | * |
++ * +----------+-------------+---------+
++ *
++ * https://datatracker.ietf.org/doc/html/rfc1331#section-3.1
++ * +----------+----------+----------+----------+------------
++ * | Flag | Address | Control | Protocol | Information
++ * | 01111110 | 11111111 | 00000011 | 16 bits | *
++ * +----------+----------+----------+----------+------------
++ * ---+----------+----------+-----------------
++ * | FCS | Flag | Inter-frame Fill
++ * | 16 bits | 01111110 | or next Address
++ * ---+----------+----------+-----------------
++ */
++ if (unlikely(len < 4))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_PPP);
++ return data;
++ }
++
++ uint16_t hdr_len = 0;
++ uint16_t next_proto = 0;
++ struct layer_private *layer = NULL;
++
++ // ppp header 1 byte
++ next_proto = *((uint8_t *)data);
++ if (is_ppp_proto(next_proto))
++ {
++ hdr_len = 1;
++ goto success;
++ }
++
++ // ppp header 2 bytes
++ next_proto = ntohs(*((uint16_t *)data));
++ if (is_ppp_proto(next_proto))
++ {
++ hdr_len = 2;
++ goto success;
++ }
++
++ // ppp header 4 bytes
++ next_proto = ntohs(*((uint16_t *)data + 1));
++ hdr_len = 4;
++
++success:
++ layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_PPP, hdr_len, data, len, 0);
++ switch (next_proto)
++ {
++ case PPP_IP:
++ return parse_ipv4(pkt, layer->pld_ptr, layer->pld_len);
++ case PPP_IPV6:
++ return parse_ipv6(pkt, layer->pld_ptr, layer->pld_len);
++ default:
++ PACKET_LOG_UNSUPPORT_PROTO(pkt, LAYER_PROTO_PPP, next_proto);
++ return layer->pld_ptr;
++ }
++}
++
++static inline const char *parse_l2tpv2_over_udp(struct packet *pkt, const char *data, uint16_t len)
++{
++ uint16_t hdr_len = calc_udp_l2tpv2_hdr_len(data, len);
++ if (unlikely(hdr_len == 0 || hdr_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_L2TP);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_L2TP, hdr_len, data, len, 0);
++
++ // control message
++ if (l2tp_hdr_get_type((const struct l2tp_hdr *)data))
++ {
++ return layer->pld_ptr;
++ }
++ // data message
++ else
++ {
++ return parse_ppp(pkt, layer->pld_ptr, layer->pld_len);
++ }
++}
++
++static inline const char *parse_l2tpv3_over_udp(struct packet *pkt, const char *data, uint16_t len)
++{
++ uint16_t hdr_len = calc_udp_l2tpv3_hdr_len(data, len);
++ if (unlikely(hdr_len == 0 || hdr_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_L2TP);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_L2TP, hdr_len, data, len, 0);
++
++ // control message
++ if (l2tp_hdr_get_type((const struct l2tp_hdr *)data))
++ {
++ return layer->pld_ptr;
++ }
++ // data message
++ else
++ {
++ // TOOD
++ return layer->pld_ptr;
++ }
++}
++
++static inline const char *parse_l2tpv3_over_ip(struct packet *pkt, const char *data, uint16_t len)
++{
++ uint16_t hdr_len = calc_ip_l2tpv3_hdr_len(data, len);
++ if (unlikely(hdr_len == 0 || hdr_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_L2TP);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_L2TP, hdr_len, data, len, 0);
++
++ // data message
++ if (ntohl(*((uint32_t *)data)))
++ {
++ // TOOD
++ return layer->pld_ptr;
++ }
++ // control message
++ else
++ {
++ return layer->pld_ptr;
++ }
++}
++
++static inline const char *parse_vlan(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < sizeof(struct vlan_hdr)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_VLAN);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ uint16_t next_proto = vlan_hdr_get_ethertype((const struct vlan_hdr *)data);
++ SET_LAYER(pkt, layer, LAYER_PROTO_VLAN, sizeof(struct vlan_hdr), data, len, 0);
++
++ return parse_l3(pkt, next_proto, layer->pld_ptr, layer->pld_len);
++}
++
++static inline const char *parse_pppoe_ses(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < 6))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_PPPOE);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_PPPOE, 6, data, len, 0);
++
++ return parse_ppp(pkt, layer->pld_ptr, layer->pld_len);
++}
++
++static inline const char *parse_mpls(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < 4))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_MPLS);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++
++ if (mpls_label_get_bos((const struct mpls_label *)data))
++ {
++ SET_LAYER(pkt, layer, LAYER_PROTO_MPLS, 4, data, len, 0);
++ if (layer->pld_len == 0)
++ {
++ return layer->pld_ptr;
++ }
++ uint8_t next_proto = layer->pld_ptr[0] >> 4;
++ switch (next_proto)
++ {
++ case 0:
++ // the first four digits of the PW Ethernet control word must be "00000", but the first four digits of Ethernet may also be "0000"
++ if (layer->pld_len >= sizeof(struct ethhdr) && is_eth_proto(eth_hdr_get_proto((const struct ethhdr *)layer->pld_ptr)))
++ {
++ return parse_ether(pkt, layer->pld_ptr, layer->pld_len);
++ }
++ else
++ {
++ return parse_pweth(pkt, layer->pld_ptr, layer->pld_len);
++ }
++ case 4:
++ return parse_ipv4(pkt, layer->pld_ptr, layer->pld_len);
++ case 6:
++ return parse_ipv6(pkt, layer->pld_ptr, layer->pld_len);
++ default:
++ return parse_ether(pkt, layer->pld_ptr, layer->pld_len);
++ }
++ }
++ else
++ {
++ SET_LAYER(pkt, layer, LAYER_PROTO_MPLS, 4, data, len, 0);
++ return parse_mpls(pkt, layer->pld_ptr, layer->pld_len);
++ }
++}
++
++static inline const char *parse_ipv4(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < sizeof(struct ip)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV4);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++
++ const struct ip *hdr = (const struct ip *)data;
++ uint8_t version = ip4_hdr_get_version(hdr);
++ if (unlikely(version != 4))
++ {
++ PACKET_PARSE_LOG_ERROR("packet %p ipv4 version %d != 4", pkt, version);
++ return data;
++ }
++
++ uint16_t hdr_len = ip4_hdr_get_hdr_len(hdr);
++ if (unlikely(hdr_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV4);
++ return data;
++ }
++
++ uint16_t total_len = ip4_hdr_get_total_len(hdr);
++ if (unlikely(total_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV4);
++ return data;
++ }
++ if (unlikely(total_len < hdr_len))
++ {
++ PACKET_PARSE_LOG_ERROR("packet %p ip total_len %d < hdr_len %d", pkt, total_len, hdr_len);
++ return data;
++ }
++ uint16_t trim_len = len - total_len;
++ SET_LAYER(pkt, layer, LAYER_PROTO_IPV4, hdr_len, data, len, trim_len);
++
++ // ip fragmented
++ if (ip4_hdr_get_mf_flag(hdr) || ip4_hdr_get_frag_offset(hdr))
++ {
++ PACKET_PARSE_LOG_DEBUG("packet %p ip layer %p is fragmented", pkt, layer);
++ pkt->frag_layer = layer;
++ return layer->pld_ptr;
++ }
++
++ uint8_t next_proto = ip4_hdr_get_proto(hdr);
++ return parse_l4(pkt, next_proto, layer->pld_ptr, layer->pld_len);
++}
++
++static inline const char *parse_ipv6(struct packet *pkt, const char *data, uint16_t len)
++{
++ /*
++ * IP6 Extension Headers
++ *
++ * Internet Protocol, Version 6 (IPv6) : https://datatracker.ietf.org/doc/html/rfc2460
++ * IP Encapsulating Security Payload (ESP) : https://datatracker.ietf.org/doc/html/rfc2406
++ * IP Authentication Header : https://datatracker.ietf.org/doc/html/rfc4302
++ *
++ * skip next header
++ * #define IPPROTO_HOPOPTS 0 // IP6 hop-by-hop options
++ * #define IPPROTO_ROUTING 43 // IP6 routing header
++ * #define IPPROTO_AH 51 // IP6 Auth Header
++ * #define IPPROTO_DSTOPTS 60 // IP6 destination option
++ *
++ * not skip next header
++ * #define IPPROTO_FRAGMENT 44 // IP6 fragmentation header
++ * #define IPPROTO_ESP 50 // IP6 Encap Sec. Payload
++ * #define IPPROTO_NONE 59 // IP6 no next header
++ */
++
++ if (unlikely(len < sizeof(struct ip6_hdr)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV6);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ const struct ip6_hdr *hdr = (const struct ip6_hdr *)data;
++ uint8_t version = ip6_hdr_get_version(hdr);
++ if (unlikely(version != 6))
++ {
++ PACKET_PARSE_LOG_ERROR("packet %p ipv6 version %d != 6", pkt, version);
++ return data;
++ }
++
++ uint16_t pld_len = ip6_hdr_get_payload_len(hdr);
++ if (unlikely(pld_len + sizeof(struct ip6_hdr) > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV6);
++ return data;
++ }
++ uint8_t next_proto = ip6_hdr_get_next_header(hdr);
++ uint16_t hdr_len = sizeof(struct ip6_hdr);
++ uint16_t trim_len = len - pld_len - sizeof(struct ip6_hdr);
++ const char *next_hdr_ptr = data + hdr_len;
++ while (next_proto == IPPROTO_HOPOPTS || next_proto == IPPROTO_ROUTING || next_proto == IPPROTO_AH || next_proto == IPPROTO_DSTOPTS)
++ {
++ if (unlikely(pld_len < 2))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV6);
++ return data;
++ }
++ struct ip6_ext *ext = (struct ip6_ext *)next_hdr_ptr;
++ uint16_t skip_len = 0;
++ if (next_proto == IPPROTO_AH)
++ {
++ /*
++ * https://datatracker.ietf.org/doc/html/rfc4302#section-2
++ * For IPv6, the total length of the header must be a multiple of 8-octet units.
++ * (Note that although IPv6 [DH98] characterizes AH as an extension header,
++ * its length is measured in 32-bit words, not the 64-bit words used by other IPv6 extension headers.)
++ */
++ skip_len = ext->ip6e_len * 4 + 8;
++ }
++ else
++ {
++ skip_len = ext->ip6e_len * 8 + 8;
++ }
++ if (unlikely(skip_len > pld_len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV6);
++ return data;
++ }
++ hdr_len += skip_len;
++ pld_len -= skip_len;
++ next_hdr_ptr += skip_len;
++ next_proto = ext->ip6e_nxt;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_IPV6, hdr_len, data, len, trim_len);
++
++ // ipv6 fragment
++ if (next_proto == IPPROTO_FRAGMENT)
++ {
++ PACKET_PARSE_LOG_DEBUG("packet %p ipv6 layer %p is fragmented", pkt, layer);
++ pkt->frag_layer = layer;
++ return layer->pld_ptr;
++ }
++
++ return parse_l4(pkt, next_proto, layer->pld_ptr, layer->pld_len);
++}
++
++static inline const char *parse_auth(struct packet *pkt, const char *data, uint16_t len)
++{
++ /*
++ * https://datatracker.ietf.org/doc/html/rfc4302#section-2
++ * For IPv4: AH not IPv4 option, as an single layer
++ * For IPv6: AH as IPv6 extension header
++ *
++ * AH Format
++ *
++ * 0 1 2 3
++ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | Next Header | Payload Len | RESERVED |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | Security Parameters Index (SPI) |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | Sequence Number Field |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | |
++ * + Integrity Check Value-ICV (variable) |
++ * | |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ */
++
++ if (unlikely(len < 12))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPAH);
++ return data;
++ }
++ /*
++ * https://datatracker.ietf.org/doc/html/rfc4302#section-2
++ * For IPv4, This 8-bit field specifies the length of AH in 32-bit words (4-byte units), minus "2".
++ * Thus, for example, if an integrity algorithm yields a 96-bit authentication value,
++ * this length field will be "4" (3 32-bit word fixed fields plus 3 32-bit words for the ICV, minus 2).
++ */
++ uint8_t next_proto = ((const uint8_t *)data)[0];
++ uint16_t hdr_len = ((const uint8_t *)data)[1] * 4 + 8;
++ if (unlikely(len < hdr_len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPAH);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_IPAH, hdr_len, data, len, 0);
++
++ return parse_l4(pkt, next_proto, layer->pld_ptr, layer->pld_len);
++}
++
++static inline const char *parse_gre(struct packet *pkt, const char *data, uint16_t len)
++{
++ uint16_t hdr_len = calc_gre_hdr_len(data, len);
++ if (unlikely(hdr_len == 0 || hdr_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_GRE);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ uint16_t next_proto = peek_gre_proto(data, len);
++ SET_LAYER(pkt, layer, LAYER_PROTO_GRE, hdr_len, data, len, 0);
++
++ return parse_l3(pkt, next_proto, layer->pld_ptr, layer->pld_len);
++}
++
++static inline const char *parse_udp(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < sizeof(struct udphdr)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_UDP);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ const struct udphdr *udp_hdr = (struct udphdr *)data;
++ SET_LAYER(pkt, layer, LAYER_PROTO_UDP, sizeof(struct udphdr), data, len, 0);
++
++ uint16_t src_port = udp_hdr_get_src_port(udp_hdr);
++ uint16_t dst_port = udp_hdr_get_dst_port(udp_hdr);
++
++ if (dst_port == 4789)
++ {
++ // VXLAN_DPORT 4789
++ return parse_vxlan(pkt, layer->pld_ptr, layer->pld_len);
++ }
++
++ if (dst_port == 2152 || src_port == 2152)
++ {
++ // only GTPv1-U, no GTPv2-U
++ return parse_gtp_u(pkt, layer->pld_ptr, layer->pld_len);
++ }
++
++ if (dst_port == 2123 || src_port == 2123)
++ {
++ // GTPv1-C or GTPv2-C
++ return parse_gtp_c(pkt, layer->pld_ptr, layer->pld_len);
++ }
++
++ if (dst_port == 1701 || src_port == 1701)
++ {
++ // L2TP_DPORT 1701
++ if (unlikely(layer->pld_len < 8))
++ {
++ return layer->pld_ptr;
++ }
++
++ switch (l2tp_hdr_get_ver((const struct l2tp_hdr *)layer->pld_ptr))
++ {
++ case 2:
++ return parse_l2tpv2_over_udp(pkt, layer->pld_ptr, layer->pld_len);
++ case 3:
++ return parse_l2tpv3_over_udp(pkt, layer->pld_ptr, layer->pld_len);
++ default:
++ return layer->pld_ptr;
++ }
++ }
++
++ if (dst_port == 3544 || src_port == 3544)
++ {
++ // Teredo IPv6 tunneling 3544
++ if (unlikely(layer->pld_len < sizeof(struct ip6_hdr)))
++ {
++ return layer->pld_ptr;
++ }
++ const struct ip6_hdr *ipv6_hdr = (const struct ip6_hdr *)layer->pld_ptr;
++ if (ip6_hdr_get_version(ipv6_hdr) != 6)
++ {
++ return layer->pld_ptr;
++ }
++ return parse_ipv6(pkt, layer->pld_ptr, layer->pld_len);
++ }
++
++ return layer->pld_ptr;
++}
++
++static inline const char *parse_tcp(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < sizeof(struct tcphdr)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_TCP);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ uint16_t hdr_len = tcp_hdr_get_hdr_len((const struct tcphdr *)data);
++ if (unlikely(hdr_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_TCP);
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_TCP, hdr_len, data, len, 0);
++
++ return layer->pld_ptr;
++}
++
++static inline const char *parse_icmp(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < sizeof(struct icmphdr)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_ICMP);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_ICMP, sizeof(struct icmphdr), data, len, 0);
++
++ return layer->pld_ptr;
++}
++
++static inline const char *parse_icmp6(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < sizeof(struct icmp6_hdr)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_ICMP6);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_ICMP6, sizeof(struct icmp6_hdr), data, len, 0);
++
++ return layer->pld_ptr;
++}
++
++static inline const char *parse_vxlan(struct packet *pkt, const char *data, uint16_t len)
++{
++ if (unlikely(len < sizeof(struct vxlan_hdr)))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_VXLAN);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_VXLAN, sizeof(struct vxlan_hdr), data, len, 0);
++
++ return parse_ether(pkt, layer->pld_ptr, layer->pld_len);
++}
++
++static inline const char *parse_gtp_u(struct packet *pkt, const char *data, uint16_t len)
++{
++ // only GTPv1-U, no GTPv2-U
++ uint8_t version = peek_gtp_version(data, len);
++ if (unlikely(version != 1))
++ {
++ return data;
++ }
++
++ uint16_t hdr_len = calc_gtp_hdr_len(data, len);
++ if (unlikely(hdr_len == 0 || hdr_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_GTP_U);
++ return data;
++ }
++
++ uint8_t next_proto = (((const uint8_t *)(data + hdr_len))[0]) >> 4;
++ if (next_proto != 4 && next_proto != 6)
++ {
++ // next_proto is not IPv4 or IPv6, this is not a normal GTP-U packet, fallback to UDP
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_GTP_U, hdr_len, data, len, 0);
++
++ switch (next_proto)
++ {
++ case 4:
++ return parse_ipv4(pkt, layer->pld_ptr, layer->pld_len);
++ case 6:
++ return parse_ipv6(pkt, layer->pld_ptr, layer->pld_len);
++ default:
++ PACKET_LOG_UNSUPPORT_PROTO(pkt, LAYER_PROTO_GTP_U, next_proto);
++ return layer->pld_ptr;
++ }
++}
++
++static inline const char *parse_gtp_c(struct packet *pkt, const char *data, uint16_t len)
++{
++ // GTPv1-C or GTPv2-C
++ uint16_t hdr_len = calc_gtp_hdr_len(data, len);
++ if (unlikely(hdr_len == 0 || hdr_len > len))
++ {
++ PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_GTP_C);
++ return data;
++ }
++
++ struct layer_private *layer = get_free_layer(pkt);
++ if (unlikely(layer == NULL))
++ {
++ return data;
++ }
++ SET_LAYER(pkt, layer, LAYER_PROTO_GTP_C, hdr_len, data, len, 0);
++
++ return layer->pld_ptr;
++}
++
++static inline const char *parse_l3(struct packet *pkt, uint16_t next_proto, const char *data, uint16_t len)
++{
++ switch (next_proto)
++ {
++ case ETH_P_8021Q:
++ case ETH_P_8021AD:
++ return parse_vlan(pkt, data, len);
++ case ETH_P_IP:
++ return parse_ipv4(pkt, data, len);
++ case ETH_P_IPV6:
++ return parse_ipv6(pkt, data, len);
++ case ETH_P_PPP_SES:
++ return parse_pppoe_ses(pkt, data, len);
++ case ETH_P_MPLS_UC:
++ return parse_mpls(pkt, data, len);
++ case 0x880b:
++ return parse_ppp(pkt, data, len);
++ default:
++ PACKET_LOG_UNSUPPORT_ETHPROTO(pkt, next_proto);
++ return data;
++ }
++}
++
++static inline const char *parse_l4(struct packet *pkt, uint8_t next_proto, const char *data, uint16_t len)
++{
++ switch (next_proto)
++ {
++ case IPPROTO_AH:
++ return parse_auth(pkt, data, len);
++ case IPPROTO_TCP:
++ return parse_tcp(pkt, data, len);
++ case IPPROTO_UDP:
++ return parse_udp(pkt, data, len);
++ case IPPROTO_IPIP:
++ return parse_ipv4(pkt, data, len);
++ case IPPROTO_IPV6:
++ return parse_ipv6(pkt, data, len);
++ case IPPROTO_GRE:
++ return parse_gre(pkt, data, len);
++ case IPPROTO_ICMP:
++ return parse_icmp(pkt, data, len);
++ case IPPROTO_ICMPV6:
++ return parse_icmp6(pkt, data, len);
++ case 115:
++ // L2TP
++ return parse_l2tpv3_over_ip(pkt, data, len);
++ default:
++ PACKET_LOG_UNSUPPORT_IPPROTO(pkt, next_proto);
++ return data;
++ }
++}
++
++/******************************************************************************
++ * Public API
++ ******************************************************************************/
++
++// return innermost payload
++const char *packet_parse(struct packet *pkt, const char *data, uint16_t len)
++{
++ pkt->frag_layer = NULL;
++ pkt->layers_used = 0;
++ pkt->layers_size = PACKET_MAX_LAYERS;
++ pkt->data_ptr = data;
++ pkt->data_len = len;
++ pkt->trim_len = 0;
++
++ return parse_ether(pkt, data, len);
++}
++
++const char *layer_proto_to_str(enum layer_proto proto)
++{
++ switch (proto)
++ {
++ case LAYER_PROTO_ETHER:
++ return "ETH";
++ case LAYER_PROTO_PWETH:
++ return "PWETH";
++ case LAYER_PROTO_PPP:
++ return "PPP";
++ case LAYER_PROTO_L2TP:
++ return "L2TP";
++ case LAYER_PROTO_VLAN:
++ return "VLAN";
++ case LAYER_PROTO_PPPOE:
++ return "PPPOE";
++ case LAYER_PROTO_MPLS:
++ return "MPLS";
++ case LAYER_PROTO_IPV4:
++ return "IPV4";
++ case LAYER_PROTO_IPV6:
++ return "IPV6";
++ case LAYER_PROTO_IPAH:
++ return "IPAH";
++ case LAYER_PROTO_GRE:
++ return "GRE";
++ case LAYER_PROTO_UDP:
++ return "UDP";
++ case LAYER_PROTO_TCP:
++ return "TCP";
++ case LAYER_PROTO_ICMP:
++ return "ICMP";
++ case LAYER_PROTO_ICMP6:
++ return "ICMP6";
++ case LAYER_PROTO_VXLAN:
++ return "VXLAN";
++ case LAYER_PROTO_GTP_C:
++ return "GTP-C";
++ case LAYER_PROTO_GTP_U:
++ return "GTP-U";
++ default:
++ return "UNKNOWN";
++ }
++}
+\ No newline at end of file
+diff -uNr tcpdump-tcpdump-4.99.4/packet_utils.c tcpdump-for-stellar/packet_utils.c
+--- tcpdump-tcpdump-4.99.4/packet_utils.c 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/packet_utils.c 2024-10-30 17:34:06.265498697 +0800
+@@ -0,0 +1,966 @@
++#include <assert.h>
++
++#include "tuple.h"
++#include "uthash.h"
++#include "log_internal.h"
++#include "packet_helper.h"
++#include "packet_internal.h"
++#include "stellar/exdata.h"
++
++#define PACKET_LOG_ERROR(format, ...) STELLAR_LOG_ERROR(__thread_local_logger, "packet", format, ##__VA_ARGS__)
++
++/******************************************************************************
++ * metadata utils
++ ******************************************************************************/
++
++void packet_set_route_ctx(struct packet *pkt, const struct route_ctx *ctx)
++{
++ pkt->meta.route_ctx = *ctx;
++}
++
++const struct route_ctx *packet_get_route_ctx(const struct packet *pkt)
++{
++ return &pkt->meta.route_ctx;
++}
++
++void packet_set_origin_ctx(struct packet *pkt, void *ctx)
++{
++ pkt->meta.origin_ctx = ctx;
++}
++
++const void *packet_get_origin_ctx(const struct packet *pkt)
++{
++ return pkt->meta.origin_ctx;
++}
++
++void packet_set_sids(struct packet *pkt, const struct sids *sids)
++{
++ pkt->meta.sids = *sids;
++}
++
++const struct sids *packet_get_sids(const struct packet *pkt)
++{
++ return &pkt->meta.sids;
++}
++
++void packet_prepend_sids(struct packet *pkt, const struct sids *sids)
++{
++ if (pkt->meta.sids.used + sids->used > MAX_SIDS)
++ {
++ PACKET_LOG_ERROR("sids overflow");
++ return;
++ }
++ else
++ {
++ for (int i = pkt->meta.sids.used - 1; i >= 0; i--)
++ {
++ pkt->meta.sids.sid[i + sids->used] = pkt->meta.sids.sid[i];
++ }
++ for (int i = 0; i < sids->used; i++)
++ {
++ pkt->meta.sids.sid[i] = sids->sid[i];
++ }
++ pkt->meta.sids.used += sids->used;
++ }
++}
++
++void packet_set_session_id(struct packet *pkt, uint64_t id)
++{
++ pkt->meta.session_id = id;
++}
++
++uint64_t packet_get_session_id(const struct packet *pkt)
++{
++ return pkt->meta.session_id;
++}
++
++void packet_set_domain(struct packet *pkt, uint64_t domain)
++{
++ pkt->meta.domain = domain;
++}
++
++uint64_t packet_get_domain(const struct packet *pkt)
++{
++ return pkt->meta.domain;
++}
++
++void packet_set_link_id(struct packet *pkt, uint16_t id)
++{
++ pkt->meta.link_id = id;
++}
++
++uint16_t packet_get_link_id(const struct packet *pkt)
++{
++ return pkt->meta.link_id;
++}
++
++void packet_set_ctrl(struct packet *pkt, bool ctrl)
++{
++ pkt->meta.is_ctrl = ctrl;
++}
++
++bool packet_is_ctrl(const struct packet *pkt)
++{
++ return pkt->meta.is_ctrl;
++}
++
++void packet_set_claim(struct packet *pkt, bool claim)
++{
++ pkt->meta.is_claim = claim;
++}
++
++bool packet_is_claim(const struct packet *pkt)
++{
++ return pkt->meta.is_claim;
++}
++
++void packet_set_direction(struct packet *pkt, enum packet_direction dir)
++{
++ pkt->meta.direction = dir;
++}
++
++enum packet_direction packet_get_direction(const struct packet *pkt)
++{
++ return pkt->meta.direction;
++}
++
++void packet_set_action(struct packet *pkt, enum packet_action action)
++{
++ pkt->meta.action = action;
++}
++
++enum packet_action packet_get_action(const struct packet *pkt)
++{
++ return pkt->meta.action;
++}
++
++void packet_set_timeval(struct packet *pkt, const struct timeval *tv)
++{
++ pkt->meta.tv = *tv;
++}
++
++const struct timeval *packet_get_timeval(const struct packet *pkt)
++{
++ return &pkt->meta.tv;
++}
++
++void packet_set_user_data(struct packet *pkt, void *data)
++{
++ pkt->user_data = data;
++}
++
++void *packet_get_user_data(struct packet *pkt)
++{
++ return pkt->user_data;
++}
++
++/******************************************************************************
++ * tuple uitls
++ ******************************************************************************/
++
++// return 0 : found
++// return -1 : not found
++int packet_get_innermost_tuple2(const struct packet *pkt, struct tuple2 *tuple)
++{
++ memset(tuple, 0, sizeof(struct tuple2));
++ const struct layer_private *layer = NULL;
++
++ for (int8_t i = pkt->layers_used - 1; i >= 0; i--)
++ {
++ layer = &pkt->layers[i];
++
++ if (layer->proto == LAYER_PROTO_IPV4)
++ {
++ const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET;
++ tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
++ tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
++ return 0;
++ }
++ if (layer->proto == LAYER_PROTO_IPV6)
++ {
++ const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET6;
++ tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
++ tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
++ return 0;
++ }
++ }
++
++ return -1;
++}
++
++// return 0 : found
++// return -1 : not found
++int packet_get_outermost_tuple2(const struct packet *pkt, struct tuple2 *tuple)
++{
++ memset(tuple, 0, sizeof(struct tuple2));
++ const struct layer_private *layer = NULL;
++
++ for (int8_t i = 0; i < pkt->layers_used; i++)
++ {
++ layer = &pkt->layers[i];
++
++ if (layer->proto == LAYER_PROTO_IPV4)
++ {
++ const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET;
++ tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
++ tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
++ return 0;
++ }
++ if (layer->proto == LAYER_PROTO_IPV6)
++ {
++ const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET6;
++ tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
++ tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
++ return 0;
++ }
++ }
++
++ return -1;
++}
++
++// return 0 : found
++// return -1 : not found
++int packet_get_innermost_tuple4(const struct packet *pkt, struct tuple4 *tuple)
++{
++ memset(tuple, 0, sizeof(struct tuple4));
++ const struct layer_private *layer_l3 = NULL;
++ const struct layer_private *layer_l4 = NULL;
++ const struct layer_private *layer = NULL;
++
++ for (int8_t i = pkt->layers_used - 1; i >= 0; i--)
++ {
++ layer = &pkt->layers[i];
++
++ // first get L4 layer
++ if (layer->proto == LAYER_PROTO_UDP)
++ {
++ const struct udphdr *udp_hdr = (const struct udphdr *)layer->hdr_ptr;
++ tuple->src_port = udp_hdr->uh_sport;
++ tuple->dst_port = udp_hdr->uh_dport;
++ layer_l4 = layer;
++ continue;
++ }
++ if (layer->proto == LAYER_PROTO_TCP)
++ {
++ const struct tcphdr *tcp_hdr = (const struct tcphdr *)layer->hdr_ptr;
++ tuple->src_port = tcp_hdr->th_sport;
++ tuple->dst_port = tcp_hdr->th_dport;
++ layer_l4 = layer;
++ continue;
++ }
++
++ // second get L3 layer
++ if (layer->proto == LAYER_PROTO_IPV4)
++ {
++ const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET;
++ tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
++ tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
++ layer_l3 = layer;
++ break;
++ }
++ if (layer->proto == LAYER_PROTO_IPV6)
++ {
++ const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET6;
++ tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
++ tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
++ layer_l3 = layer;
++ break;
++ }
++ }
++
++ if (layer_l3 && layer_l4 && layer_l4 - layer_l3 == 1)
++ {
++ return 0;
++ }
++ else
++ {
++ return -1;
++ }
++}
++
++// return 0 : found
++// return -1 : not found
++int packet_get_outermost_tuple4(const struct packet *pkt, struct tuple4 *tuple)
++{
++ memset(tuple, 0, sizeof(struct tuple4));
++ const struct layer_private *layer_l3 = NULL;
++ const struct layer_private *layer_l4 = NULL;
++ const struct layer_private *layer = NULL;
++
++ for (int8_t i = 0; i < pkt->layers_used; i++)
++ {
++ layer = &pkt->layers[i];
++
++ // first get L3 layer
++ if (layer->proto == LAYER_PROTO_IPV4)
++ {
++ const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET;
++ tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
++ tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
++ layer_l3 = layer;
++ continue;
++ }
++ if (layer->proto == LAYER_PROTO_IPV6)
++ {
++ const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET6;
++ tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
++ tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
++ layer_l3 = layer;
++ continue;
++ }
++
++ // second get L4 layer
++ if (layer->proto == LAYER_PROTO_UDP)
++ {
++ const struct udphdr *udp_hdr = (const struct udphdr *)layer->hdr_ptr;
++ tuple->src_port = udp_hdr->uh_sport;
++ tuple->dst_port = udp_hdr->uh_dport;
++ layer_l4 = layer;
++ break;
++ }
++ if (layer->proto == LAYER_PROTO_TCP)
++ {
++ const struct tcphdr *tcp_hdr = (const struct tcphdr *)layer->hdr_ptr;
++ tuple->src_port = tcp_hdr->th_sport;
++ tuple->dst_port = tcp_hdr->th_dport;
++ layer_l4 = layer;
++ break;
++ }
++ }
++
++ if (layer_l3 && layer_l4 && layer_l4 - layer_l3 == 1)
++ {
++ return 0;
++ }
++ else
++ {
++ return -1;
++ }
++}
++
++// return 0 : found
++// return -1 : not found
++int packet_get_innermost_tuple6(const struct packet *pkt, struct tuple6 *tuple)
++{
++ memset(tuple, 0, sizeof(struct tuple6));
++ const struct layer_private *layer_l3 = NULL;
++ const struct layer_private *layer_l4 = NULL;
++ const struct layer_private *layer = NULL;
++
++ for (int8_t i = pkt->layers_used - 1; i >= 0; i--)
++ {
++ layer = &pkt->layers[i];
++
++ // first get L4 layer
++ if (layer->proto == LAYER_PROTO_UDP)
++ {
++ const struct udphdr *udp_hdr = (const struct udphdr *)layer->hdr_ptr;
++ tuple->ip_proto = IPPROTO_UDP;
++ tuple->src_port = udp_hdr->uh_sport;
++ tuple->dst_port = udp_hdr->uh_dport;
++ layer_l4 = layer;
++ continue;
++ }
++ if (layer->proto == LAYER_PROTO_TCP)
++ {
++ const struct tcphdr *tcp_hdr = (const struct tcphdr *)layer->hdr_ptr;
++ tuple->ip_proto = IPPROTO_TCP;
++ tuple->src_port = tcp_hdr->th_sport;
++ tuple->dst_port = tcp_hdr->th_dport;
++ layer_l4 = layer;
++ continue;
++ }
++
++ // second get L3 layer
++ if (layer->proto == LAYER_PROTO_IPV4)
++ {
++ const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET;
++ tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
++ tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
++ layer_l3 = layer;
++ break;
++ }
++ if (layer->proto == LAYER_PROTO_IPV6)
++ {
++ const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET6;
++ tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
++ tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
++ layer_l3 = layer;
++ break;
++ }
++ }
++
++ if (layer_l3 && layer_l4 && layer_l4 - layer_l3 == 1)
++ {
++ tuple->domain = packet_get_domain(pkt);
++ return 0;
++ }
++ else
++ {
++ return -1;
++ }
++}
++
++// return 0 : found
++// return -1 : not found
++int packet_get_outermost_tuple6(const struct packet *pkt, struct tuple6 *tuple)
++{
++ memset(tuple, 0, sizeof(struct tuple6));
++ const struct layer_private *layer_l3 = NULL;
++ const struct layer_private *layer_l4 = NULL;
++ const struct layer_private *layer = NULL;
++
++ for (int8_t i = 0; i < pkt->layers_used; i++)
++ {
++ layer = &pkt->layers[i];
++
++ // first get L3 layer
++ if (layer->proto == LAYER_PROTO_IPV4)
++ {
++ const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET;
++ tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
++ tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
++ layer_l3 = layer;
++ continue;
++ }
++ if (layer->proto == LAYER_PROTO_IPV6)
++ {
++ const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
++ tuple->addr_family = AF_INET6;
++ tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
++ tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
++ layer_l3 = layer;
++ continue;
++ }
++
++ // second get L4 layer
++ if (layer->proto == LAYER_PROTO_UDP)
++ {
++ const struct udphdr *udp_hdr = (const struct udphdr *)layer->hdr_ptr;
++ tuple->ip_proto = IPPROTO_UDP;
++ tuple->src_port = udp_hdr->uh_sport;
++ tuple->dst_port = udp_hdr->uh_dport;
++ layer_l4 = layer;
++ break;
++ }
++ if (layer->proto == LAYER_PROTO_TCP)
++ {
++ const struct tcphdr *tcp_hdr = (const struct tcphdr *)layer->hdr_ptr;
++ tuple->ip_proto = IPPROTO_TCP;
++ tuple->src_port = tcp_hdr->th_sport;
++ tuple->dst_port = tcp_hdr->th_dport;
++ layer_l4 = layer;
++ break;
++ }
++ }
++
++ if (layer_l3 && layer_l4 && layer_l4 - layer_l3 == 1)
++ {
++ tuple->domain = packet_get_domain(pkt);
++ return 0;
++ }
++ else
++ {
++ return -1;
++ }
++}
++
++/******************************************************************************
++ * layer uitls
++ ******************************************************************************/
++
++int packet_get_layer_count(const struct packet *pkt)
++{
++ return pkt->layers_used;
++}
++
++const struct layer *packet_get_layer_by_idx(const struct packet *pkt, int idx)
++{
++ const struct layer_private *layer = packet_get_layer(pkt, idx);
++ if (layer == NULL)
++ {
++ return NULL;
++ }
++ else
++ {
++ return (const struct layer *)layer;
++ }
++}
++
++const struct layer_private *packet_get_layer(const struct packet *pkt, int idx)
++{
++ if (idx < 0 || idx >= pkt->layers_used)
++ {
++ return NULL;
++ }
++ return &pkt->layers[idx];
++}
++
++const struct layer_private *packet_get_innermost_layer(const struct packet *pkt, enum layer_proto proto)
++{
++ const struct layer_private *layer = NULL;
++
++ for (int8_t i = pkt->layers_used - 1; i >= 0; i--)
++ {
++ layer = &pkt->layers[i];
++ if (layer->proto == proto)
++ {
++ return layer;
++ }
++ }
++
++ return NULL;
++}
++
++const struct layer_private *packet_get_outermost_layer(const struct packet *pkt, enum layer_proto proto)
++{
++ const struct layer_private *layer = NULL;
++
++ for (int8_t i = 0; i < pkt->layers_used; i++)
++ {
++ layer = &pkt->layers[i];
++ if (layer->proto == proto)
++ {
++ return layer;
++ }
++ }
++
++ return NULL;
++}
++
++/******************************************************************************
++ * tunnel uitls
++ ******************************************************************************/
++
++struct tunnel_detector
++{
++ enum tunnel_type type;
++ int contain_layers;
++ int (*identify_func)(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2);
++};
++
++static int is_ipv4_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2 __attribute__((unused)))
++{
++ if (curr && curr->proto == LAYER_PROTO_IPV4 &&
++ next1 && (next1->proto == LAYER_PROTO_IPV4 || next1->proto == LAYER_PROTO_IPV6))
++ {
++ return 1;
++ }
++
++ return 0;
++}
++
++static int is_ipv6_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2 __attribute__((unused)))
++{
++ if (curr && curr->proto == LAYER_PROTO_IPV6 &&
++ next1 && (next1->proto == LAYER_PROTO_IPV4 || next1->proto == LAYER_PROTO_IPV6))
++ {
++ return 1;
++ }
++
++ return 0;
++}
++
++static int is_gre_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2 __attribute__((unused)))
++{
++ if (curr && (curr->proto == LAYER_PROTO_IPV4 || curr->proto == LAYER_PROTO_IPV6) &&
++ next1 && next1->proto == LAYER_PROTO_GRE)
++ {
++ return 1;
++ }
++
++ return 0;
++}
++
++static int is_gtp_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
++{
++ if (curr && (curr->proto == LAYER_PROTO_IPV4 || curr->proto == LAYER_PROTO_IPV6) &&
++ next1 && next1->proto == LAYER_PROTO_UDP &&
++ next2 && next2->proto == LAYER_PROTO_GTP_U)
++ {
++ return 1;
++ }
++
++ return 0;
++}
++
++static int is_vxlan_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
++{
++ if (curr && (curr->proto == LAYER_PROTO_IPV4 || curr->proto == LAYER_PROTO_IPV6) &&
++ next1 && next1->proto == LAYER_PROTO_UDP &&
++ next2 && next2->proto == LAYER_PROTO_VXLAN)
++ {
++ return 1;
++ }
++
++ return 0;
++}
++
++static int is_l2tp_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
++{
++ if (curr && (curr->proto == LAYER_PROTO_IPV4 || curr->proto == LAYER_PROTO_IPV6) &&
++ next1 && next1->proto == LAYER_PROTO_UDP &&
++ next2 && next2->proto == LAYER_PROTO_L2TP)
++ {
++ return 1;
++ }
++
++ return 0;
++}
++
++static int is_teredo_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
++{
++ if (curr && curr->proto == LAYER_PROTO_IPV4 &&
++ next1 && next1->proto == LAYER_PROTO_UDP &&
++ next2 && next2->proto == LAYER_PROTO_IPV6)
++ {
++ return 1;
++ }
++
++ return 0;
++}
++
++static struct tunnel_detector detectors[] = {
++ {TUNNEL_IPV4, 1, is_ipv4_tunnel},
++ {TUNNEL_IPV6, 1, is_ipv6_tunnel},
++ {TUNNEL_GRE, 2, is_gre_tunnel},
++ {TUNNEL_GTP, 3, is_gtp_tunnel},
++ {TUNNEL_VXLAN, 3, is_vxlan_tunnel},
++ {TUNNEL_L2TP, 3, is_l2tp_tunnel},
++ {TUNNEL_TEREDO, 2, is_teredo_tunnel},
++};
++
++// return index of detectors
++static int detect_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
++{
++ for (int i = 0; i < (int)(sizeof(detectors) / sizeof(detectors[0])); i++)
++ {
++ if (detectors[i].identify_func(curr, next1, next2))
++ {
++ return i;
++ }
++ }
++
++ return -1;
++}
++
++int packet_get_tunnel_count(const struct packet *pkt)
++{
++ int count = 0;
++ int used = packet_get_layer_count(pkt);
++ const struct layer_private *curr = NULL;
++ const struct layer_private *next1 = NULL;
++ const struct layer_private *next2 = NULL;
++
++ for (int i = 0; i < used; i++)
++ {
++ curr = packet_get_layer(pkt, i);
++ next1 = packet_get_layer(pkt, i + 1);
++ next2 = packet_get_layer(pkt, i + 2);
++ if (detect_tunnel(curr, next1, next2) >= 0)
++ {
++ count++;
++ }
++ }
++
++ return count;
++}
++
++// return 0: success 
++// return -1: failed
++int packet_get_tunnel_by_idx(const struct packet *pkt, int idx, struct tunnel *out)
++{
++ int ret = -1;
++ int count = 0;
++ int used = packet_get_layer_count(pkt);
++ const struct layer_private *curr = NULL;
++ const struct layer_private *next1 = NULL;
++ const struct layer_private *next2 = NULL;
++ memset(out, 0, sizeof(struct tunnel));
++
++ for (int i = 0; i < used; i++)
++ {
++ curr = packet_get_layer(pkt, i);
++ next1 = packet_get_layer(pkt, i + 1);
++ next2 = packet_get_layer(pkt, i + 2);
++ ret = detect_tunnel(curr, next1, next2);
++ if (ret >= 0 && count++ == idx)
++ {
++ struct tunnel_detector *hit = &detectors[ret];
++ out->type = hit->type;
++ out->layer_count = hit->contain_layers;
++ if (out->layer_count >= 1)
++ out->layers[0] = (const struct layer *)curr;
++ if (out->layer_count >= 2)
++ out->layers[1] = (const struct layer *)next1;
++ if (out->layer_count >= 3)
++ out->layers[2] = (const struct layer *)next2;
++ return 0;
++ }
++ }
++
++ return -1;
++}
++
++/******************************************************************************
++ * load balance uitls
++ ******************************************************************************/
++
++uint64_t packet_ldbc_hash(const struct packet *pkt, enum packet_load_balance_method method, enum packet_direction direction)
++{
++ uint64_t temp = 0;
++ uint64_t hash_value = 1;
++
++ int inner_addr_len = 0;
++ int outer_addr_len = 0;
++ const char *inner_src_addr = NULL;
++ const char *inner_dst_addr = NULL;
++ const char *outer_src_addr = NULL;
++ const char *outer_dst_addr = NULL;
++
++ struct tuple2 inner_addr;
++ struct tuple2 outer_addr;
++
++ if (pkt == NULL)
++ {
++ return hash_value;
++ }
++
++ if (packet_get_innermost_tuple2(pkt, &inner_addr) == -1)
++ {
++ return hash_value;
++ }
++
++ if (packet_get_outermost_tuple2(pkt, &outer_addr) == -1)
++ {
++ return hash_value;
++ }
++
++ if (inner_addr.addr_family == AF_INET)
++ {
++ inner_src_addr = (const char *)&inner_addr.src_addr.v4;
++ inner_dst_addr = (const char *)&inner_addr.dst_addr.v4;
++ inner_addr_len = sizeof(struct in_addr);
++ }
++ else
++ {
++ inner_src_addr = (const char *)&inner_addr.src_addr.v6;
++ inner_dst_addr = (const char *)&inner_addr.dst_addr.v6;
++ inner_addr_len = sizeof(struct in6_addr);
++ }
++
++ if (outer_addr.addr_family == AF_INET)
++ {
++ outer_src_addr = (const char *)&outer_addr.src_addr.v4;
++ outer_dst_addr = (const char *)&outer_addr.dst_addr.v4;
++ outer_addr_len = sizeof(struct in_addr);
++ }
++ else
++ {
++ outer_src_addr = (const char *)&outer_addr.src_addr.v6;
++ outer_dst_addr = (const char *)&outer_addr.dst_addr.v6;
++ outer_addr_len = sizeof(struct in6_addr);
++ }
++
++ switch (method)
++ {
++ case PKT_LDBC_METH_OUTERMOST_INT_IP:
++ if (direction == PACKET_DIRECTION_INCOMING)
++ {
++ // direction 1: E2I
++ HASH_VALUE(outer_dst_addr, outer_addr_len, hash_value);
++ }
++ else
++ {
++ // direction 0: I2E
++ HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
++ }
++ break;
++ case PKT_LDBC_METH_OUTERMOST_EXT_IP:
++ if (direction == PACKET_DIRECTION_INCOMING)
++ {
++ // direction 1: E2I
++ HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
++ }
++ else
++ {
++ // direction 0: I2E
++ HASH_VALUE(outer_dst_addr, outer_addr_len, hash_value);
++ }
++ break;
++ case PKT_LDBC_METH_OUTERMOST_INT_EXT_IP:
++ HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
++ HASH_VALUE(outer_dst_addr, outer_addr_len, temp);
++ hash_value = hash_value ^ temp;
++ break;
++ case PKT_LDBC_METH_INNERMOST_INT_IP:
++ if (direction == PACKET_DIRECTION_INCOMING)
++ {
++ // direction 1: E2I
++ HASH_VALUE(inner_dst_addr, inner_addr_len, hash_value);
++ }
++ else
++ {
++ // direction 0: I2E
++ HASH_VALUE(inner_src_addr, inner_addr_len, hash_value);
++ }
++ break;
++ case PKT_LDBC_METH_INNERMOST_EXT_IP:
++ if (direction == PACKET_DIRECTION_INCOMING)
++ {
++ // direction 1: E2I
++ HASH_VALUE(inner_src_addr, inner_addr_len, hash_value);
++ }
++ else
++ {
++ // direction 0: I2E
++ HASH_VALUE(inner_dst_addr, inner_addr_len, hash_value);
++ }
++ break;
++ default:
++ return hash_value;
++ }
++
++ return hash_value;
++}
++
++/******************************************************************************
++ * other uitls
++ ******************************************************************************/
++
++const char *packet_get_raw_data(const struct packet *pkt)
++{
++ return pkt->data_ptr;
++}
++
++uint16_t packet_get_raw_len(const struct packet *pkt)
++{
++ return pkt->data_len;
++}
++
++const char *packet_get_payload(const struct packet *pkt)
++{
++ if (pkt == NULL || pkt->layers_used == 0)
++ {
++ return NULL;
++ }
++
++ if (pkt->layers[pkt->layers_used - 1].pld_len)
++ {
++ return pkt->layers[pkt->layers_used - 1].pld_ptr;
++ }
++ else
++ {
++ return NULL;
++ }
++}
++
++uint16_t packet_get_payload_len(const struct packet *pkt)
++{
++ if (pkt == NULL || pkt->layers_used == 0)
++ {
++ return 0;
++ }
++ return pkt->layers[pkt->layers_used - 1].pld_len;
++}
++
++struct packet *packet_new(uint16_t pkt_len)
++{
++ struct packet *pkt = (struct packet *)calloc(1, sizeof(struct packet) + pkt_len);
++ if (pkt == NULL)
++ {
++ return NULL;
++ }
++ pkt->data_len = pkt_len;
++ pkt->data_ptr = (const char *)pkt + sizeof(struct packet);
++ pkt->need_free = 1;
++
++ return pkt;
++}
++
++struct packet *packet_dup(const struct packet *pkt)
++{
++ if (pkt == NULL)
++ {
++ return NULL;
++ }
++
++ struct packet *dup_pkt = packet_new(pkt->data_len);
++ if (dup_pkt == NULL)
++ {
++ return NULL;
++ }
++
++ dup_pkt->layers_used = pkt->layers_used;
++ dup_pkt->layers_size = pkt->layers_size;
++
++ memcpy((char *)dup_pkt->data_ptr, pkt->data_ptr, pkt->data_len);
++ dup_pkt->data_len = pkt->data_len;
++ packet_set_action(dup_pkt, PACKET_ACTION_DROP);
++
++ for (int8_t i = 0; i < pkt->layers_used; i++)
++ {
++ dup_pkt->layers[i].proto = pkt->layers[i].proto;
++ dup_pkt->layers[i].hdr_ptr = dup_pkt->data_ptr + pkt->layers[i].hdr_offset;
++ dup_pkt->layers[i].pld_ptr = dup_pkt->data_ptr + pkt->layers[i].hdr_offset + pkt->layers[i].hdr_len;
++ dup_pkt->layers[i].hdr_offset = pkt->layers[i].hdr_offset;
++ dup_pkt->layers[i].hdr_len = pkt->layers[i].hdr_len;
++ dup_pkt->layers[i].pld_len = pkt->layers[i].pld_len;
++ }
++
++ // update frag_layer
++ if (pkt->frag_layer)
++ {
++ dup_pkt->frag_layer = &dup_pkt->layers[pkt->frag_layer - pkt->layers];
++ }
++ memcpy(&dup_pkt->meta, &pkt->meta, sizeof(struct metadata));
++ packet_set_origin_ctx(dup_pkt, (void *)NULL);
++
++ return dup_pkt;
++}
++
++void packet_free(struct packet *pkt)
++{
++ if (pkt)
++ {
++ if (packet_is_claim(pkt))
++ {
++ PACKET_LOG_ERROR("packet has been claimed and cannot be released, please check the module arrangement order");
++ assert(0);
++ return;
++ }
++
++ if (pkt->need_free)
++ {
++ free((void *)pkt);
++ }
++ }
++}
++
++int packet_is_fragment(const struct packet *pkt)
++{
++ return (pkt->frag_layer) ? 1 : 0;
++}
++
++void packet_set_exdata(struct packet *pkt, int idx, void *ex_ptr)
++{
++ struct exdata_runtime *exdata_rt = (struct exdata_runtime *)packet_get_user_data(pkt);
++ exdata_set(exdata_rt, idx, ex_ptr);
++}
++
++void *packet_get_exdata(struct packet *pkt, int idx)
++{
++ struct exdata_runtime *exdata_rt = (struct exdata_runtime *)packet_get_user_data(pkt);
++ return exdata_get(exdata_rt, idx);
++}
+diff -uNr tcpdump-tcpdump-4.99.4/print-bfd.c tcpdump-for-stellar/print-bfd.c
+--- tcpdump-tcpdump-4.99.4/print-bfd.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-bfd.c 2024-10-30 17:34:05.612498702 +0800
+@@ -30,7 +30,7 @@
+ #include "netdissect.h"
+ #include "extract.h"
+
+-#include "udp.h"
++#include "print-udp.h"
+
+ /*
+ * Control packet, BFDv0, draft-ietf-bfd-base-01
+@@ -144,9 +144,9 @@
+ };
+
+ static const struct tok bfd_port_values[] = {
+- { BFD_CONTROL_PORT, "Control" },
+- { BFD_MULTIHOP_PORT, "Multihop" },
+- { BFD_LAG_PORT, "Lag" },
++ { TCPDUMP_BFD_CONTROL_PORT, "Control" },
++ { TCPDUMP_BFD_MULTIHOP_PORT, "Multihop" },
++ { TCPDUMP_BFD_LAG_PORT, "Lag" },
+ { 0, NULL }
+ };
+
+@@ -292,9 +292,9 @@
+ u_int len, u_int port)
+ {
+ ndo->ndo_protocol = "bfd";
+- if (port == BFD_CONTROL_PORT ||
+- port == BFD_MULTIHOP_PORT ||
+- port == BFD_LAG_PORT) {
++ if (port == TCPDUMP_BFD_CONTROL_PORT ||
++ port == TCPDUMP_BFD_MULTIHOP_PORT ||
++ port == TCPDUMP_BFD_LAG_PORT) {
+ /*
+ * Control packet.
+ */
+@@ -401,7 +401,7 @@
+ }
+ break;
+ }
+- } else if (port == BFD_ECHO_PORT) {
++ } else if (port == TCPDUMP_BFD_ECHO_PORT) {
+ /*
+ * Echo packet.
+ */
+diff -uNr tcpdump-tcpdump-4.99.4/print-cnfp.c tcpdump-for-stellar/print-cnfp.c
+--- tcpdump-tcpdump-4.99.4/print-cnfp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-cnfp.c 2024-10-30 17:34:05.613498702 +0800
+@@ -52,7 +52,7 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "tcp.h"
++#include "print-tcp.h"
+ #include "ipproto.h"
+
+ struct nfhdr_v1 {
+diff -uNr tcpdump-tcpdump-4.99.4/print-dccp.c tcpdump-for-stellar/print-dccp.c
+--- tcpdump-tcpdump-4.99.4/print-dccp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-dccp.c 2024-10-30 17:34:05.614498702 +0800
+@@ -20,8 +20,8 @@
+ #include "netdissect.h"
+ #include "addrtoname.h"
+ #include "extract.h"
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+ #include "ipproto.h"
+
+ /* RFC4340: Datagram Congestion Control Protocol (DCCP) */
+@@ -199,14 +199,14 @@
+ return (cov > len)? len : cov;
+ }
+
+-static uint16_t dccp_cksum(netdissect_options *ndo, const struct ip *ip,
++static uint16_t dccp_cksum(netdissect_options *ndo, const struct tcpdump_ip *ip,
+ const struct dccp_hdr *dh, u_int len)
+ {
+ return nextproto4_cksum(ndo, ip, (const uint8_t *)(const void *)dh, len,
+ dccp_csum_coverage(ndo, dh, len), IPPROTO_DCCP);
+ }
+
+-static uint16_t dccp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
++static uint16_t dccp6_cksum(netdissect_options *ndo, const struct tcpdump_ip6_hdr *ip6,
+ const struct dccp_hdr *dh, u_int len)
+ {
+ return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)dh, len,
+@@ -270,8 +270,8 @@
+ u_int len)
+ {
+ const struct dccp_hdr *dh;
+- const struct ip *ip;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip *ip;
++ const struct tcpdump_ip6_hdr *ip6;
+ const u_char *cp;
+ u_short sport, dport;
+ u_int hlen;
+@@ -281,9 +281,9 @@
+ ndo->ndo_protocol = "dccp";
+ dh = (const struct dccp_hdr *)bp;
+
+- ip = (const struct ip *)data2;
++ ip = (const struct tcpdump_ip *)data2;
+ if (IP_V(ip) == 6)
+- ip6 = (const struct ip6_hdr *)data2;
++ ip6 = (const struct tcpdump_ip6_hdr *)data2;
+ else
+ ip6 = NULL;
+
+diff -uNr tcpdump-tcpdump-4.99.4/print-esp.c tcpdump-for-stellar/print-esp.c
+--- tcpdump-tcpdump-4.99.4/print-esp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-esp.c 2024-10-30 17:34:05.615498702 +0800
+@@ -54,8 +54,8 @@
+ #include "ascii_strcasecmp.h"
+ #endif
+
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+
+ /*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+@@ -737,7 +737,7 @@
+ #ifdef HAVE_LIBCRYPTO
+ const struct ip *ip;
+ struct sa_list *sa = NULL;
+- const struct ip6_hdr *ip6 = NULL;
++ const struct tcpdump_ip6_hdr *ip6 = NULL;
+ const u_char *iv;
+ u_int ivlen;
+ u_int payloadlen;
+@@ -776,7 +776,7 @@
+ ip = (const struct ip *)bp2;
+ switch (ver) {
+ case 6:
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ /* we do not attempt to decrypt jumbograms */
+ if (!GET_BE_U_2(ip6->ip6_plen))
+ return;
+diff -uNr tcpdump-tcpdump-4.99.4/print-frag6.c tcpdump-for-stellar/print-frag6.c
+--- tcpdump-tcpdump-4.99.4/print-frag6.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-frag6.c 2024-10-30 17:34:05.616498702 +0800
+@@ -30,29 +30,29 @@
+ #include "netdissect.h"
+ #include "extract.h"
+
+-#include "ip6.h"
++#include "tcpdump_ip6.h"
+
+ int
+ frag6_print(netdissect_options *ndo, const u_char *bp, const u_char *bp2)
+ {
+- const struct ip6_frag *dp;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip6_frag *dp;
++ const struct tcpdump_ip6_hdr *ip6;
+
+ ndo->ndo_protocol = "frag6";
+- dp = (const struct ip6_frag *)bp;
+- ip6 = (const struct ip6_hdr *)bp2;
++ dp = (const struct tcpdump_ip6_frag *)bp;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("frag (0x%08x:%u|%zu)",
+ GET_BE_U_4(dp->ip6f_ident),
+ GET_BE_U_2(dp->ip6f_offlg) & IP6F_OFF_MASK,
+- sizeof(struct ip6_hdr) + GET_BE_U_2(ip6->ip6_plen) -
+- (bp - bp2) - sizeof(struct ip6_frag));
++ sizeof(struct tcpdump_ip6_hdr) + GET_BE_U_2(ip6->ip6_plen) -
++ (bp - bp2) - sizeof(struct tcpdump_ip6_frag));
+ } else {
+ ND_PRINT("frag (%u|%zu)",
+ GET_BE_U_2(dp->ip6f_offlg) & IP6F_OFF_MASK,
+- sizeof(struct ip6_hdr) + GET_BE_U_2(ip6->ip6_plen) -
+- (bp - bp2) - sizeof(struct ip6_frag));
++ sizeof(struct tcpdump_ip6_hdr) + GET_BE_U_2(ip6->ip6_plen) -
++ (bp - bp2) - sizeof(struct tcpdump_ip6_frag));
+ }
+
+ /* it is meaningless to decode non-first fragment */
+@@ -61,6 +61,6 @@
+ else
+ {
+ ND_PRINT(" ");
+- return sizeof(struct ip6_frag);
++ return sizeof(struct tcpdump_ip6_frag);
+ }
+ }
+diff -uNr tcpdump-tcpdump-4.99.4/print-icmp6.c tcpdump-for-stellar/print-icmp6.c
+--- tcpdump-tcpdump-4.99.4/print-icmp6.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-icmp6.c 2024-10-30 17:34:05.617498702 +0800
+@@ -35,10 +35,10 @@
+ #include "addrtostr.h"
+ #include "extract.h"
+
+-#include "ip6.h"
++#include "tcpdump_ip6.h"
+ #include "ipproto.h"
+
+-#include "udp.h"
++#include "print-udp.h"
+ #include "ah.h"
+
+ /* NetBSD: icmp6.h,v 1.13 2000/08/03 16:30:37 itojun Exp */
+@@ -769,7 +769,7 @@
+ }
+ }
+
+-static uint16_t icmp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
++static uint16_t icmp6_cksum(netdissect_options *ndo, const struct tcpdump_ip6_hdr *ip6,
+ const struct icmp6_hdr *icp, u_int len)
+ {
+ return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)icp, len, len,
+@@ -1011,8 +1011,8 @@
+ {
+ const struct icmp6_hdr *dp;
+ uint8_t icmp6_type, icmp6_code;
+- const struct ip6_hdr *ip;
+- const struct ip6_hdr *oip;
++ const struct tcpdump_ip6_hdr *ip;
++ const struct tcpdump_ip6_hdr *oip;
+ const struct udphdr *ouh;
+ uint16_t dport;
+ const u_char *ep;
+@@ -1020,8 +1020,8 @@
+
+ ndo->ndo_protocol = "icmp6";
+ dp = (const struct icmp6_hdr *)bp;
+- ip = (const struct ip6_hdr *)bp2;
+- oip = (const struct ip6_hdr *)(dp + 1);
++ ip = (const struct tcpdump_ip6_hdr *)bp2;
++ oip = (const struct tcpdump_ip6_hdr *)(dp + 1);
+ /* 'ep' points to the end of available data. */
+ ep = ndo->ndo_snapend;
+ if (length == 0) {
+@@ -1319,10 +1319,10 @@
+ get_upperlayer(netdissect_options *ndo, const u_char *bp, u_int *prot)
+ {
+ const u_char *ep;
+- const struct ip6_hdr *ip6 = (const struct ip6_hdr *)bp;
++ const struct tcpdump_ip6_hdr *ip6 = (const struct tcpdump_ip6_hdr *)bp;
+ const struct udphdr *uh;
+- const struct ip6_hbh *hbh;
+- const struct ip6_frag *fragh;
++ const struct tcpdump_ip6_hbh *hbh;
++ const struct tcpdump_ip6_frag *fragh;
+ const struct ah *ah;
+ u_int nh;
+ int hlen;
+@@ -1334,7 +1334,7 @@
+ return NULL;
+
+ nh = GET_U_1(ip6->ip6_nxt);
+- hlen = sizeof(struct ip6_hdr);
++ hlen = sizeof(struct tcpdump_ip6_hdr);
+
+ while (bp < ep) {
+ bp += hlen;
+@@ -1354,7 +1354,7 @@
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+- hbh = (const struct ip6_hbh *)bp;
++ hbh = (const struct tcpdump_ip6_hbh *)bp;
+ if (!ND_TTEST_1(hbh->ip6h_len))
+ return(NULL);
+ nh = GET_U_1(hbh->ip6h_nxt);
+@@ -1362,14 +1362,14 @@
+ break;
+
+ case IPPROTO_FRAGMENT: /* this should be odd, but try anyway */
+- fragh = (const struct ip6_frag *)bp;
++ fragh = (const struct tcpdump_ip6_frag *)bp;
+ if (!ND_TTEST_2(fragh->ip6f_offlg))
+ return(NULL);
+ /* fragments with non-zero offset are meaningless */
+ if ((GET_BE_U_2(fragh->ip6f_offlg) & IP6F_OFF_MASK) != 0)
+ return(NULL);
+ nh = GET_U_1(fragh->ip6f_nxt);
+- hlen = sizeof(struct ip6_frag);
++ hlen = sizeof(struct tcpdump_ip6_frag);
+ break;
+
+ case IPPROTO_AH:
+diff -uNr tcpdump-tcpdump-4.99.4/print-icmp.c tcpdump-for-stellar/print-icmp.c
+--- tcpdump-tcpdump-4.99.4/print-icmp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-icmp.c 2024-10-30 17:34:05.617498702 +0800
+@@ -34,8 +34,8 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip.h"
+-#include "udp.h"
++#include "tcpdump_ip.h"
++#include "print-udp.h"
+ #include "ipproto.h"
+ #include "mpls.h"
+
+@@ -72,7 +72,7 @@
+ nd_uint32_t its_ttime;
+ } id_ts;
+ struct id_ip {
+- struct ip idi_ip;
++ struct tcpdump_ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ nd_uint32_t id_mask;
+@@ -95,10 +95,10 @@
+ * ip header length.
+ */
+ #define ICMP_MINLEN 8 /* abs minimum */
+-#define ICMP_EXTD_MINLEN (156 - sizeof (struct ip)) /* draft-bonica-internet-icmp-08 */
++#define ICMP_EXTD_MINLEN (156 - sizeof (struct tcpdump_ip)) /* draft-bonica-internet-icmp-08 */
+ #define ICMP_TSLEN (8 + 3 * sizeof (uint32_t)) /* timestamp */
+ #define ICMP_MASKLEN 12 /* address mask */
+-#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
++#define ICMP_ADVLENMIN (8 + sizeof (struct tcpdump_ip) + 8) /* min */
+ #define ICMP_ADVLEN(p) (8 + (IP_HL(&(p)->icmp_ip) << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+@@ -318,9 +318,9 @@
+ const struct icmp *dp;
+ uint8_t icmp_type, icmp_code;
+ const struct icmp_ext_t *ext_dp;
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+ const char *str;
+- const struct ip *oip;
++ const struct tcpdump_ip *oip;
+ uint8_t ip_proto;
+ const struct udphdr *ouh;
+ const uint8_t *obj_tptr;
+@@ -334,7 +334,7 @@
+ ndo->ndo_protocol = "icmp";
+ dp = (const struct icmp *)bp;
+ ext_dp = (const struct icmp_ext_t *)bp;
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ str = buf;
+
+ icmp_type = GET_U_1(dp->icmp_type);
+@@ -674,7 +674,7 @@
+
+ bp += 8;
+ ND_PRINT("\n\t");
+- ip = (const struct ip *)bp;
++ ip = (const struct tcpdump_ip *)bp;
+ snapend_save = ndo->ndo_snapend;
+ /*
+ * Update the snapend because extensions (MPLS, ...) may be
+diff -uNr tcpdump-tcpdump-4.99.4/print-ip6.c tcpdump-for-stellar/print-ip6.c
+--- tcpdump-tcpdump-4.99.4/print-ip6.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-ip6.c 2024-10-30 17:34:05.618498702 +0800
+@@ -33,7 +33,7 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip6.h"
++#include "tcpdump_ip6.h"
+ #include "ipproto.h"
+
+ /*
+@@ -45,20 +45,20 @@
+ */
+ static void
+ ip6_finddst(netdissect_options *ndo, nd_ipv6 *dst,
+- const struct ip6_hdr *ip6)
++ const struct tcpdump_ip6_hdr *ip6)
+ {
+ const u_char *cp;
+ u_int advance;
+ u_int nh;
+ const void *dst_addr;
+- const struct ip6_rthdr *dp;
+- const struct ip6_rthdr0 *dp0;
+- const struct ip6_srh *srh;
++ const struct tcpdump_ip6_rthdr *dp;
++ const struct tcpdump_ip6_rthdr0 *dp0;
++ const struct tcpdump_ip6_srh *srh;
+ const u_char *p;
+ int i, len;
+
+ cp = (const u_char *)ip6;
+- advance = sizeof(struct ip6_hdr);
++ advance = sizeof(struct tcpdump_ip6_hdr);
+ nh = GET_U_1(ip6->ip6_nxt);
+ dst_addr = (const void *)ip6->ip6_dst;
+
+@@ -87,7 +87,7 @@
+ * marked as reserved, and the header is always
+ * the same size.
+ */
+- advance = sizeof(struct ip6_frag);
++ advance = sizeof(struct tcpdump_ip6_frag);
+ nh = GET_U_1(cp);
+ break;
+
+@@ -95,14 +95,14 @@
+ /*
+ * OK, we found it.
+ */
+- dp = (const struct ip6_rthdr *)cp;
++ dp = (const struct tcpdump_ip6_rthdr *)cp;
+ ND_TCHECK_SIZE(dp);
+ len = GET_U_1(dp->ip6r_len);
+ switch (GET_U_1(dp->ip6r_type)) {
+
+ case IPV6_RTHDR_TYPE_0:
+ case IPV6_RTHDR_TYPE_2: /* Mobile IPv6 ID-20 */
+- dp0 = (const struct ip6_rthdr0 *)dp;
++ dp0 = (const struct tcpdump_ip6_rthdr0 *)dp;
+ if (len % 2 == 1)
+ goto trunc;
+ len >>= 1;
+@@ -115,7 +115,7 @@
+ break;
+ case IPV6_RTHDR_TYPE_4:
+ /* IPv6 Segment Routing Header (SRH) */
+- srh = (const struct ip6_srh *)dp;
++ srh = (const struct tcpdump_ip6_srh *)dp;
+ if (len % 2 == 1)
+ goto trunc;
+ p = (const u_char *) srh->srh_segments;
+@@ -171,7 +171,7 @@
+ */
+ uint16_t
+ nextproto6_cksum(netdissect_options *ndo,
+- const struct ip6_hdr *ip6, const uint8_t *data,
++ const struct tcpdump_ip6_hdr *ip6, const uint8_t *data,
+ u_int len, u_int covlen, uint8_t next_proto)
+ {
+ struct {
+@@ -225,7 +225,7 @@
+ void
+ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
+ {
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip6_hdr *ip6;
+ int advance;
+ u_int len;
+ u_int total_advance;
+@@ -239,10 +239,10 @@
+ int found_hbh;
+
+ ndo->ndo_protocol = "ip6";
+- ip6 = (const struct ip6_hdr *)bp;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp;
+
+ ND_TCHECK_SIZE(ip6);
+- if (length < sizeof (struct ip6_hdr)) {
++ if (length < sizeof (struct tcpdump_ip6_hdr)) {
+ ND_PRINT("truncated-ip6 %u", length);
+ return;
+ }
+@@ -279,12 +279,12 @@
+ * a Jumbo Payload option, and fail if we haven't.
+ */
+ if (payload_len != 0) {
+- len = payload_len + sizeof(struct ip6_hdr);
++ len = payload_len + sizeof(struct tcpdump_ip6_hdr);
+ if (length < len)
+ ND_PRINT("truncated-ip6 - %u bytes missing!",
+ len - length);
+ } else
+- len = length + sizeof(struct ip6_hdr);
++ len = length + sizeof(struct tcpdump_ip6_hdr);
+
+ ph = 255;
+ nh = GET_U_1(ip6->ip6_nxt);
+@@ -313,7 +313,7 @@
+ }
+
+ cp = (const u_char *)ip6;
+- advance = sizeof(struct ip6_hdr);
++ advance = sizeof(struct tcpdump_ip6_hdr);
+ total_advance = 0;
+ /* Process extension headers */
+ found_extension_header = 0;
+@@ -433,7 +433,7 @@
+ * the total number of bytes we've
+ * processed so far.
+ */
+- len = payload_len + sizeof(struct ip6_hdr);
++ len = payload_len + sizeof(struct tcpdump_ip6_hdr);
+ if (len < total_advance)
+ goto trunc;
+ if (length < len)
+@@ -471,7 +471,7 @@
+ * and change the snapshot length
+ * accordingly.
+ */
+- len = sizeof(struct ip6_hdr);
++ len = sizeof(struct tcpdump_ip6_hdr);
+ nd_change_snaplen(ndo, bp, len);
+
+ /*
+diff -uNr tcpdump-tcpdump-4.99.4/print-ip6opts.c tcpdump-for-stellar/print-ip6opts.c
+--- tcpdump-tcpdump-4.99.4/print-ip6opts.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-ip6opts.c 2024-10-30 17:34:05.618498702 +0800
+@@ -39,7 +39,7 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip6.h"
++#include "tcpdump_ip6.h"
+
+ static int
+ ip6_sopt_print(netdissect_options *ndo, const u_char *bp, int len)
+@@ -219,7 +219,7 @@
+ hbhopt_process(netdissect_options *ndo, const u_char *bp, int *found_jumbo,
+ uint32_t *jumbolen)
+ {
+- const struct ip6_hbh *dp = (const struct ip6_hbh *)bp;
++ const struct tcpdump_ip6_hbh *dp = (const struct tcpdump_ip6_hbh *)bp;
+ u_int hbhlen = 0;
+
+ ndo->ndo_protocol = "hbhopt";
+@@ -239,7 +239,7 @@
+ int
+ dstopt_process(netdissect_options *ndo, const u_char *bp)
+ {
+- const struct ip6_dest *dp = (const struct ip6_dest *)bp;
++ const struct tcpdump_ip6_dest *dp = (const struct tcpdump_ip6_dest *)bp;
+ u_int dstoptlen = 0;
+
+ ndo->ndo_protocol = "dstopt";
+diff -uNr tcpdump-tcpdump-4.99.4/print-ip.c tcpdump-for-stellar/print-ip.c
+--- tcpdump-tcpdump-4.99.4/print-ip.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-ip.c 2024-10-30 17:34:05.618498702 +0800
+@@ -31,7 +31,7 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip.h"
++#include "tcpdump_ip.h"
+ #include "ipproto.h"
+
+
+@@ -89,7 +89,7 @@
+ */
+ static uint32_t
+ ip_finddst(netdissect_options *ndo,
+- const struct ip *ip)
++ const struct tcpdump_ip *ip)
+ {
+ u_int length;
+ u_int len;
+@@ -97,9 +97,9 @@
+
+ cp = (const u_char *)(ip + 1);
+ length = IP_HL(ip) * 4;
+- if (length < sizeof(struct ip))
++ if (length < sizeof(struct tcpdump_ip))
+ goto trunc;
+- length -= sizeof(struct ip);
++ length -= sizeof(struct tcpdump_ip);
+
+ for (; length != 0; cp += len, length -= len) {
+ int tt;
+@@ -135,7 +135,7 @@
+ */
+ uint16_t
+ nextproto4_cksum(netdissect_options *ndo,
+- const struct ip *ip, const uint8_t *data,
++ const struct tcpdump_ip *ip, const uint8_t *data,
+ u_int len, u_int covlen, uint8_t next_proto)
+ {
+ struct phdr {
+@@ -320,7 +320,7 @@
+ const u_char *bp,
+ u_int length)
+ {
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+ u_int off;
+ u_int hlen;
+ u_int len;
+@@ -331,7 +331,7 @@
+ int truncated = 0;
+
+ ndo->ndo_protocol = "ip";
+- ip = (const struct ip *)bp;
++ ip = (const struct tcpdump_ip *)bp;
+ if (IP_V(ip) != 4) { /* print version and fail if != 4 */
+ if (IP_V(ip) == 6)
+ ND_PRINT("IP6, wrong link-layer encapsulation");
+@@ -344,12 +344,12 @@
+ ND_PRINT("IP ");
+
+ ND_TCHECK_SIZE(ip);
+- if (length < sizeof (struct ip)) {
++ if (length < sizeof (struct tcpdump_ip)) {
+ ND_PRINT("truncated-ip %u", length);
+ return;
+ }
+ hlen = IP_HL(ip) * 4;
+- if (hlen < sizeof (struct ip)) {
++ if (hlen < sizeof (struct tcpdump_ip)) {
+ ND_PRINT("bad-hlen %u", hlen);
+ return;
+ }
+@@ -428,10 +428,10 @@
+
+ ND_PRINT(", length %u", GET_BE_U_2(ip->ip_len));
+
+- if ((hlen - sizeof(struct ip)) > 0) {
++ if ((hlen - sizeof(struct tcpdump_ip)) > 0) {
+ ND_PRINT(", options (");
+ if (ip_optprint(ndo, (const u_char *)(ip + 1),
+- hlen - sizeof(struct ip)) == -1) {
++ hlen - sizeof(struct tcpdump_ip)) == -1) {
+ ND_PRINT(" [truncated-option]");
+ truncated = 1;
+ }
+diff -uNr tcpdump-tcpdump-4.99.4/print-ip-demux.c tcpdump-for-stellar/print-ip-demux.c
+--- tcpdump-tcpdump-4.99.4/print-ip-demux.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-ip-demux.c 2024-10-30 17:34:05.618498702 +0800
+@@ -31,7 +31,7 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip.h"
++#include "tcpdump_ip.h"
+ #include "ipproto.h"
+
+ void
+diff -uNr tcpdump-tcpdump-4.99.4/print-isakmp.c tcpdump-for-stellar/print-isakmp.c
+--- tcpdump-tcpdump-4.99.4/print-isakmp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-isakmp.c 2024-10-30 17:34:05.619498702 +0800
+@@ -53,8 +53,8 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+ #include "ipproto.h"
+
+ typedef nd_byte cookie_t[8];
+@@ -771,8 +771,8 @@
+ cookie_record(netdissect_options *ndo, const cookie_t *in, const u_char *bp2)
+ {
+ int i;
+- const struct ip *ip;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip *ip;
++ const struct tcpdump_ip6_hdr *ip6;
+
+ i = cookie_find(in);
+ if (0 <= i) {
+@@ -780,7 +780,7 @@
+ return;
+ }
+
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ switch (IP_V(ip)) {
+ case 4:
+ cookiecache[ninitiator].version = 4;
+@@ -790,7 +790,7 @@
+ ip->ip_dst, sizeof(nd_ipv4));
+ break;
+ case 6:
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ cookiecache[ninitiator].version = 6;
+ UNALIGNED_MEMCPY(&cookiecache[ninitiator].iaddr.in6,
+ ip6->ip6_src, sizeof(nd_ipv6));
+@@ -809,10 +809,10 @@
+ static int
+ cookie_sidecheck(netdissect_options *ndo, int i, const u_char *bp2, int initiator)
+ {
+- const struct ip *ip;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip *ip;
++ const struct tcpdump_ip6_hdr *ip6;
+
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ switch (IP_V(ip)) {
+ case 4:
+ if (cookiecache[i].version != 4)
+@@ -828,7 +828,7 @@
+ case 6:
+ if (cookiecache[i].version != 6)
+ return 0;
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ if (initiator) {
+ if (UNALIGNED_MEMCMP(ip6->ip6_src, &cookiecache[i].iaddr.in6, sizeof(nd_ipv6)) == 0)
+ return 1;
+diff -uNr tcpdump-tcpdump-4.99.4/print-lisp.c tcpdump-for-stellar/print-lisp.c
+--- tcpdump-tcpdump-4.99.4/print-lisp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-lisp.c 2024-10-30 17:34:05.620498702 +0800
+@@ -101,8 +101,8 @@
+ #include "netdissect-stdinc.h"
+ #include "netdissect.h"
+
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+
+ #include "extract.h"
+ #include "addrtoname.h"
+diff -uNr tcpdump-tcpdump-4.99.4/print-lldp.c tcpdump-for-stellar/print-lldp.c
+--- tcpdump-tcpdump-4.99.4/print-lldp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-lldp.c 2024-10-30 17:34:05.620498702 +0800
+@@ -41,9 +41,9 @@
+ */
+ #define LLDP_END_TLV 0
+ #define LLDP_CHASSIS_ID_TLV 1
+-#define LLDP_PORT_ID_TLV 2
++#define LTCPDUMP_LDP_PORT_ID_TLV 2
+ #define LLDP_TTL_TLV 3
+-#define LLDP_PORT_DESCR_TLV 4
++#define LTCPDUMP_LDP_PORT_DESCR_TLV 4
+ #define LLDP_SYSTEM_NAME_TLV 5
+ #define LLDP_SYSTEM_DESCR_TLV 6
+ #define LLDP_SYSTEM_CAP_TLV 7
+@@ -53,9 +53,9 @@
+ static const struct tok lldp_tlv_values[] = {
+ { LLDP_END_TLV, "End" },
+ { LLDP_CHASSIS_ID_TLV, "Chassis ID" },
+- { LLDP_PORT_ID_TLV, "Port ID" },
++ { LTCPDUMP_LDP_PORT_ID_TLV, "Port ID" },
+ { LLDP_TTL_TLV, "Time to Live" },
+- { LLDP_PORT_DESCR_TLV, "Port Description" },
++ { LTCPDUMP_LDP_PORT_DESCR_TLV, "Port Description" },
+ { LLDP_SYSTEM_NAME_TLV, "System Name" },
+ { LLDP_SYSTEM_DESCR_TLV, "System Description" },
+ { LLDP_SYSTEM_CAP_TLV, "System Capabilities" },
+@@ -89,22 +89,22 @@
+ /*
+ * Port ID subtypes
+ */
+-#define LLDP_PORT_INTF_ALIAS_SUBTYPE 1
+-#define LLDP_PORT_PORT_COMP_SUBTYPE 2
+-#define LLDP_PORT_MAC_ADDR_SUBTYPE 3
+-#define LLDP_PORT_NETWORK_ADDR_SUBTYPE 4
+-#define LLDP_PORT_INTF_NAME_SUBTYPE 5
+-#define LLDP_PORT_AGENT_CIRC_ID_SUBTYPE 6
+-#define LLDP_PORT_LOCAL_SUBTYPE 7
++#define LTCPDUMP_LDP_PORT_INTF_ALIAS_SUBTYPE 1
++#define LTCPDUMP_LDP_PORT_PORT_COMP_SUBTYPE 2
++#define LTCPDUMP_LDP_PORT_MAC_ADDR_SUBTYPE 3
++#define LTCPDUMP_LDP_PORT_NETWORK_ADDR_SUBTYPE 4
++#define LTCPDUMP_LDP_PORT_INTF_NAME_SUBTYPE 5
++#define LTCPDUMP_LDP_PORT_AGENT_CIRC_ID_SUBTYPE 6
++#define LTCPDUMP_LDP_PORT_LOCAL_SUBTYPE 7
+
+ static const struct tok lldp_port_subtype_values[] = {
+- { LLDP_PORT_INTF_ALIAS_SUBTYPE, "Interface alias"},
+- { LLDP_PORT_PORT_COMP_SUBTYPE, "Port component"},
+- { LLDP_PORT_MAC_ADDR_SUBTYPE, "MAC address"},
+- { LLDP_PORT_NETWORK_ADDR_SUBTYPE, "Network Address"},
+- { LLDP_PORT_INTF_NAME_SUBTYPE, "Interface Name"},
+- { LLDP_PORT_AGENT_CIRC_ID_SUBTYPE, "Agent circuit ID"},
+- { LLDP_PORT_LOCAL_SUBTYPE, "Local"},
++ { LTCPDUMP_LDP_PORT_INTF_ALIAS_SUBTYPE, "Interface alias"},
++ { LTCPDUMP_LDP_PORT_PORT_COMP_SUBTYPE, "Port component"},
++ { LTCPDUMP_LDP_PORT_MAC_ADDR_SUBTYPE, "MAC address"},
++ { LTCPDUMP_LDP_PORT_NETWORK_ADDR_SUBTYPE, "Network Address"},
++ { LTCPDUMP_LDP_PORT_INTF_NAME_SUBTYPE, "Interface Name"},
++ { LTCPDUMP_LDP_PORT_AGENT_CIRC_ID_SUBTYPE, "Agent circuit ID"},
++ { LTCPDUMP_LDP_PORT_LOCAL_SUBTYPE, "Local"},
+ { 0, NULL}
+ };
+
+@@ -1531,7 +1531,7 @@
+ }
+ break;
+
+- case LLDP_PORT_ID_TLV:
++ case LTCPDUMP_LDP_PORT_ID_TLV:
+ if (ndo->ndo_vflag) {
+ if (tlv_len < 2) {
+ goto trunc;
+@@ -1542,22 +1542,22 @@
+ subtype);
+
+ switch (subtype) {
+- case LLDP_PORT_MAC_ADDR_SUBTYPE:
++ case LTCPDUMP_LDP_PORT_MAC_ADDR_SUBTYPE:
+ if (tlv_len < 1+6) {
+ goto trunc;
+ }
+ ND_PRINT("%s", GET_ETHERADDR_STRING(tptr + 1));
+ break;
+
+- case LLDP_PORT_INTF_NAME_SUBTYPE: /* fall through */
+- case LLDP_PORT_LOCAL_SUBTYPE:
+- case LLDP_PORT_AGENT_CIRC_ID_SUBTYPE:
+- case LLDP_PORT_INTF_ALIAS_SUBTYPE:
+- case LLDP_PORT_PORT_COMP_SUBTYPE:
++ case LTCPDUMP_LDP_PORT_INTF_NAME_SUBTYPE: /* fall through */
++ case LTCPDUMP_LDP_PORT_LOCAL_SUBTYPE:
++ case LTCPDUMP_LDP_PORT_AGENT_CIRC_ID_SUBTYPE:
++ case LTCPDUMP_LDP_PORT_INTF_ALIAS_SUBTYPE:
++ case LTCPDUMP_LDP_PORT_PORT_COMP_SUBTYPE:
+ nd_printjnp(ndo, tptr + 1, tlv_len - 1);
+ break;
+
+- case LLDP_PORT_NETWORK_ADDR_SUBTYPE:
++ case LTCPDUMP_LDP_PORT_NETWORK_ADDR_SUBTYPE:
+ network_addr = lldp_network_addr_print(ndo, tptr+1, tlv_len-1);
+ if (network_addr == NULL) {
+ goto trunc;
+@@ -1581,7 +1581,7 @@
+ }
+ break;
+
+- case LLDP_PORT_DESCR_TLV:
++ case LTCPDUMP_LDP_PORT_DESCR_TLV:
+ if (ndo->ndo_vflag) {
+ ND_PRINT(": ");
+ nd_printjnp(ndo, tptr, tlv_len);
+diff -uNr tcpdump-tcpdump-4.99.4/print-mobility.c tcpdump-for-stellar/print-mobility.c
+--- tcpdump-tcpdump-4.99.4/print-mobility.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-mobility.c 2024-10-30 17:34:05.622498702 +0800
+@@ -40,7 +40,7 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip6.h"
++#include "tcpdump_ip6.h"
+
+
+ /* Mobility header */
+diff -uNr tcpdump-tcpdump-4.99.4/print-mptcp.c tcpdump-for-stellar/print-mptcp.c
+--- tcpdump-tcpdump-4.99.4/print-mptcp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-mptcp.c 2024-10-30 17:34:05.622498702 +0800
+@@ -46,7 +46,7 @@
+ #include "extract.h"
+ #include "addrtoname.h"
+
+-#include "tcp.h"
++#include "print-tcp.h"
+
+ #define MPTCP_SUB_CAPABLE 0x0
+ #define MPTCP_SUB_JOIN 0x1
+diff -uNr tcpdump-tcpdump-4.99.4/print-nfs.c tcpdump-for-stellar/print-nfs.c
+--- tcpdump-tcpdump-4.99.4/print-nfs.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-nfs.c 2024-10-30 17:34:05.623498702 +0800
+@@ -38,8 +38,8 @@
+ #include "nfs.h"
+ #include "nfsfh.h"
+
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+ #include "rpc_auth.h"
+ #include "rpc_msg.h"
+
+@@ -201,19 +201,19 @@
+ print_nfsaddr(netdissect_options *ndo,
+ const u_char *bp, const char *s, const char *d)
+ {
+- const struct ip *ip;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip *ip;
++ const struct tcpdump_ip6_hdr *ip6;
+ char srcaddr[INET6_ADDRSTRLEN], dstaddr[INET6_ADDRSTRLEN];
+
+ srcaddr[0] = dstaddr[0] = '\0';
+- switch (IP_V((const struct ip *)bp)) {
++ switch (IP_V((const struct tcpdump_ip *)bp)) {
+ case 4:
+- ip = (const struct ip *)bp;
++ ip = (const struct tcpdump_ip *)bp;
+ strlcpy(srcaddr, GET_IPADDR_STRING(ip->ip_src), sizeof(srcaddr));
+ strlcpy(dstaddr, GET_IPADDR_STRING(ip->ip_dst), sizeof(dstaddr));
+ break;
+ case 6:
+- ip6 = (const struct ip6_hdr *)bp;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp;
+ strlcpy(srcaddr, GET_IP6ADDR_STRING(ip6->ip6_src),
+ sizeof(srcaddr));
+ strlcpy(dstaddr, GET_IP6ADDR_STRING(ip6->ip6_dst),
+@@ -941,18 +941,18 @@
+ xid_map_enter(netdissect_options *ndo,
+ const struct sunrpc_msg *rp, const u_char *bp)
+ {
+- const struct ip *ip = NULL;
+- const struct ip6_hdr *ip6 = NULL;
++ const struct tcpdump_ip *ip = NULL;
++ const struct tcpdump_ip6_hdr *ip6 = NULL;
+ struct xid_map_entry *xmep;
+
+ if (!ND_TTEST_4(rp->rm_call.cb_proc))
+ return (0);
+- switch (IP_V((const struct ip *)bp)) {
++ switch (IP_V((const struct tcpdump_ip *)bp)) {
+ case 4:
+- ip = (const struct ip *)bp;
++ ip = (const struct tcpdump_ip *)bp;
+ break;
+ case 6:
+- ip6 = (const struct ip6_hdr *)bp;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp;
+ break;
+ default:
+ return (1);
+@@ -994,8 +994,8 @@
+ int i;
+ struct xid_map_entry *xmep;
+ uint32_t xid;
+- const struct ip *ip = (const struct ip *)bp;
+- const struct ip6_hdr *ip6 = (const struct ip6_hdr *)bp;
++ const struct tcpdump_ip *ip = (const struct tcpdump_ip *)bp;
++ const struct tcpdump_ip6_hdr *ip6 = (const struct tcpdump_ip6_hdr *)bp;
+ int cmp;
+
+ UNALIGNED_MEMCPY(&xid, &rp->rm_xid, sizeof(xmep->xid));
+diff -uNr tcpdump-tcpdump-4.99.4/print-pgm.c tcpdump-for-stellar/print-pgm.c
+--- tcpdump-tcpdump-4.99.4/print-pgm.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-pgm.c 2024-10-30 17:34:05.625498702 +0800
+@@ -26,8 +26,8 @@
+ #include "addrtoname.h"
+ #include "addrtostr.h"
+
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+ #include "ipproto.h"
+ #include "af.h"
+
+@@ -148,20 +148,20 @@
+ const u_char *bp2)
+ {
+ const struct pgm_header *pgm;
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+ uint8_t pgm_type_val;
+ uint16_t sport, dport;
+ u_int nla_afnum;
+ char nla_buf[INET6_ADDRSTRLEN];
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip6_hdr *ip6;
+ uint8_t opt_type, opt_len;
+ uint32_t seq, opts_len, len, offset;
+
+ ndo->ndo_protocol = "pgm";
+ pgm = (const struct pgm_header *)bp;
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ if (IP_V(ip) == 6)
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ else
+ ip6 = NULL;
+ if (!ND_TTEST_2(pgm->pgm_dport)) {
+diff -uNr tcpdump-tcpdump-4.99.4/print-pim.c tcpdump-for-stellar/print-pim.c
+--- tcpdump-tcpdump-4.99.4/print-pim.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-pim.c 2024-10-30 17:34:05.625498702 +0800
+@@ -31,8 +31,8 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+ #include "ipproto.h"
+
+ #define PIMV1_TYPE_QUERY 0
+@@ -657,14 +657,14 @@
+ pimv2_check_checksum(netdissect_options *ndo, const u_char *bp,
+ const u_char *bp2, u_int len)
+ {
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+ u_int cksum;
+
+ if (!ND_TTEST_LEN(bp, len)) {
+ /* We don't have all the data. */
+ return (UNVERIFIED);
+ }
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ if (IP_V(ip) == 4) {
+ struct cksum_vec vec[1];
+
+@@ -673,9 +673,9 @@
+ cksum = in_cksum(vec, 1);
+ return (cksum ? INCORRECT : CORRECT);
+ } else if (IP_V(ip) == 6) {
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip6_hdr *ip6;
+
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ cksum = nextproto6_cksum(ndo, ip6, bp, len, len, IPPROTO_PIM);
+ return (cksum ? INCORRECT : CORRECT);
+ } else {
+@@ -882,7 +882,7 @@
+
+ case PIMV2_TYPE_REGISTER:
+ {
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+
+ if (len < 4)
+ goto trunc;
+@@ -897,7 +897,7 @@
+ /* encapsulated multicast packet */
+ if (len == 0)
+ goto trunc;
+- ip = (const struct ip *)bp;
++ ip = (const struct tcpdump_ip *)bp;
+ switch (IP_V(ip)) {
+ case 0: /* Null header */
+ ND_PRINT("IP-Null-header %s > %s",
+diff -uNr tcpdump-tcpdump-4.99.4/print-rt6.c tcpdump-for-stellar/print-rt6.c
+--- tcpdump-tcpdump-4.99.4/print-rt6.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-rt6.c 2024-10-30 17:34:05.627498702 +0800
+@@ -31,21 +31,21 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip6.h"
++#include "tcpdump_ip6.h"
+
+ int
+ rt6_print(netdissect_options *ndo, const u_char *bp, const u_char *bp2 _U_)
+ {
+- const struct ip6_rthdr *dp;
+- const struct ip6_rthdr0 *dp0;
+- const struct ip6_srh *srh;
++ const struct tcpdump_ip6_rthdr *dp;
++ const struct tcpdump_ip6_rthdr0 *dp0;
++ const struct tcpdump_ip6_srh *srh;
+ u_int i, len, type;
+ const u_char *p;
+
+ ndo->ndo_protocol = "rt6";
+
+ nd_print_protocol_caps(ndo);
+- dp = (const struct ip6_rthdr *)bp;
++ dp = (const struct tcpdump_ip6_rthdr *)bp;
+
+ len = GET_U_1(dp->ip6r_len);
+ ND_PRINT(" (len=%u", len); /*)*/
+@@ -58,7 +58,7 @@
+ switch (type) {
+ case IPV6_RTHDR_TYPE_0:
+ case IPV6_RTHDR_TYPE_2: /* Mobile IPv6 ID-20 */
+- dp0 = (const struct ip6_rthdr0 *)dp;
++ dp0 = (const struct tcpdump_ip6_rthdr0 *)dp;
+
+ if (GET_BE_U_4(dp0->ip6r0_reserved) || ndo->ndo_vflag) {
+ ND_PRINT(", rsv=0x%0x",
+@@ -80,7 +80,7 @@
+ return((GET_U_1(dp0->ip6r0_len) + 1) << 3);
+ break;
+ case IPV6_RTHDR_TYPE_4:
+- srh = (const struct ip6_srh *)dp;
++ srh = (const struct tcpdump_ip6_srh *)dp;
+ ND_PRINT(", last-entry=%u", GET_U_1(srh->srh_last_ent));
+
+ if (GET_U_1(srh->srh_flags) || ndo->ndo_vflag) {
+diff -uNr tcpdump-tcpdump-4.99.4/print-rx.c tcpdump-for-stellar/print-rx.c
+--- tcpdump-tcpdump-4.99.4/print-rx.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-rx.c 2024-10-30 17:34:05.628498702 +0800
+@@ -47,7 +47,7 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip.h"
++#include "tcpdump_ip.h"
+
+ #define FS_RX_PORT 7000
+ #define CB_RX_PORT 7001
+@@ -493,9 +493,9 @@
+
+ static uint32_t rx_cache_next = 0;
+ static uint32_t rx_cache_hint = 0;
+-static void rx_cache_insert(netdissect_options *, const u_char *, const struct ip *, uint16_t);
++static void rx_cache_insert(netdissect_options *, const u_char *, const struct tcpdump_ip *, uint16_t);
+ static int rx_cache_find(netdissect_options *, const struct rx_header *,
+- const struct ip *, uint16_t, uint32_t *);
++ const struct tcpdump_ip *, uint16_t, uint32_t *);
+
+ static void fs_print(netdissect_options *, const u_char *, u_int);
+ static void fs_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+@@ -597,7 +597,7 @@
+ * have a chance to print out replies
+ */
+
+- rx_cache_insert(ndo, bp, (const struct ip *) bp2, dport);
++ rx_cache_insert(ndo, bp, (const struct tcpdump_ip *) bp2, dport);
+
+ switch (dport) {
+ case FS_RX_PORT: /* AFS file service */
+@@ -636,7 +636,7 @@
+ GET_BE_U_4(rxh->seq) == 1) ||
+ type == RX_PACKET_TYPE_ABORT) &&
+ (flags & RX_CLIENT_INITIATED) == 0 &&
+- rx_cache_find(ndo, rxh, (const struct ip *) bp2,
++ rx_cache_find(ndo, rxh, (const struct tcpdump_ip *) bp2,
+ sport, &opcode)) {
+
+ switch (sport) {
+@@ -684,7 +684,7 @@
+
+ static void
+ rx_cache_insert(netdissect_options *ndo,
+- const u_char *bp, const struct ip *ip, uint16_t dport)
++ const u_char *bp, const struct tcpdump_ip *ip, uint16_t dport)
+ {
+ struct rx_cache_entry *rxent;
+ const struct rx_header *rxh = (const struct rx_header *) bp;
+@@ -714,7 +714,7 @@
+
+ static int
+ rx_cache_find(netdissect_options *ndo, const struct rx_header *rxh,
+- const struct ip *ip, uint16_t sport, uint32_t *opcode)
++ const struct tcpdump_ip *ip, uint16_t sport, uint32_t *opcode)
+ {
+ uint32_t i;
+ struct rx_cache_entry *rxent;
+diff -uNr tcpdump-tcpdump-4.99.4/print-sctp.c tcpdump-for-stellar/print-sctp.c
+--- tcpdump-tcpdump-4.99.4/print-sctp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-sctp.c 2024-10-30 17:34:05.628498702 +0800
+@@ -44,8 +44,8 @@
+ #include "netdissect.h"
+ #include "addrtoname.h"
+ #include "extract.h"
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+
+ /* Definitions from:
+ *
+@@ -455,8 +455,8 @@
+ {
+ u_int sctpPacketLengthRemaining;
+ const struct sctpHeader *sctpPktHdr;
+- const struct ip *ip;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip *ip;
++ const struct tcpdump_ip6_hdr *ip6;
+ uint8_t chunkID;
+ u_short sourcePort, destPort;
+ u_int chunkCount;
+@@ -478,9 +478,9 @@
+ sourcePort = GET_BE_U_2(sctpPktHdr->source);
+ destPort = GET_BE_U_2(sctpPktHdr->destination);
+
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ if (IP_V(ip) == 6)
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ else
+ ip6 = NULL;
+
+diff -uNr tcpdump-tcpdump-4.99.4/print-sl.c tcpdump-for-stellar/print-sl.c
+--- tcpdump-tcpdump-4.99.4/print-sl.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-sl.c 2024-10-30 17:34:05.628498702 +0800
+@@ -31,8 +31,8 @@
+ #include "netdissect.h"
+ #include "extract.h"
+
+-#include "ip.h"
+-#include "tcp.h"
++#include "tcpdump_ip.h"
++#include "print-tcp.h"
+ #include "slcompress.h"
+
+ /*
+@@ -51,15 +51,15 @@
+ static u_int lastlen[2][256];
+ static u_int lastconn = 255;
+
+-static void sliplink_print(netdissect_options *, const u_char *, const struct ip *, u_int);
+-static void compressed_sl_print(netdissect_options *, const u_char *, const struct ip *, u_int, int);
++static void sliplink_print(netdissect_options *, const u_char *, const struct tcpdump_ip *, u_int);
++static void compressed_sl_print(netdissect_options *, const u_char *, const struct tcpdump_ip *, u_int, int);
+
+ void
+ sl_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+ {
+ u_int length = h->len;
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+
+ ndo->ndo_protocol = "slip";
+ ND_TCHECK_LEN(p, SLIP_HDRLEN);
+@@ -67,7 +67,7 @@
+
+ length -= SLIP_HDRLEN;
+
+- ip = (const struct ip *)(p + SLIP_HDRLEN);
++ ip = (const struct tcpdump_ip *)(p + SLIP_HDRLEN);
+
+ if (ndo->ndo_eflag)
+ sliplink_print(ndo, p, ip, length);
+@@ -89,7 +89,7 @@
+ const struct pcap_pkthdr *h, const u_char *p)
+ {
+ u_int length = h->len;
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+
+ ndo->ndo_protocol = "slip_bsdos";
+ ND_TCHECK_LEN(p, SLIP_HDRLEN);
+@@ -97,7 +97,7 @@
+
+ length -= SLIP_HDRLEN;
+
+- ip = (const struct ip *)(p + SLIP_HDRLEN);
++ ip = (const struct tcpdump_ip *)(p + SLIP_HDRLEN);
+
+ #ifdef notdef
+ if (ndo->ndo_eflag)
+@@ -109,7 +109,7 @@
+
+ static void
+ sliplink_print(netdissect_options *ndo,
+- const u_char *p, const struct ip *ip,
++ const u_char *p, const struct tcpdump_ip *ip,
+ u_int length)
+ {
+ int dir;
+@@ -143,7 +143,7 @@
+ * Get it from the link layer since sl_uncompress_tcp()
+ * has restored the IP header copy to IPPROTO_TCP.
+ */
+- lastconn = GET_U_1(((const struct ip *)(p + SLX_CHDR))->ip_p);
++ lastconn = GET_U_1(((const struct tcpdump_ip *)(p + SLX_CHDR))->ip_p);
+ ND_PRINT("utcp %u: ", lastconn);
+ if (dir == -1) {
+ /* Direction is bogus, don't use it */
+@@ -204,7 +204,7 @@
+
+ static void
+ compressed_sl_print(netdissect_options *ndo,
+- const u_char *chdr, const struct ip *ip,
++ const u_char *chdr, const struct tcpdump_ip *ip,
+ u_int length, int dir)
+ {
+ const u_char *cp = chdr;
+diff -uNr tcpdump-tcpdump-4.99.4/print-someip.c tcpdump-for-stellar/print-someip.c
+--- tcpdump-tcpdump-4.99.4/print-someip.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-someip.c 2024-10-30 17:34:05.631498702 +0800
+@@ -22,7 +22,7 @@
+ #include "netdissect-stdinc.h"
+ #include "netdissect.h"
+ #include "extract.h"
+-#include "udp.h"
++#include "print-udp.h"
+
+ /*
+ * SOMEIP Header (R19-11)
+diff -uNr tcpdump-tcpdump-4.99.4/print-sunrpc.c tcpdump-for-stellar/print-sunrpc.c
+--- tcpdump-tcpdump-4.99.4/print-sunrpc.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-sunrpc.c 2024-10-30 17:34:05.631498702 +0800
+@@ -57,8 +57,8 @@
+ #include "addrtoname.h"
+ #include "extract.h"
+
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+
+ #include "rpc_auth.h"
+ #include "rpc_msg.h"
+@@ -159,8 +159,8 @@
+ u_int length, const u_char *bp2)
+ {
+ const struct sunrpc_msg *rp;
+- const struct ip *ip;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip *ip;
++ const struct tcpdump_ip6_hdr *ip6;
+ uint32_t x;
+ char srcid[20], dstid[20]; /*fits 32bit*/
+
+@@ -178,15 +178,15 @@
+ snprintf(dstid, sizeof(dstid), "0x%x", SUNRPC_PMAPPORT);
+ }
+
+- switch (IP_V((const struct ip *)bp2)) {
++ switch (IP_V((const struct tcpdump_ip *)bp2)) {
+ case 4:
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ ND_PRINT("%s.%s > %s.%s: %u",
+ GET_IPADDR_STRING(ip->ip_src), srcid,
+ GET_IPADDR_STRING(ip->ip_dst), dstid, length);
+ break;
+ case 6:
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ ND_PRINT("%s.%s > %s.%s: %u",
+ GET_IP6ADDR_STRING(ip6->ip6_src), srcid,
+ GET_IP6ADDR_STRING(ip6->ip6_dst), dstid, length);
+diff -uNr tcpdump-tcpdump-4.99.4/print-tcp.c tcpdump-for-stellar/print-tcp.c
+--- tcpdump-tcpdump-4.99.4/print-tcp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-tcp.c 2024-10-30 17:34:05.631498702 +0800
+@@ -25,6 +25,12 @@
+
+ /* \summary: TCP printer */
+
++#if defined(__clang__)
++#pragma clang diagnostic ignored "-Wall"
++#elif defined(__GNUC__)
++#pragma GCC diagnostic ignored "-Wall"
++#endif
++
+ #ifndef lint
+ #else
+ __RCSID("$NetBSD: print-tcp.c,v 1.8 2007/07/24 11:53:48 drochner Exp $");
+@@ -45,10 +51,10 @@
+
+ #include "diag-control.h"
+
+-#include "tcp.h"
++#include "print-tcp.h"
+
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+ #include "ipproto.h"
+ #include "rpc_auth.h"
+ #include "rpc_msg.h"
+@@ -56,9 +62,8 @@
+ #ifdef HAVE_LIBCRYPTO
+ #include <openssl/md5.h>
+ #include "signature.h"
+-
+ static int tcp_verify_signature(netdissect_options *ndo,
+- const struct ip *ip, const struct tcphdr *tp,
++ const struct tcpdump_ip *ip, const struct tcphdr *tp,
+ const u_char *data, u_int length, const u_char *rcvsig);
+ #endif
+
+@@ -66,29 +71,35 @@
+ static void print_tcp_fastopen_option(netdissect_options *ndo, const u_char *cp,
+ u_int datalen, int exp);
+
+-#define MAX_RST_DATA_LEN 30
++#define MAX_RST_DATA_LEN 30
+
++/* For source or destination ports tests (UDP, TCP, ...) */
++#define IS_SRC_OR_DST_TCPPORT(p) (sport == (p) || dport == (p))
+
+-struct tha {
++struct tha
++{
+ nd_ipv4 src;
+ nd_ipv4 dst;
+ u_int port;
+ };
+
+-struct tcp_seq_hash {
++struct tcp_seq_hash
++{
+ struct tcp_seq_hash *nxt;
+ struct tha addr;
+ uint32_t seq;
+ uint32_t ack;
+ };
+
+-struct tha6 {
++struct tha6
++{
+ nd_ipv6 src;
+ nd_ipv6 dst;
+ u_int port;
+ };
+
+-struct tcp_seq_hash6 {
++struct tcp_seq_hash6
++{
+ struct tcp_seq_hash6 *nxt;
+ struct tha6 addr;
+ uint32_t seq;
+@@ -104,43 +115,41 @@
+ static struct tcp_seq_hash6 tcp_seq_hash6[TSEQ_HASHSIZE];
+
+ static const struct tok tcp_flag_values[] = {
+- { TH_FIN, "F" },
+- { TH_SYN, "S" },
+- { TH_RST, "R" },
+- { TH_PUSH, "P" },
+- { TH_ACK, "." },
+- { TH_URG, "U" },
+- { TH_ECNECHO, "E" },
+- { TH_CWR, "W" },
+- { 0, NULL }
+-};
++ {TH_FIN, "F"},
++ {TH_SYN, "S"},
++ {TH_RST, "R"},
++ {TH_PUSH, "P"},
++ {TH_ACK, "."},
++ {TH_URG, "U"},
++ {TH_ECNECHO, "E"},
++ {TH_CWR, "W"},
++ {0, NULL}};
+
+ static const struct tok tcp_option_values[] = {
+- { TCPOPT_EOL, "eol" },
+- { TCPOPT_NOP, "nop" },
+- { TCPOPT_MAXSEG, "mss" },
+- { TCPOPT_WSCALE, "wscale" },
+- { TCPOPT_SACKOK, "sackOK" },
+- { TCPOPT_SACK, "sack" },
+- { TCPOPT_ECHO, "echo" },
+- { TCPOPT_ECHOREPLY, "echoreply" },
+- { TCPOPT_TIMESTAMP, "TS" },
+- { TCPOPT_CC, "cc" },
+- { TCPOPT_CCNEW, "ccnew" },
+- { TCPOPT_CCECHO, "" },
+- { TCPOPT_SIGNATURE, "md5" },
+- { TCPOPT_SCPS, "scps" },
+- { TCPOPT_UTO, "uto" },
+- { TCPOPT_TCPAO, "tcp-ao" },
+- { TCPOPT_MPTCP, "mptcp" },
+- { TCPOPT_FASTOPEN, "tfo" },
+- { TCPOPT_EXPERIMENT2, "exp" },
+- { 0, NULL }
+-};
++ {TCPOPT_EOL, "eol"},
++ {TCPOPT_NOP, "nop"},
++ {TCPOPT_MAXSEG, "mss"},
++ {TCPOPT_WSCALE, "wscale"},
++ {TCPOPT_SACKOK, "sackOK"},
++ {TCPOPT_SACK, "sack"},
++ {TCPOPT_ECHO, "echo"},
++ {TCPOPT_ECHOREPLY, "echoreply"},
++ {TCPOPT_TIMESTAMP, "TS"},
++ {TCPOPT_CC, "cc"},
++ {TCPOPT_CCNEW, "ccnew"},
++ {TCPOPT_CCECHO, ""},
++ {TCPOPT_SIGNATURE, "md5"},
++ {TCPOPT_SCPS, "scps"},
++ {TCPOPT_UTO, "uto"},
++ {TCPOPT_TCPAO, "tcp-ao"},
++ {TCPOPT_MPTCP, "mptcp"},
++ {TCPOPT_FASTOPEN, "tfo"},
++ {TCPOPT_EXPERIMENT2, "exp"},
++ {0, NULL}};
+
+ static uint16_t
+ tcp_cksum(netdissect_options *ndo,
+- const struct ip *ip,
++ const struct tcpdump_ip *ip,
+ const struct tcphdr *tp,
+ u_int len)
+ {
+@@ -150,7 +159,7 @@
+
+ static uint16_t
+ tcp6_cksum(netdissect_options *ndo,
+- const struct ip6_hdr *ip6,
++ const struct tcpdump_ip6_hdr *ip6,
+ const struct tcphdr *tp,
+ u_int len)
+ {
+@@ -158,13 +167,12 @@
+ IPPROTO_TCP);
+ }
+
+-void
+-tcp_print(netdissect_options *ndo,
+- const u_char *bp, u_int length,
+- const u_char *bp2, int fragmented)
++void tcp_print(netdissect_options *ndo,
++ const u_char *bp, u_int length,
++ const u_char *bp2, int fragmented)
+ {
+ const struct tcphdr *tp;
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+ u_char flags;
+ u_int hlen;
+ char ch;
+@@ -173,23 +181,27 @@
+ u_int utoval;
+ uint16_t magic;
+ int rev;
+- const struct ip6_hdr *ip6;
+- u_int header_len; /* Header length in bytes */
++ const struct tcpdump_ip6_hdr *ip6;
++ u_int header_len; /* Header length in bytes */
+
+ ndo->ndo_protocol = "tcp";
+ tp = (const struct tcphdr *)bp;
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ if (IP_V(ip) == 6)
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ else
+ ip6 = NULL;
+ ch = '\0';
+- if (!ND_TTEST_2(tp->th_dport)) {
+- if (ip6) {
++ if (!ND_TTEST_2(tp->th_dport))
++ {
++ if (ip6)
++ {
+ ND_PRINT("%s > %s:",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ GET_IP6ADDR_STRING(ip6->ip6_dst));
+- } else {
++ }
++ else
++ {
+ ND_PRINT("%s > %s:",
+ GET_IPADDR_STRING(ip->ip_src),
+ GET_IPADDR_STRING(ip->ip_dst));
+@@ -201,25 +213,34 @@
+ sport = GET_BE_U_2(tp->th_sport);
+ dport = GET_BE_U_2(tp->th_dport);
+
+- if (ip6) {
+- if (GET_U_1(ip6->ip6_nxt) == IPPROTO_TCP) {
++ if (ip6)
++ {
++ if (GET_U_1(ip6->ip6_nxt) == IPPROTO_TCP)
++ {
+ ND_PRINT("%s.%s > %s.%s: ",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ tcpport_string(ndo, sport),
+ GET_IP6ADDR_STRING(ip6->ip6_dst),
+ tcpport_string(ndo, dport));
+- } else {
++ }
++ else
++ {
+ ND_PRINT("%s > %s: ",
+ tcpport_string(ndo, sport), tcpport_string(ndo, dport));
+ }
+- } else {
+- if (GET_U_1(ip->ip_p) == IPPROTO_TCP) {
++ }
++ else
++ {
++ if (GET_U_1(ip->ip_p) == IPPROTO_TCP)
++ {
+ ND_PRINT("%s.%s > %s.%s: ",
+ GET_IPADDR_STRING(ip->ip_src),
+ tcpport_string(ndo, sport),
+ GET_IPADDR_STRING(ip->ip_dst),
+ tcpport_string(ndo, dport));
+- } else {
++ }
++ else
++ {
+ ND_PRINT("%s > %s: ",
+ tcpport_string(ndo, sport), tcpport_string(ndo, dport));
+ }
+@@ -229,7 +250,8 @@
+
+ hlen = TH_OFF(tp) * 4;
+
+- if (hlen < sizeof(*tp)) {
++ if (hlen < sizeof(*tp))
++ {
+ ND_PRINT(" tcp %u [bad hdr length %u - too short, < %zu]",
+ length - hlen, hlen, sizeof(*tp));
+ return;
+@@ -240,9 +262,11 @@
+ win = GET_BE_U_2(tp->th_win);
+ urp = GET_BE_U_2(tp->th_urp);
+
+- if (ndo->ndo_qflag) {
++ if (ndo->ndo_qflag)
++ {
+ ND_PRINT("tcp %u", length - hlen);
+- if (hlen > length) {
++ if (hlen > length)
++ {
+ ND_PRINT(" [bad hdr length %u - too long, > %u]",
+ hlen, length);
+ }
+@@ -252,7 +276,8 @@
+ flags = GET_U_1(tp->th_flags);
+ ND_PRINT("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags));
+
+- if (!ndo->ndo_Sflag && (flags & TH_ACK)) {
++ if (!ndo->ndo_Sflag && (flags & TH_ACK))
++ {
+ /*
+ * Find (or record) the initial sequence numbers for
+ * this conversation. (we pick an arbitrary
+@@ -260,7 +285,8 @@
+ * both directions).
+ */
+ rev = 0;
+- if (ip6) {
++ if (ip6)
++ {
+ struct tcp_seq_hash6 *th;
+ struct tcp_seq_hash6 *tcp_seq_hash;
+ const void *src, *dst;
+@@ -271,15 +297,19 @@
+ dst = (const void *)ip6->ip6_dst;
+ if (sport > dport)
+ rev = 1;
+- else if (sport == dport) {
++ else if (sport == dport)
++ {
+ if (UNALIGNED_MEMCMP(src, dst, sizeof(ip6->ip6_dst)) > 0)
+ rev = 1;
+ }
+- if (rev) {
++ if (rev)
++ {
+ UNALIGNED_MEMCPY(&tha.src, dst, sizeof(ip6->ip6_dst));
+ UNALIGNED_MEMCPY(&tha.dst, src, sizeof(ip6->ip6_src));
+ tha.port = ((u_int)dport) << 16 | sport;
+- } else {
++ }
++ else
++ {
+ UNALIGNED_MEMCPY(&tha.dst, dst, sizeof(ip6->ip6_dst));
+ UNALIGNED_MEMCPY(&tha.src, src, sizeof(ip6->ip6_src));
+ tha.port = ((u_int)sport) << 16 | dport;
+@@ -291,24 +321,28 @@
+ sizeof(th->addr)) == 0)
+ break;
+
+- if (!th->nxt || (flags & TH_SYN)) {
++ if (!th->nxt || (flags & TH_SYN))
++ {
+ /* didn't find it or new conversation */
+ /* calloc() return used by the 'tcp_seq_hash6'
+ hash table: do not free() */
+- if (th->nxt == NULL) {
++ if (th->nxt == NULL)
++ {
+ th->nxt = (struct tcp_seq_hash6 *)
+- calloc(1, sizeof(*th));
++ calloc(1, sizeof(*th));
+ if (th->nxt == NULL)
+ (*ndo->ndo_error)(ndo,
+- S_ERR_ND_MEM_ALLOC,
+- "%s: calloc", __func__);
++ S_ERR_ND_MEM_ALLOC,
++ "%s: calloc", __func__);
+ }
+ th->addr = tha;
+ if (rev)
+ th->ack = seq, th->seq = ack - 1;
+ else
+ th->seq = seq, th->ack = ack - 1;
+- } else {
++ }
++ else
++ {
+ if (rev)
+ seq -= th->ack, ack -= th->seq;
+ else
+@@ -317,7 +351,9 @@
+
+ thseq = th->seq;
+ thack = th->ack;
+- } else {
++ }
++ else
++ {
+ struct tcp_seq_hash *th;
+ struct tcp_seq_hash *tcp_seq_hash;
+ struct tha tha;
+@@ -325,17 +361,21 @@
+ tcp_seq_hash = tcp_seq_hash4;
+ if (sport > dport)
+ rev = 1;
+- else if (sport == dport) {
++ else if (sport == dport)
++ {
+ if (UNALIGNED_MEMCMP(ip->ip_src, ip->ip_dst, sizeof(ip->ip_dst)) > 0)
+ rev = 1;
+ }
+- if (rev) {
++ if (rev)
++ {
+ UNALIGNED_MEMCPY(&tha.src, ip->ip_dst,
+ sizeof(ip->ip_dst));
+ UNALIGNED_MEMCPY(&tha.dst, ip->ip_src,
+ sizeof(ip->ip_src));
+ tha.port = ((u_int)dport) << 16 | sport;
+- } else {
++ }
++ else
++ {
+ UNALIGNED_MEMCPY(&tha.dst, ip->ip_dst,
+ sizeof(ip->ip_dst));
+ UNALIGNED_MEMCPY(&tha.src, ip->ip_src,
+@@ -349,24 +389,28 @@
+ sizeof(th->addr)) == 0)
+ break;
+
+- if (!th->nxt || (flags & TH_SYN)) {
++ if (!th->nxt || (flags & TH_SYN))
++ {
+ /* didn't find it or new conversation */
+ /* calloc() return used by the 'tcp_seq_hash4'
+ hash table: do not free() */
+- if (th->nxt == NULL) {
++ if (th->nxt == NULL)
++ {
+ th->nxt = (struct tcp_seq_hash *)
+- calloc(1, sizeof(*th));
++ calloc(1, sizeof(*th));
+ if (th->nxt == NULL)
+ (*ndo->ndo_error)(ndo,
+- S_ERR_ND_MEM_ALLOC,
+- "%s: calloc", __func__);
++ S_ERR_ND_MEM_ALLOC,
++ "%s: calloc", __func__);
+ }
+ th->addr = tha;
+ if (rev)
+ th->ack = seq, th->seq = ack - 1;
+ else
+ th->seq = seq, th->ack = ack - 1;
+- } else {
++ }
++ else
++ {
+ if (rev)
+ seq -= th->ack, ack -= th->seq;
+ else
+@@ -376,58 +420,69 @@
+ thseq = th->seq;
+ thack = th->ack;
+ }
+- } else {
++ }
++ else
++ {
+ /*fool gcc*/
+ thseq = thack = rev = 0;
+ }
+- if (hlen > length) {
++ if (hlen > length)
++ {
+ ND_PRINT(" [bad hdr length %u - too long, > %u]",
+ hlen, length);
+ return;
+ }
+
+- if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented) {
++ if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented)
++ {
+ /* Check the checksum, if possible. */
+ uint16_t sum, tcp_sum;
+
+- if (IP_V(ip) == 4) {
+- if (ND_TTEST_LEN(tp->th_sport, length)) {
++ if (IP_V(ip) == 4)
++ {
++ if (ND_TTEST_LEN(tp->th_sport, length))
++ {
+ sum = tcp_cksum(ndo, ip, tp, length);
+ tcp_sum = GET_BE_U_2(tp->th_sum);
+
+ ND_PRINT(", cksum 0x%04x", tcp_sum);
+ if (sum != 0)
+ ND_PRINT(" (incorrect -> 0x%04x)",
+- in_cksum_shouldbe(tcp_sum, sum));
++ in_cksum_shouldbe(tcp_sum, sum));
+ else
+ ND_PRINT(" (correct)");
+ }
+- } else if (IP_V(ip) == 6) {
+- if (ND_TTEST_LEN(tp->th_sport, length)) {
++ }
++ else if (IP_V(ip) == 6)
++ {
++ if (ND_TTEST_LEN(tp->th_sport, length))
++ {
+ sum = tcp6_cksum(ndo, ip6, tp, length);
+ tcp_sum = GET_BE_U_2(tp->th_sum);
+
+ ND_PRINT(", cksum 0x%04x", tcp_sum);
+ if (sum != 0)
+ ND_PRINT(" (incorrect -> 0x%04x)",
+- in_cksum_shouldbe(tcp_sum, sum));
++ in_cksum_shouldbe(tcp_sum, sum));
+ else
+ ND_PRINT(" (correct)");
+-
+ }
+ }
+ }
+
+ length -= hlen;
+- if (ndo->ndo_vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) {
++ if (ndo->ndo_vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST))
++ {
+ ND_PRINT(", seq %u", seq);
+
+- if (length > 0) {
++ if (length > 0)
++ {
+ ND_PRINT(":%u", seq + length);
+ }
+ }
+
+- if (flags & TH_ACK) {
++ if (flags & TH_ACK)
++ {
+ ND_PRINT(", ack %u", ack);
+ }
+
+@@ -438,7 +493,8 @@
+ /*
+ * Handle any options.
+ */
+- if (hlen > sizeof(*tp)) {
++ if (hlen > sizeof(*tp))
++ {
+ const u_char *cp;
+ u_int i, opt, datalen;
+ u_int len;
+@@ -446,30 +502,37 @@
+ hlen -= sizeof(*tp);
+ cp = (const u_char *)tp + sizeof(*tp);
+ ND_PRINT(", options [");
+- while (hlen > 0) {
++ while (hlen > 0)
++ {
+ if (ch != '\0')
+ ND_PRINT("%c", ch);
+ opt = GET_U_1(cp);
+ cp++;
+ if (ZEROLENOPT(opt))
+ len = 1;
+- else {
++ else
++ {
+ len = GET_U_1(cp);
+- cp++; /* total including type, len */
++ cp++; /* total including type, len */
+ if (len < 2 || len > hlen)
+ goto bad;
+- --hlen; /* account for length byte */
++ --hlen; /* account for length byte */
+ }
+- --hlen; /* account for type byte */
++ --hlen; /* account for type byte */
+ datalen = 0;
+
+ /* Bail if "l" bytes of data are not left or were not captured */
+-#define LENCHECK(l) { if ((l) > hlen) goto bad; ND_TCHECK_LEN(cp, l); }
+-
++#define LENCHECK(l) \
++ { \
++ if ((l) > hlen) \
++ goto bad; \
++ ND_TCHECK_LEN(cp, l); \
++ }
+
+ ND_PRINT("%s", tok2str(tcp_option_values, "unknown-%u", opt));
+
+- switch (opt) {
++ switch (opt)
++ {
+
+ case TCPOPT_MAXSEG:
+ datalen = 2;
+@@ -485,21 +548,28 @@
+
+ case TCPOPT_SACK:
+ datalen = len - 2;
+- if (datalen % 8 != 0) {
++ if (datalen % 8 != 0)
++ {
+ ND_PRINT(" invalid sack");
+- } else {
++ }
++ else
++ {
+ uint32_t s, e;
+
+ ND_PRINT(" %u ", datalen / 8);
+- for (i = 0; i < datalen; i += 8) {
++ for (i = 0; i < datalen; i += 8)
++ {
+ LENCHECK(i + 4);
+ s = GET_BE_U_4(cp + i);
+ LENCHECK(i + 8);
+ e = GET_BE_U_4(cp + i + 4);
+- if (rev) {
++ if (rev)
++ {
+ s -= thseq;
+ e -= thseq;
+- } else {
++ }
++ else
++ {
+ s -= thack;
+ e -= thack;
+ }
+@@ -527,8 +597,8 @@
+ datalen = 8;
+ LENCHECK(datalen);
+ ND_PRINT(" val %u ecr %u",
+- GET_BE_U_4(cp),
+- GET_BE_U_4(cp + 4));
++ GET_BE_U_4(cp),
++ GET_BE_U_4(cp + 4));
+ break;
+
+ case TCPOPT_SIGNATURE:
+@@ -537,7 +607,8 @@
+ ND_PRINT(" ");
+ #ifdef HAVE_LIBCRYPTO
+ switch (tcp_verify_signature(ndo, ip, tp,
+- bp + TH_OFF(tp) * 4, length, cp)) {
++ bp + TH_OFF(tp) * 4, length, cp))
++ {
+
+ case SIGNATURE_VALID:
+ ND_PRINT("valid");
+@@ -574,17 +645,22 @@
+ * (This includes the Kind and Length fields already processed
+ * at this point.)
+ */
+- if (datalen < 2) {
++ if (datalen < 2)
++ {
+ nd_print_invalid(ndo);
+- } else {
++ }
++ else
++ {
+ LENCHECK(1);
+ ND_PRINT(" keyid %u", GET_U_1(cp));
+ LENCHECK(2);
+ ND_PRINT(" rnextkeyid %u",
+ GET_U_1(cp + 1));
+- if (datalen > 2) {
++ if (datalen > 2)
++ {
+ ND_PRINT(" mac 0x");
+- for (i = 2; i < datalen; i++) {
++ for (i = 2; i < datalen; i++)
++ {
+ LENCHECK(i + 1);
+ ND_PRINT("%02x",
+ GET_U_1(cp + i));
+@@ -615,7 +691,7 @@
+ break;
+
+ case TCPOPT_MPTCP:
+- {
++ {
+ const u_char *snapend_save;
+ int ret;
+
+@@ -636,7 +712,7 @@
+ if (!ret)
+ goto bad;
+ break;
+- }
++ }
+
+ case TCPOPT_FASTOPEN:
+ datalen = len - 2;
+@@ -654,7 +730,8 @@
+ magic = GET_BE_U_2(cp);
+ ND_PRINT("-");
+
+- switch(magic) {
++ switch (magic)
++ {
+
+ case 0xf989: /* TCP Fast Open RFC 7413 */
+ print_tcp_fastopen_option(ndo, cp + 2, datalen - 2, TRUE);
+@@ -671,7 +748,8 @@
+ datalen = len - 2;
+ if (datalen)
+ ND_PRINT(" 0x");
+- for (i = 0; i < datalen; ++i) {
++ for (i = 0; i < datalen; ++i)
++ {
+ LENCHECK(i + 1);
+ ND_PRINT("%02x", GET_U_1(cp + i));
+ }
+@@ -683,9 +761,9 @@
+ hlen -= datalen;
+
+ /* Check specification against observed length */
+- ++datalen; /* option octet */
++ ++datalen; /* option octet */
+ if (!ZEROLENOPT(opt))
+- ++datalen; /* size octet */
++ ++datalen; /* size octet */
+ if (datalen != len)
+ ND_PRINT("[len %u]", len);
+ ch = ',';
+@@ -711,19 +789,23 @@
+ * Do a bounds check before decoding the payload.
+ * At least the header data is required.
+ */
+- if (!ND_TTEST_LEN(bp, header_len)) {
++ if (!ND_TTEST_LEN(bp, header_len))
++ {
+ ND_PRINT(" [remaining caplen(%u) < header length(%u)]",
+ ND_BYTES_AVAILABLE_AFTER(bp), header_len);
+ nd_trunc_longjmp(ndo);
+ }
+ bp += header_len;
+- if ((flags & TH_RST) && ndo->ndo_vflag) {
++ if ((flags & TH_RST) && ndo->ndo_vflag)
++ {
+ print_tcp_rst_data(ndo, bp, length);
+ return;
+ }
+
+- if (ndo->ndo_packettype) {
+- switch (ndo->ndo_packettype) {
++ if (ndo->ndo_packettype)
++ {
++ switch (ndo->ndo_packettype)
++ {
+ case PT_ZMTP1:
+ zmtp1_print(ndo, bp, length);
+ break;
+@@ -738,52 +820,107 @@
+ return;
+ }
+
+- if (IS_SRC_OR_DST_PORT(TELNET_PORT)) {
++ if (IS_SRC_OR_DST_TCPPORT(TELNET_PORT))
++ {
+ telnet_print(ndo, bp, length);
+- } else if (IS_SRC_OR_DST_PORT(SMTP_PORT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(SMTP_PORT))
++ {
+ ND_PRINT(": ");
+ smtp_print(ndo, bp, length);
+- } else if (IS_SRC_OR_DST_PORT(WHOIS_PORT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(WHOIS_PORT))
++ {
+ ND_PRINT(": ");
+ whois_print(ndo, bp, length);
+- } else if (IS_SRC_OR_DST_PORT(BGP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(BGP_PORT))
++ {
+ bgp_print(ndo, bp, length);
+- else if (IS_SRC_OR_DST_PORT(PPTP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(PPTP_PORT))
++ {
+ pptp_print(ndo, bp);
+- else if (IS_SRC_OR_DST_PORT(REDIS_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(REDIS_PORT))
++ {
+ resp_print(ndo, bp, length);
+- else if (IS_SRC_OR_DST_PORT(SSH_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(SSH_PORT))
++ {
+ ssh_print(ndo, bp, length);
++ return;
++ }
+ #ifdef ENABLE_SMB
+- else if (IS_SRC_OR_DST_PORT(NETBIOS_SSN_PORT))
++ if (IS_SRC_OR_DST_TCPPORT(NETBIOS_SSN_PORT))
++ {
+ nbt_tcp_print(ndo, bp, length);
+- else if (IS_SRC_OR_DST_PORT(SMB_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(SMB_PORT))
++ {
+ smb_tcp_print(ndo, bp, length);
++ return;
++ }
+ #endif
+- else if (IS_SRC_OR_DST_PORT(BEEP_PORT))
++ if (IS_SRC_OR_DST_TCPPORT(BEEP_PORT))
++ {
+ beep_print(ndo, bp, length);
+- else if (IS_SRC_OR_DST_PORT(OPENFLOW_PORT_OLD) || IS_SRC_OR_DST_PORT(OPENFLOW_PORT_IANA))
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(OPENFLOW_PORT_OLD) || IS_SRC_OR_DST_TCPPORT(OPENFLOW_PORT_IANA))
++ {
+ openflow_print(ndo, bp, length);
+- else if (IS_SRC_OR_DST_PORT(FTP_PORT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(FTP_PORT))
++ {
+ ND_PRINT(": ");
+ ftp_print(ndo, bp, length);
+- } else if (IS_SRC_OR_DST_PORT(HTTP_PORT) || IS_SRC_OR_DST_PORT(HTTP_PORT_ALT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(HTTP_PORT) || IS_SRC_OR_DST_TCPPORT(HTTP_PORT_ALT))
++ {
+ ND_PRINT(": ");
+ http_print(ndo, bp, length);
+- } else if (IS_SRC_OR_DST_PORT(RTSP_PORT) || IS_SRC_OR_DST_PORT(RTSP_PORT_ALT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(RTSP_PORT) || IS_SRC_OR_DST_TCPPORT(RTSP_PORT_ALT))
++ {
+ ND_PRINT(": ");
+ rtsp_print(ndo, bp, length);
+- } else if (IS_SRC_OR_DST_PORT(NAMESERVER_PORT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(TCPDUMP_NAMESERVER_TCP_PORT))
++ {
+ /* over_tcp: TRUE, is_mdns: FALSE */
+ domain_print(ndo, bp, length, TRUE, FALSE);
+- } else if (IS_SRC_OR_DST_PORT(MSDP_PORT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(MSDP_PORT))
++ {
+ msdp_print(ndo, bp, length);
+- } else if (IS_SRC_OR_DST_PORT(RPKI_RTR_PORT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(RPKI_RTR_PORT))
++ {
+ rpki_rtr_print(ndo, bp, length);
+- } else if (IS_SRC_OR_DST_PORT(LDP_PORT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_TCPPORT(TCPDUMP_LDP_PORT))
++ {
+ ldp_print(ndo, bp, length);
+- } else if ((IS_SRC_OR_DST_PORT(NFS_PORT)) &&
+- length >= 4 && ND_TTEST_4(bp)) {
++ return;
++ }
++ if ((IS_SRC_OR_DST_TCPPORT(NFS_PORT)) &&
++ length >= 4 && ND_TTEST_4(bp))
++ {
+ /*
+ * If data present, header length valid, and NFS port used,
+ * assume NFS.
+@@ -795,24 +932,28 @@
+ enum sunrpc_msg_type direction;
+
+ fraglen = GET_BE_U_4(bp) & 0x7FFFFFFF;
+- if (fraglen > (length) - 4)
+- fraglen = (length) - 4;
++ if (fraglen > (length)-4)
++ fraglen = (length)-4;
+ rp = (const struct sunrpc_msg *)(bp + 4);
+- if (ND_TTEST_4(rp->rm_direction)) {
+- direction = (enum sunrpc_msg_type) GET_BE_U_4(rp->rm_direction);
+- if (dport == NFS_PORT && direction == SUNRPC_CALL) {
++ if (ND_TTEST_4(rp->rm_direction))
++ {
++ direction = (enum sunrpc_msg_type)GET_BE_U_4(rp->rm_direction);
++ if (dport == NFS_PORT && direction == SUNRPC_CALL)
++ {
+ ND_PRINT(": NFS request xid %u ",
+ GET_BE_U_4(rp->rm_xid));
+ nfsreq_noaddr_print(ndo, (const u_char *)rp, fraglen, (const u_char *)ip);
+ return;
+ }
+- if (sport == NFS_PORT && direction == SUNRPC_REPLY) {
++ if (sport == NFS_PORT && direction == SUNRPC_REPLY)
++ {
+ ND_PRINT(": NFS reply xid %u ",
+ GET_BE_U_4(rp->rm_xid));
+ nfsreply_noaddr_print(ndo, (const u_char *)rp, fraglen, (const u_char *)ip);
+ return;
+ }
+ }
++ return;
+ }
+
+ return;
+@@ -849,12 +990,14 @@
+ u_char c;
+
+ ND_PRINT(ND_TTEST_LEN(sp, length) ? " [RST" : " [!RST");
+- if (length > MAX_RST_DATA_LEN) {
+- length = MAX_RST_DATA_LEN; /* can use -X for longer */
+- ND_PRINT("+"); /* indicate we truncate */
++ if (length > MAX_RST_DATA_LEN)
++ {
++ length = MAX_RST_DATA_LEN; /* can use -X for longer */
++ ND_PRINT("+"); /* indicate we truncate */
+ }
+ ND_PRINT(" ");
+- while (length && sp < ndo->ndo_snapend) {
++ while (length && sp < ndo->ndo_snapend)
++ {
+ c = GET_U_1(sp);
+ sp++;
+ fn_print_char(ndo, c);
+@@ -872,14 +1015,20 @@
+ if (exp)
+ ND_PRINT("tfo");
+
+- if (datalen == 0) {
++ if (datalen == 0)
++ {
+ /* Fast Open Cookie Request */
+ ND_PRINT(" cookiereq");
+- } else {
++ }
++ else
++ {
+ /* Fast Open Cookie */
+- if (datalen % 2 != 0 || datalen < 4 || datalen > 16) {
++ if (datalen % 2 != 0 || datalen < 4 || datalen > 16)
++ {
+ nd_print_invalid(ndo);
+- } else {
++ }
++ else
++ {
+ ND_PRINT(" cookie ");
+ for (i = 0; i < datalen; ++i)
+ ND_PRINT("%02x", GET_U_1(cp + i));
+@@ -891,7 +1040,7 @@
+ DIAG_OFF_DEPRECATION
+ static int
+ tcp_verify_signature(netdissect_options *ndo,
+- const struct ip *ip, const struct tcphdr *tp,
++ const struct tcpdump_ip *ip, const struct tcphdr *tp,
+ const u_char *data, u_int length, const u_char *rcvsig)
+ {
+ struct tcphdr tp1;
+@@ -899,18 +1048,20 @@
+ char zero_proto = 0;
+ MD5_CTX ctx;
+ uint16_t savecsum, tlen;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip6_hdr *ip6;
+ uint32_t len32;
+ uint8_t nxt;
+
+- if (data + length > ndo->ndo_snapend) {
++ if (data + length > ndo->ndo_snapend)
++ {
+ ND_PRINT("snaplen too short, ");
+ return (CANT_CHECK_SIGNATURE);
+ }
+
+ tp1 = *tp;
+
+- if (ndo->ndo_sigsecret == NULL) {
++ if (ndo->ndo_sigsecret == NULL)
++ {
+ ND_PRINT("shared secret not supplied with -M, ");
+ return (CANT_CHECK_SIGNATURE);
+ }
+@@ -919,7 +1070,8 @@
+ /*
+ * Step 1: Update MD5 hash with IP pseudo-header.
+ */
+- if (IP_V(ip) == 4) {
++ if (IP_V(ip) == 4)
++ {
+ MD5_Update(&ctx, (const char *)&ip->ip_src, sizeof(ip->ip_src));
+ MD5_Update(&ctx, (const char *)&ip->ip_dst, sizeof(ip->ip_dst));
+ MD5_Update(&ctx, (const char *)&zero_proto, sizeof(zero_proto));
+@@ -927,8 +1079,10 @@
+ tlen = GET_BE_U_2(ip->ip_len) - IP_HL(ip) * 4;
+ tlen = htons(tlen);
+ MD5_Update(&ctx, (const char *)&tlen, sizeof(tlen));
+- } else if (IP_V(ip) == 6) {
+- ip6 = (const struct ip6_hdr *)ip;
++ }
++ else if (IP_V(ip) == 6)
++ {
++ ip6 = (const struct tcpdump_ip6_hdr *)ip;
+ MD5_Update(&ctx, (const char *)&ip6->ip6_src, sizeof(ip6->ip6_src));
+ MD5_Update(&ctx, (const char *)&ip6->ip6_dst, sizeof(ip6->ip6_dst));
+ len32 = htonl(GET_BE_U_2(ip6->ip6_plen));
+@@ -939,7 +1093,9 @@
+ MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt));
+ nxt = IPPROTO_TCP;
+ MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt));
+- } else {
++ }
++ else
++ {
+ ND_PRINT("IP version not 4 or 6, ");
+ return (CANT_CHECK_SIGNATURE);
+ }
+diff -uNr tcpdump-tcpdump-4.99.4/print-tcp.h tcpdump-for-stellar/print-tcp.h
+--- tcpdump-tcpdump-4.99.4/print-tcp.h 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/print-tcp.h 2024-10-30 17:34:06.265498697 +0800
+@@ -0,0 +1,159 @@
++/*
++ * Copyright (c) 1982, 1986, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
++ */
++#pragma once
++/*
++ * TCP header.
++ * Per RFC 793, September, 1981.
++ */
++struct tcphdr
++{
++ nd_uint16_t th_sport; /* source port */
++ nd_uint16_t th_dport; /* destination port */
++ nd_uint32_t th_seq; /* sequence number */
++ nd_uint32_t th_ack; /* acknowledgement number */
++ nd_uint8_t th_offx2; /* data offset, rsvd */
++ nd_uint8_t th_flags;
++ nd_uint16_t th_win; /* window */
++ nd_uint16_t th_sum; /* checksum */
++ nd_uint16_t th_urp; /* urgent pointer */
++};
++
++#define TH_OFF(th) ((GET_U_1((th)->th_offx2) & 0xf0) >> 4)
++
++/* TCP flags */
++#define TH_FIN 0x01
++#define TH_SYN 0x02
++#define TH_RST 0x04
++#define TH_PUSH 0x08
++#define TH_ACK 0x10
++#define TH_URG 0x20
++#define TH_ECNECHO 0x40 /* ECN Echo */
++#define TH_CWR 0x80 /* ECN Cwnd Reduced */
++
++#define TCPOPT_EOL 0
++#define TCPOPT_NOP 1
++#define TCPOPT_MAXSEG 2
++#define TCPOLEN_MAXSEG 4
++#define TCPOPT_WSCALE 3 /* window scale factor (rfc1323) */
++#define TCPOPT_SACKOK 4 /* selective ack ok (rfc2018) */
++#define TCPOPT_SACK 5 /* selective ack (rfc2018) */
++#define TCPOPT_ECHO 6 /* echo (rfc1072) */
++#define TCPOPT_ECHOREPLY 7 /* echo (rfc1072) */
++#define TCPOPT_TIMESTAMP 8 /* timestamp (rfc1323) */
++#define TCPOLEN_TIMESTAMP 10
++#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP + 2) /* appendix A */
++#define TCPOPT_CC 11 /* T/TCP CC options (rfc1644) */
++#define TCPOPT_CCNEW 12 /* T/TCP CC options (rfc1644) */
++#define TCPOPT_CCECHO 13 /* T/TCP CC options (rfc1644) */
++#define TCPOPT_SIGNATURE 19 /* Keyed MD5 (rfc2385) */
++#define TCPOLEN_SIGNATURE 18
++#define TCP_SIGLEN 16 /* length of an option 19 digest */
++#define TCPOPT_SCPS 20 /* SCPS-TP (CCSDS 714.0-B-2) */
++#define TCPOPT_UTO 28 /* tcp user timeout (rfc5482) */
++#define TCPOLEN_UTO 4
++#define TCPOPT_TCPAO 29 /* TCP authentication option (rfc5925) */
++#define TCPOPT_MPTCP 30 /* MPTCP options */
++#define TCPOPT_FASTOPEN 34 /* TCP Fast Open (rfc7413) */
++#define TCPOPT_EXPERIMENT2 254 /* experimental headers (rfc4727) */
++
++#define TCPOPT_TSTAMP_HDR \
++ (TCPOPT_NOP << 24 | TCPOPT_NOP << 16 | TCPOPT_TIMESTAMP << 8 | TCPOLEN_TIMESTAMP)
++
++#ifndef FTP_PORT
++#define FTP_PORT 21
++#endif
++#ifndef SSH_PORT
++#define SSH_PORT 22
++#endif
++#ifndef TELNET_PORT
++#define TELNET_PORT 23
++#endif
++#ifndef SMTP_PORT
++#define SMTP_PORT 25
++#endif
++#ifndef WHOIS_PORT
++#define WHOIS_PORT 43
++#endif
++
++#define TCPDUMP_NAMESERVER_TCP_PORT 53
++
++#ifndef HTTP_PORT
++#define HTTP_PORT 80
++#endif
++#ifndef NETBIOS_SSN_PORT
++#define NETBIOS_SSN_PORT 139 /* RFC 1001, RFC 1002 */
++#endif
++#ifndef BGP_PORT
++#define BGP_PORT 179
++#endif
++#ifndef RPKI_RTR_PORT
++#define RPKI_RTR_PORT 323
++#endif
++#ifndef SMB_PORT
++#define SMB_PORT 445
++#endif
++#ifndef RTSP_PORT
++#define RTSP_PORT 554
++#endif
++#ifndef MSDP_PORT
++#define MSDP_PORT 639
++#endif
++#ifndef TCPDUMP_LDP_PORT
++#define TCPDUMP_LDP_PORT 646
++#endif
++#ifndef PPTP_PORT
++#define PPTP_PORT 1723
++#endif
++#ifndef NFS_PORT
++#define NFS_PORT 2049
++#endif
++#ifndef OPENFLOW_PORT_OLD
++#define OPENFLOW_PORT_OLD 6633
++#endif
++#ifndef OPENFLOW_PORT_IANA
++#define OPENFLOW_PORT_IANA 6653
++#endif
++#ifndef HTTP_PORT_ALT
++#define HTTP_PORT_ALT 8080
++#endif
++#ifndef RTSP_PORT_ALT
++#define RTSP_PORT_ALT 8554
++#endif
++#ifndef BEEP_PORT
++#define BEEP_PORT 10288
++#endif
++#ifndef REDIS_PORT
++#define REDIS_PORT 6379
++#endif
+diff -uNr tcpdump-tcpdump-4.99.4/print-udp.c tcpdump-for-stellar/print-udp.c
+--- tcpdump-tcpdump-4.99.4/print-udp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-udp.c 2024-10-30 17:34:05.631498702 +0800
+@@ -20,109 +20,118 @@
+ */
+
+ /* \summary: UDP printer */
+-
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+-
+ #include "netdissect-stdinc.h"
+-
+ #include "netdissect.h"
+ #include "addrtoname.h"
+ #include "extract.h"
+ #include "appletalk.h"
+-
+-#include "udp.h"
+-
+-#include "ip.h"
+-#include "ip6.h"
++#include "tcpdump_ip.h"
++#include "tcpdump_ip6.h"
+ #include "ipproto.h"
+ #include "rpc_auth.h"
+ #include "rpc_msg.h"
+-
+ #include "nfs.h"
++#include "print-udp.h"
+
+-
+-struct rtcphdr {
+- nd_uint16_t rh_flags; /* T:2 P:1 CNT:5 PT:8 */
+- nd_uint16_t rh_len; /* length of message (in words) */
+- nd_uint32_t rh_ssrc; /* synchronization src id */
++struct rtcphdr
++{
++ nd_uint16_t rh_flags; /* T:2 P:1 CNT:5 PT:8 */
++ nd_uint16_t rh_len; /* length of message (in words) */
++ nd_uint32_t rh_ssrc; /* synchronization src id */
+ };
+
+-typedef struct {
+- nd_uint32_t upper; /* more significant 32 bits */
+- nd_uint32_t lower; /* less significant 32 bits */
++typedef struct
++{
++ nd_uint32_t upper; /* more significant 32 bits */
++ nd_uint32_t lower; /* less significant 32 bits */
+ } ntp64;
+
+ /*
+ * Sender report.
+ */
+-struct rtcp_sr {
+- ntp64 sr_ntp; /* 64-bit ntp timestamp */
+- nd_uint32_t sr_ts; /* reference media timestamp */
+- nd_uint32_t sr_np; /* no. packets sent */
+- nd_uint32_t sr_nb; /* no. bytes sent */
++struct rtcp_sr
++{
++ ntp64 sr_ntp; /* 64-bit ntp timestamp */
++ nd_uint32_t sr_ts; /* reference media timestamp */
++ nd_uint32_t sr_np; /* no. packets sent */
++ nd_uint32_t sr_nb; /* no. bytes sent */
+ };
+
+ /*
+ * Receiver report.
+ * Time stamps are middle 32-bits of ntp timestamp.
+ */
+-struct rtcp_rr {
+- nd_uint32_t rr_srcid; /* sender being reported */
+- nd_uint32_t rr_nl; /* no. packets lost */
+- nd_uint32_t rr_ls; /* extended last seq number received */
+- nd_uint32_t rr_dv; /* jitter (delay variance) */
+- nd_uint32_t rr_lsr; /* orig. ts from last rr from this src */
+- nd_uint32_t rr_dlsr; /* time from recpt of last rr to xmit time */
++struct rtcp_rr
++{
++ nd_uint32_t rr_srcid; /* sender being reported */
++ nd_uint32_t rr_nl; /* no. packets lost */
++ nd_uint32_t rr_ls; /* extended last seq number received */
++ nd_uint32_t rr_dv; /* jitter (delay variance) */
++ nd_uint32_t rr_lsr; /* orig. ts from last rr from this src */
++ nd_uint32_t rr_dlsr; /* time from recpt of last rr to xmit time */
+ };
+
+ /*XXX*/
+-#define RTCP_PT_SR 200
+-#define RTCP_PT_RR 201
+-#define RTCP_PT_SDES 202
+-#define RTCP_SDES_CNAME 1
+-#define RTCP_SDES_NAME 2
+-#define RTCP_SDES_EMAIL 3
+-#define RTCP_SDES_PHONE 4
+-#define RTCP_SDES_LOC 5
+-#define RTCP_SDES_TOOL 6
+-#define RTCP_SDES_NOTE 7
+-#define RTCP_SDES_PRIV 8
+-#define RTCP_PT_BYE 203
+-#define RTCP_PT_APP 204
++#define RTCP_PT_SR 200
++#define RTCP_PT_RR 201
++#define RTCP_PT_SDES 202
++#define RTCP_SDES_CNAME 1
++#define RTCP_SDES_NAME 2
++#define RTCP_SDES_EMAIL 3
++#define RTCP_SDES_PHONE 4
++#define RTCP_SDES_LOC 5
++#define RTCP_SDES_TOOL 6
++#define RTCP_SDES_NOTE 7
++#define RTCP_SDES_PRIV 8
++#define RTCP_PT_BYE 203
++#define RTCP_PT_APP 204
++
++#define IS_SRC_OR_DST_UDPPORT(p) (sport == (p) || dport == (p))
++
++static inline int CHECK_IS_SRC_OR_DST_PORT(unsigned short p, unsigned short sport, unsigned short dport)
++{
++ return (sport == (p) || dport == (p));
++}
+
+-static void
+-vat_print(netdissect_options *ndo, const u_char *hdr, u_int length)
++// cppcheck-suppress
++static void vat_print(netdissect_options *ndo, const u_char *hdr, u_int length)
+ {
+ /* vat/vt audio */
+ u_int ts;
+
+ ndo->ndo_protocol = "vat";
+- if (length < 2) {
++ if (length < 2)
++ {
+ ND_PRINT("udp/va/vat, length %u < 2", length);
+ return;
+ }
+ ts = GET_BE_U_2(hdr);
+- if ((ts & 0xf060) != 0) {
++ if ((ts & 0xf060) != 0)
++ {
+ /* probably vt */
+ ND_PRINT("udp/vt %u %u / %u",
+- length,
+- ts & 0x3ff, ts >> 10);
+- } else {
++ length,
++ ts & 0x3ff, ts >> 10);
++ }
++ else
++ {
+ /* probably vat */
+ uint32_t i0, i1;
+
+- if (length < 8) {
++ if (length < 8)
++ {
+ ND_PRINT("udp/vat, length %u < 8", length);
+ return;
+ }
+ i0 = GET_BE_U_4(&((const u_int *)hdr)[0]);
+ i1 = GET_BE_U_4(&((const u_int *)hdr)[1]);
+ ND_PRINT("udp/vat %u c%u %u%s",
+- length - 8,
+- i0 & 0xffff,
+- i1, i0 & 0x800000? "*" : "");
++ length - 8,
++ i0 & 0xffff,
++ i1, i0 & 0x800000 ? "*" : "");
+ /* audio format */
+ if (i0 & 0x1f0000)
+ ND_PRINT(" f%u", (i0 >> 16) & 0x1f);
+@@ -131,17 +140,18 @@
+ }
+ }
+
+-static void
+-rtp_print(netdissect_options *ndo, const u_char *hdr, u_int len)
++// cppcheck-suppress
++static void rtp_print(netdissect_options *ndo, const u_char *hdr, u_int len)
+ {
+ /* rtp v1 or v2 */
+ const u_int *ip = (const u_int *)hdr;
+ u_int hasopt, hasext, contype, hasmarker, dlen;
+ uint32_t i0, i1;
+- const char * ptype;
++ const char *ptype;
+
+ ndo->ndo_protocol = "rtp";
+- if (len < 8) {
++ if (len < 8)
++ {
+ ND_PRINT("udp/rtp, length %u < 8", len);
+ return;
+ }
+@@ -153,15 +163,19 @@
+ len -= 2;
+ hasopt = 0;
+ hasext = 0;
+- if ((i0 >> 30) == 1) {
++ if ((i0 >> 30) == 1)
++ {
+ /* rtp v1 - draft-ietf-avt-rtp-04 */
+ hasopt = i0 & 0x800000;
+ contype = (i0 >> 16) & 0x3f;
+ hasmarker = i0 & 0x400000;
+ ptype = "rtpv1";
+- } else {
++ }
++ else
++ {
+ /* rtp v2 - RFC 3550 */
+- if (dlen < 4) {
++ if (dlen < 4)
++ {
+ ND_PRINT("udp/rtp, length %u < 12", dlen + 8);
+ return;
+ }
+@@ -174,21 +188,25 @@
+ len -= 1;
+ }
+ ND_PRINT("udp/%s %u c%u %s%s %u %u",
+- ptype,
+- dlen,
+- contype,
+- (hasopt || hasext)? "+" : "",
+- hasmarker? "*" : "",
+- i0 & 0xffff,
+- i1);
+- if (ndo->ndo_vflag) {
++ ptype,
++ dlen,
++ contype,
++ (hasopt || hasext) ? "+" : "",
++ hasmarker ? "*" : "",
++ i0 & 0xffff,
++ i1);
++ if (ndo->ndo_vflag)
++ {
+ ND_PRINT(" %u", GET_BE_U_4(&((const u_int *)hdr)[2]));
+- if (hasopt) {
++ if (hasopt)
++ {
+ u_int i2, optlen;
+- do {
++ do
++ {
+ i2 = GET_BE_U_4(ip);
+ optlen = (i2 >> 16) & 0xff;
+- if (optlen == 0 || optlen > len) {
++ if (optlen == 0 || optlen > len)
++ {
+ ND_PRINT(" !opt");
+ return;
+ }
+@@ -196,11 +214,13 @@
+ len -= optlen;
+ } while ((int)i2 >= 0);
+ }
+- if (hasext) {
++ if (hasext)
++ {
+ u_int i2, extlen;
+ i2 = GET_BE_U_4(ip);
+ extlen = (i2 & 0xffff) + 1;
+- if (extlen > len) {
++ if (extlen > len)
++ {
+ ND_PRINT(" !ext");
+ return;
+ }
+@@ -211,8 +231,8 @@
+ }
+ }
+
+-static const u_char *
+-rtcp_print(netdissect_options *ndo, const u_char *hdr, const u_char *ep)
++// cppcheck-suppress
++static const u_char *rtcp_print(netdissect_options *ndo, const u_char *hdr, const u_char *ep)
+ {
+ /* rtp v2 control (rtcp) */
+ const struct rtcp_rr *rr = 0;
+@@ -230,7 +250,8 @@
+ len = (GET_BE_U_2(rh->rh_len) + 1) * 4;
+ flags = GET_BE_U_2(rh->rh_flags);
+ cnt = (flags >> 8) & 0x1f;
+- switch (flags & 0xff) {
++ switch (flags & 0xff)
++ {
+ case RTCP_PT_SR:
+ sr = (const struct rtcp_sr *)(rh + 1);
+ ND_PRINT(" sr");
+@@ -242,10 +263,10 @@
+ goto trunc;
+ ND_TCHECK_SIZE(sr);
+ ts = (double)(GET_BE_U_4(sr->sr_ntp.upper)) +
+- ((double)(GET_BE_U_4(sr->sr_ntp.lower)) /
+- FMAXINT);
++ ((double)(GET_BE_U_4(sr->sr_ntp.lower)) /
++ FMAXINT);
+ ND_PRINT(" @%.2f %u %up %ub", ts, GET_BE_U_4(sr->sr_ts),
+- GET_BE_U_4(sr->sr_np), GET_BE_U_4(sr->sr_nb));
++ GET_BE_U_4(sr->sr_np), GET_BE_U_4(sr->sr_nb));
+ rr = (const struct rtcp_rr *)(sr + 1);
+ break;
+ case RTCP_PT_RR:
+@@ -275,7 +296,8 @@
+ }
+ if (cnt > 1)
+ ND_PRINT(" c%u", cnt);
+- while (cnt != 0) {
++ while (cnt != 0)
++ {
+ if ((const u_char *)(rr + 1) > ep)
+ goto trunc;
+ ND_TCHECK_SIZE(rr);
+@@ -284,9 +306,9 @@
+ ts = (double)(GET_BE_U_4(rr->rr_lsr)) / 65536.;
+ dts = (double)(GET_BE_U_4(rr->rr_dlsr)) / 65536.;
+ ND_PRINT(" %ul %us %uj @%.2f+%.2f",
+- GET_BE_U_4(rr->rr_nl) & 0x00ffffff,
+- GET_BE_U_4(rr->rr_ls),
+- GET_BE_U_4(rr->rr_dv), ts, dts);
++ GET_BE_U_4(rr->rr_nl) & 0x00ffffff,
++ GET_BE_U_4(rr->rr_ls),
++ GET_BE_U_4(rr->rr_dv), ts, dts);
+ cnt--;
+ }
+ return (hdr + len);
+@@ -296,94 +318,112 @@
+ return ep;
+ }
+
+-static uint16_t udp_cksum(netdissect_options *ndo, const struct ip *ip,
+- const struct udphdr *up,
+- u_int len)
++// cppcheck-suppress
++static uint16_t udp_cksum(netdissect_options *ndo, const struct tcpdump_ip *ip,
++ const struct udphdr *up,
++ u_int len)
+ {
+ return nextproto4_cksum(ndo, ip, (const uint8_t *)(const void *)up, len, len,
+- IPPROTO_UDP);
++ IPPROTO_UDP);
+ }
+-
+-static uint16_t udp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
+- const struct udphdr *up, u_int len)
++// cppcheck-suppress
++static uint16_t udp6_cksum(netdissect_options *ndo, const struct tcpdump_ip6_hdr *ip6,
++ const struct udphdr *up, u_int len)
+ {
+ return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)up, len, len,
+- IPPROTO_UDP);
++ IPPROTO_UDP);
+ }
+
+-static void
+-udpipaddr_print(netdissect_options *ndo, const struct ip *ip, int sport, int dport)
++// cppcheck-suppress
++static void udpipaddr_print(netdissect_options *ndo, const struct tcpdump_ip *ip, int sport, int dport)
+ {
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip6_hdr *ip6;
+
+ if (IP_V(ip) == 6)
+- ip6 = (const struct ip6_hdr *)ip;
++ ip6 = (const struct tcpdump_ip6_hdr *)ip;
+ else
+ ip6 = NULL;
+
+- if (ip6) {
+- if (GET_U_1(ip6->ip6_nxt) == IPPROTO_UDP) {
+- if (sport == -1) {
++ if (ip6)
++ {
++ if (GET_U_1(ip6->ip6_nxt) == IPPROTO_UDP)
++ {
++ if (sport == -1)
++ {
+ ND_PRINT("%s > %s: ",
+- GET_IP6ADDR_STRING(ip6->ip6_src),
+- GET_IP6ADDR_STRING(ip6->ip6_dst));
+- } else {
++ GET_IP6ADDR_STRING(ip6->ip6_src),
++ GET_IP6ADDR_STRING(ip6->ip6_dst));
++ }
++ else
++ {
+ ND_PRINT("%s.%s > %s.%s: ",
+- GET_IP6ADDR_STRING(ip6->ip6_src),
+- udpport_string(ndo, (uint16_t)sport),
+- GET_IP6ADDR_STRING(ip6->ip6_dst),
+- udpport_string(ndo, (uint16_t)dport));
++ GET_IP6ADDR_STRING(ip6->ip6_src),
++ udpport_string(ndo, (uint16_t)sport),
++ GET_IP6ADDR_STRING(ip6->ip6_dst),
++ udpport_string(ndo, (uint16_t)dport));
+ }
+- } else {
+- if (sport != -1) {
++ }
++ else
++ {
++ if (sport != -1)
++ {
+ ND_PRINT("%s > %s: ",
+- udpport_string(ndo, (uint16_t)sport),
+- udpport_string(ndo, (uint16_t)dport));
++ udpport_string(ndo, (uint16_t)sport),
++ udpport_string(ndo, (uint16_t)dport));
+ }
+ }
+- } else {
+- if (GET_U_1(ip->ip_p) == IPPROTO_UDP) {
+- if (sport == -1) {
++ }
++ else
++ {
++ if (GET_U_1(ip->ip_p) == IPPROTO_UDP)
++ {
++ if (sport == -1)
++ {
+ ND_PRINT("%s > %s: ",
+- GET_IPADDR_STRING(ip->ip_src),
+- GET_IPADDR_STRING(ip->ip_dst));
+- } else {
++ GET_IPADDR_STRING(ip->ip_src),
++ GET_IPADDR_STRING(ip->ip_dst));
++ }
++ else
++ {
+ ND_PRINT("%s.%s > %s.%s: ",
+- GET_IPADDR_STRING(ip->ip_src),
+- udpport_string(ndo, (uint16_t)sport),
+- GET_IPADDR_STRING(ip->ip_dst),
+- udpport_string(ndo, (uint16_t)dport));
++ GET_IPADDR_STRING(ip->ip_src),
++ udpport_string(ndo, (uint16_t)sport),
++ GET_IPADDR_STRING(ip->ip_dst),
++ udpport_string(ndo, (uint16_t)dport));
+ }
+- } else {
+- if (sport != -1) {
++ }
++ else
++ {
++ if (sport != -1)
++ {
+ ND_PRINT("%s > %s: ",
+- udpport_string(ndo, (uint16_t)sport),
+- udpport_string(ndo, (uint16_t)dport));
++ udpport_string(ndo, (uint16_t)sport),
++ udpport_string(ndo, (uint16_t)dport));
+ }
+ }
+ }
+ }
+
+-void
+-udp_print(netdissect_options *ndo, const u_char *bp, u_int length,
+- const u_char *bp2, int fragmented, u_int ttl_hl)
++// cppcheck-suppress
++void udp_print(netdissect_options *ndo, const u_char *bp, u_int length, const u_char *bp2, int fragmented, u_int ttl_hl)
+ {
+ const struct udphdr *up;
+- const struct ip *ip;
++ const struct tcpdump_ip *ip;
+ const u_char *cp;
+ const u_char *ep = ndo->ndo_snapend;
+ uint16_t sport, dport;
+ u_int ulen;
+- const struct ip6_hdr *ip6;
++ const struct tcpdump_ip6_hdr *ip6;
+
+ ndo->ndo_protocol = "udp";
+ up = (const struct udphdr *)bp;
+- ip = (const struct ip *)bp2;
++ ip = (const struct tcpdump_ip *)bp2;
+ if (IP_V(ip) == 6)
+- ip6 = (const struct ip6_hdr *)bp2;
++ ip6 = (const struct tcpdump_ip6_hdr *)bp2;
+ else
+ ip6 = NULL;
+- if (!ND_TTEST_2(up->uh_dport)) {
++ if (!ND_TTEST_2(up->uh_dport))
++ {
+ udpipaddr_print(ndo, ip, -1, -1);
+ goto trunc;
+ }
+@@ -391,12 +431,14 @@
+ sport = GET_BE_U_2(up->uh_sport);
+ dport = GET_BE_U_2(up->uh_dport);
+
+- if (length < sizeof(struct udphdr)) {
++ if (length < sizeof(struct udphdr))
++ {
+ udpipaddr_print(ndo, ip, sport, dport);
+ ND_PRINT("truncated-udp %u", length);
+ return;
+ }
+- if (!ND_TTEST_2(up->uh_ulen)) {
++ if (!ND_TTEST_2(up->uh_ulen))
++ {
+ udpipaddr_print(ndo, ip, sport, dport);
+ goto trunc;
+ }
+@@ -408,7 +450,8 @@
+ */
+ if (ulen == 0 && length > 65535)
+ ulen = length;
+- if (ulen < sizeof(struct udphdr)) {
++ if (ulen < sizeof(struct udphdr))
++ {
+ udpipaddr_print(ndo, ip, sport, dport);
+ ND_PRINT("truncated-udplength %u", ulen);
+ return;
+@@ -419,16 +462,19 @@
+ length = ulen;
+
+ cp = (const u_char *)(up + 1);
+- if (cp > ndo->ndo_snapend) {
++ if (cp > ndo->ndo_snapend)
++ {
+ udpipaddr_print(ndo, ip, sport, dport);
+ goto trunc;
+ }
+
+- if (ndo->ndo_packettype) {
++ if (ndo->ndo_packettype)
++ {
+ const struct sunrpc_msg *rp;
+ enum sunrpc_msg_type direction;
+
+- switch (ndo->ndo_packettype) {
++ switch (ndo->ndo_packettype)
++ {
+
+ case PT_VAT:
+ udpipaddr_print(ndo, ip, sport, dport);
+@@ -442,13 +488,13 @@
+
+ case PT_RPC:
+ rp = (const struct sunrpc_msg *)cp;
+- direction = (enum sunrpc_msg_type) GET_BE_U_4(rp->rm_direction);
++ direction = (enum sunrpc_msg_type)GET_BE_U_4(rp->rm_direction);
+ if (direction == SUNRPC_CALL)
+ sunrpc_print(ndo, (const u_char *)rp, length,
+- (const u_char *)ip);
++ (const u_char *)ip);
+ else
+ nfsreply_print(ndo, (const u_char *)rp, length,
+- (const u_char *)ip); /*XXX*/
++ (const u_char *)ip); /*XXX*/
+ break;
+
+ case PT_RTP:
+@@ -480,7 +526,7 @@
+ case PT_AODV:
+ udpipaddr_print(ndo, ip, sport, dport);
+ aodv_print(ndo, cp, length,
+- ip6 != NULL);
++ ip6 != NULL);
+ break;
+
+ case PT_RADIUS:
+@@ -520,29 +566,34 @@
+ }
+
+ udpipaddr_print(ndo, ip, sport, dport);
+- if (!ndo->ndo_qflag) {
++ if (!ndo->ndo_qflag)
++ {
+ const struct sunrpc_msg *rp;
+ enum sunrpc_msg_type direction;
+
+ rp = (const struct sunrpc_msg *)cp;
+- if (ND_TTEST_4(rp->rm_direction)) {
+- direction = (enum sunrpc_msg_type) GET_BE_U_4(rp->rm_direction);
+- if (dport == NFS_PORT && direction == SUNRPC_CALL) {
++ if (ND_TTEST_4(rp->rm_direction))
++ {
++ direction = (enum sunrpc_msg_type)GET_BE_U_4(rp->rm_direction);
++ if (dport == NFS_PORT && direction == SUNRPC_CALL)
++ {
+ ND_PRINT("NFS request xid %u ",
+- GET_BE_U_4(rp->rm_xid));
++ GET_BE_U_4(rp->rm_xid));
+ nfsreq_noaddr_print(ndo, (const u_char *)rp, length,
+- (const u_char *)ip);
++ (const u_char *)ip);
+ return;
+ }
+- if (sport == NFS_PORT && direction == SUNRPC_REPLY) {
++ if (sport == NFS_PORT && direction == SUNRPC_REPLY)
++ {
+ ND_PRINT("NFS reply xid %u ",
+- GET_BE_U_4(rp->rm_xid));
++ GET_BE_U_4(rp->rm_xid));
+ nfsreply_noaddr_print(ndo, (const u_char *)rp, length,
+- (const u_char *)ip);
++ (const u_char *)ip);
+ return;
+ }
+ #ifdef notdef
+- if (dport == SUNRPC_PORT && direction == SUNRPC_CALL) {
++ if (dport == 111 /*TCPDUMP_SUNRPC_PORT*/ && direction == SUNRPC_CALL)
++ {
+ sunrpc_print((const u_char *)rp, length, (const u_char *)ip);
+ return;
+ }
+@@ -550,7 +601,8 @@
+ }
+ }
+
+- if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented) {
++ if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented)
++ {
+ /* Check the checksum, if possible. */
+ uint16_t sum, udp_sum;
+
+@@ -558,181 +610,353 @@
+ * XXX - do this even if vflag == 1?
+ * TCP does, and we do so for UDP-over-IPv6.
+ */
+- if (IP_V(ip) == 4 && (ndo->ndo_vflag > 1)) {
++ if (IP_V(ip) == 4 && (ndo->ndo_vflag > 1))
++ {
+ udp_sum = GET_BE_U_2(up->uh_sum);
+- if (udp_sum == 0) {
++ if (udp_sum == 0)
++ {
+ ND_PRINT("[no cksum] ");
+- } else if (ND_TTEST_LEN(cp, length)) {
++ }
++ else if (ND_TTEST_LEN(cp, length))
++ {
+ sum = udp_cksum(ndo, ip, up, length + sizeof(struct udphdr));
+
+- if (sum != 0) {
++ if (sum != 0)
++ {
+ ND_PRINT("[bad udp cksum 0x%04x -> 0x%04x!] ",
+- udp_sum,
+- in_cksum_shouldbe(udp_sum, sum));
+- } else
++ udp_sum,
++ in_cksum_shouldbe(udp_sum, sum));
++ }
++ else
+ ND_PRINT("[udp sum ok] ");
+ }
+ }
+- else if (IP_V(ip) == 6) {
++ else if (IP_V(ip) == 6)
++ {
+ /* for IPv6, UDP checksum is mandatory */
+- if (ND_TTEST_LEN(cp, length)) {
++ if (ND_TTEST_LEN(cp, length))
++ {
+ sum = udp6_cksum(ndo, ip6, up, length + sizeof(struct udphdr));
+ udp_sum = GET_BE_U_2(up->uh_sum);
+
+- if (sum != 0) {
++ if (sum != 0)
++ {
+ ND_PRINT("[bad udp cksum 0x%04x -> 0x%04x!] ",
+- udp_sum,
+- in_cksum_shouldbe(udp_sum, sum));
+- } else
++ udp_sum,
++ in_cksum_shouldbe(udp_sum, sum));
++ }
++ else
+ ND_PRINT("[udp sum ok] ");
+ }
+ }
+ }
+
+- if (!ndo->ndo_qflag) {
+- if (IS_SRC_OR_DST_PORT(NAMESERVER_PORT))
++ if (!ndo->ndo_qflag)
++ {
++ if (CHECK_IS_SRC_OR_DST_PORT(53 /*TCPDUMP_NAMESERVER_PORT*/, sport, dport))
++ {
+ /* over_tcp: FALSE, is_mdns: FALSE */
+ domain_print(ndo, cp, length, FALSE, FALSE);
+- else if (IS_SRC_OR_DST_PORT(MULTICASTDNS_PORT))
++ return;
++ }
++
++ if (CHECK_IS_SRC_OR_DST_PORT(5353 /*TCPDUMP_MULTICASTDNS_PORT*/, sport, dport))
++ {
+ /* over_tcp: FALSE, is_mdns: TRUE */
+ domain_print(ndo, cp, length, FALSE, TRUE);
+- else if (IS_SRC_OR_DST_PORT(TIMED_PORT))
++ return;
++ }
++
++ if (CHECK_IS_SRC_OR_DST_PORT(525 /*TCPDUMP_TIMED_PORT*/, sport, dport))
++ {
+ timed_print(ndo, (const u_char *)cp);
+- else if (IS_SRC_OR_DST_PORT(TFTP_PORT))
++ return;
++ }
++ if (CHECK_IS_SRC_OR_DST_PORT(69 /*TCPDUMP_TFTP_PORT*/, sport, dport))
++ {
+ tftp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(BOOTPC_PORT) || IS_SRC_OR_DST_PORT(BOOTPS_PORT))
++ return;
++ }
++ if (CHECK_IS_SRC_OR_DST_PORT(68 /*TCPDUMP_BOOTPC_PORT*/, sport, dport) || CHECK_IS_SRC_OR_DST_PORT(67 /*TCPDUMP_BOOTPS_PORT*/, sport, dport))
++ {
+ bootp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(RIP_PORT))
++ return;
++ }
++ if (CHECK_IS_SRC_OR_DST_PORT(520 /*TCPDUMP_RIP_PORT*/, sport, dport))
++ {
+ rip_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(AODV_PORT))
+- aodv_print(ndo, cp, length,
+- ip6 != NULL);
+- else if (IS_SRC_OR_DST_PORT(ISAKMP_PORT))
+- isakmp_print(ndo, cp, length, bp2);
+- else if (IS_SRC_OR_DST_PORT(ISAKMP_PORT_NATT))
+- isakmp_rfc3948_print(ndo, cp, length, bp2, IP_V(ip), fragmented, ttl_hl);
+- else if (IS_SRC_OR_DST_PORT(ISAKMP_PORT_USER1) || IS_SRC_OR_DST_PORT(ISAKMP_PORT_USER2))
++ return;
++ }
++ if (CHECK_IS_SRC_OR_DST_PORT(654 /*AODV_PORT*/, sport, dport))
++ {
++ aodv_print(ndo, cp, length, ip6 != NULL);
++ return;
++ }
++
++ if (CHECK_IS_SRC_OR_DST_PORT(500 /*TCPDUMP_ISAKMP_PORT*/, sport, dport))
++ {
++ isakmp_print(ndo, cp, length, bp2);
++ return;
++ }
++ if (CHECK_IS_SRC_OR_DST_PORT(4500 /*TCPDUMP_ISAKMP_PORT_NATT*/, sport, dport))
++ {
++ isakmp_rfc3948_print(ndo, cp, length, bp2, IP_V(ip), fragmented, ttl_hl);
++ return;
++ }
++ if (CHECK_IS_SRC_OR_DST_PORT(7500 /*TCPDUMP_ISAKMP_PORT_USER1*/, sport, dport) || CHECK_IS_SRC_OR_DST_PORT(8500 /*TCPDUMP_ISAKMP_PORT_USER2*/, sport, dport))
++ {
+ isakmp_print(ndo, cp, length, bp2);
+- else if (IS_SRC_OR_DST_PORT(SNMP_PORT) || IS_SRC_OR_DST_PORT(SNMPTRAP_PORT))
++ return;
++ }
++ if (CHECK_IS_SRC_OR_DST_PORT(161 /*TCPDUMP_SNMP_PORT*/, sport, dport) || CHECK_IS_SRC_OR_DST_PORT(162 /*TCPDUMP_SNMPTRAP_PORT*/, sport, dport))
++ {
+ snmp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(NTP_PORT))
++ return;
++ }
++ if (CHECK_IS_SRC_OR_DST_PORT(123 /*TCPDUMP_NTP_PORT*/, sport, dport))
++ {
+ ntp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(KERBEROS_PORT) || IS_SRC_OR_DST_PORT(KERBEROS_SEC_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(88 /*TCPDUMP_KERBEROS_PORT*/) || CHECK_IS_SRC_OR_DST_PORT(750 /*TCPDUMP_KERBEROS_SEC_PORT*/, sport, dport))
++ {
+ krb_print(ndo, (const u_char *)cp);
+- else if (IS_SRC_OR_DST_PORT(L2TP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(1701 /*TCPDUMP_L2TP_PORT*/))
++ {
+ l2tp_print(ndo, cp, length);
++ return;
++ }
+ #ifdef ENABLE_SMB
+- else if (IS_SRC_OR_DST_PORT(NETBIOS_NS_PORT))
++ if (IS_SRC_OR_DST_UDPPORT(137 /*TCPDUMP_NETBIOS_NS_PORT*/))
++ {
+ nbt_udp137_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(NETBIOS_DGRAM_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(138 /*TCPDUMP_NETBIOS_DGRAM_PORT*/))
++ {
+ nbt_udp138_print(ndo, cp, length);
++ return;
++ }
+ #endif
+- else if (dport == VAT_PORT)
++
++ if (dport == 3456 /*TCPDUMP_VAT_PORT*/)
++ {
+ vat_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(ZEPHYR_SRV_PORT) || IS_SRC_OR_DST_PORT(ZEPHYR_CLT_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(2103 /*TCPDUMP_ZEPHYR_SRV_PORT*/) || IS_SRC_OR_DST_UDPPORT(2104 /*ZEPHYR_CLT_PORT*/))
++ {
+ zephyr_print(ndo, cp, length);
++ return;
++ }
+ /*
+ * Since there are 10 possible ports to check, I think
+ * a <> test would be more efficient
+ */
+- else if ((sport >= RX_PORT_LOW && sport <= RX_PORT_HIGH) ||
+- (dport >= RX_PORT_LOW && dport <= RX_PORT_HIGH))
+- rx_print(ndo, cp, length, sport, dport,
+- (const u_char *) ip);
+- else if (IS_SRC_OR_DST_PORT(RIPNG_PORT))
++ if ((sport >= 7000 /*TCPDUMP_RX_PORT_LOW*/ && sport <= 7009 /*TCPDUMP_RX_PORT_HIGH*/) ||
++ (dport >= 7000 /*TCPDUMP_RX_PORT_LOW*/ && dport <= 7009 /*TCPDUMP_RX_PORT_HIGH*/))
++ {
++ rx_print(ndo, cp, length, sport, dport, (const u_char *)ip);
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(521 /*TCPDUMP_RIPNG_PORT*/))
++ {
+ ripng_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(DHCP6_SERV_PORT) || IS_SRC_OR_DST_PORT(DHCP6_CLI_PORT))
++ return;
++ }
++
++ if (IS_SRC_OR_DST_UDPPORT(546 /*TCPDUMP_DHCP6_SERV_PORT*/) || IS_SRC_OR_DST_UDPPORT(547 /*TCPDUMP_DHCP6_CLI_PORT*/))
++ {
+ dhcp6_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(AHCP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(5359 /*TCPDUMP_AHCP_PORT*/))
++ {
+ ahcp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(BABEL_PORT) || IS_SRC_OR_DST_PORT(BABEL_PORT_OLD))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(6696 /*TCPDUMP_BABEL_PORT*/) || IS_SRC_OR_DST_UDPPORT(6697 /*TCPDUMP_BABEL_PORT_OLD*/))
++ {
+ babel_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(HNCP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(8231 /*TCPDUMP_HNCP_PORT*/))
++ {
+ hncp_print(ndo, cp, length);
++ return;
++ }
+ /*
+ * Kludge in test for whiteboard packets.
+ */
+- else if (dport == WB_PORT)
++ if (dport == 4567 /*TCPDUMP_WB_PORT*/)
++ {
+ wb_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(CISCO_AUTORP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(496 /*TCPDUMP_CISCO_AUTORP_PORT*/))
++ {
+ cisco_autorp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(RADIUS_PORT) ||
+- IS_SRC_OR_DST_PORT(RADIUS_NEW_PORT) ||
+- IS_SRC_OR_DST_PORT(RADIUS_ACCOUNTING_PORT) ||
+- IS_SRC_OR_DST_PORT(RADIUS_NEW_ACCOUNTING_PORT) ||
+- IS_SRC_OR_DST_PORT(RADIUS_CISCO_COA_PORT) ||
+- IS_SRC_OR_DST_PORT(RADIUS_COA_PORT) )
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(1645 /*TCPDUMP_RADIUS_PORT*/) ||
++ IS_SRC_OR_DST_UDPPORT(1812 /*TCPDUMP_RADIUS_NEW_PORT*/) ||
++ IS_SRC_OR_DST_UDPPORT(1646 /*TCPDUMP_RADIUS_ACCOUNTING_PORT*/) ||
++ IS_SRC_OR_DST_UDPPORT(1813 /*TCPDUMP_RADIUS_NEW_ACCOUNTING_PORT*/) ||
++ IS_SRC_OR_DST_UDPPORT(1700 /*TCPDUMP_RADIUS_CISCO_COA_PORT*/) ||
++ IS_SRC_OR_DST_UDPPORT(3799 /*TCPDUMP_RADIUS_COA_PORT*/))
++ {
+ radius_print(ndo, cp, length);
+- else if (dport == HSRP_PORT)
++ return;
++ }
++ if (dport == 1985 /*TCPDUMP_HSRP_PORT*/)
++ {
+ hsrp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(LWRES_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(921 /*TCPDUMP_LWRES_PORT*/))
++ {
+ lwres_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(LDP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(646 /*TCPDUMP_LDP_PORT*/))
++ {
+ ldp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(OLSR_PORT))
+- olsr_print(ndo, cp, length,
+- (IP_V(ip) == 6) ? 1 : 0);
+- else if (IS_SRC_OR_DST_PORT(MPLS_LSP_PING_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(698 /*TCPDUMP_OLSR_PORT*/))
++ {
++ olsr_print(ndo, cp, length, (IP_V(ip) == 6) ? 1 : 0);
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(3503 /*TCPDUMP_MPLS_LSP_PING_PORT*/))
++ {
+ lspping_print(ndo, cp, length);
+- else if (sport == BCM_LI_PORT)
++ return;
++ }
++ if (sport == 49152 /*TCPDUMP_BCM_LI_PORT*/)
++ {
+ bcm_li_print(ndo, cp, length);
+- else if (dport == BFD_CONTROL_PORT ||
+- dport == BFD_MULTIHOP_PORT ||
+- dport == BFD_LAG_PORT ||
+- dport == BFD_ECHO_PORT )
++ return;
++ }
++
++ if (dport == 3784 /*TCPDUMP_BFD_CONTROL_PORT*/ ||
++ dport == 4784 /*TCPDUMP_BFD_MULTIHOP_PORT*/ ||
++ dport == 6784 /*TCPDUMP_BFD_LAG_PORT*/ ||
++ dport == 3785 /*TCPDUMP_BFD_ECHO_PORT*/)
++ {
+ bfd_print(ndo, cp, length, dport);
+- else if (IS_SRC_OR_DST_PORT(LMP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(701 /*TCPDUMP_LMP_PORT*/))
++ {
+ lmp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(VQP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(1589 /*TCPDUMP_VQP_PORT*/))
++ {
+ vqp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(SFLOW_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(6343 /*TCPDUMP_SFLOW_PORT*/))
++ {
+ sflow_print(ndo, cp, length);
+- else if (dport == LWAPP_CONTROL_PORT)
++ return;
++ }
++ if (dport == 12223 /*TCPDUMP_LWAPP_CONTROL_PORT*/)
++ {
+ lwapp_control_print(ndo, cp, length, 1);
+- else if (sport == LWAPP_CONTROL_PORT)
++ return;
++ }
++ if (sport == 12223 /*TCPDUMP_LWAPP_CONTROL_PORT*/)
++ {
+ lwapp_control_print(ndo, cp, length, 0);
+- else if (IS_SRC_OR_DST_PORT(LWAPP_DATA_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(12222 /*TCPDUMP_LWAPP_DATA_PORT*/))
++ {
+ lwapp_data_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(SIP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(5060 /*TCPDUMP_SIP_PORT*/))
++ {
+ sip_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(SYSLOG_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(514 /*TCPDUMP_SYSLOG_PORT*/))
++ {
+ syslog_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(OTV_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(8472 /*TCPDUMP_OTV_PORT*/))
++ {
+ otv_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(VXLAN_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(4789 /*TCPDUMP_VXLAN_PORT*/))
++ {
+ vxlan_print(ndo, cp, length);
+- else if (dport == GENEVE_PORT)
++ return;
++ }
++ if (dport == 6081 /*TCPDUMP_GENEVE_PORT*/)
++ {
+ geneve_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(LISP_CONTROL_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(4342 /*TCPDUMP_LISP_CONTROL_PORT*/))
++ {
+ lisp_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(VXLAN_GPE_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(4790 /*TCPDUMP_VXLAN_GPE_PORT*/))
++ {
+ vxlan_gpe_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(ZEP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(17754 /*TCPDUMP_ZEP_PORT*/))
++ {
+ zep_print(ndo, cp, length);
+- else if (IS_SRC_OR_DST_PORT(MPLS_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(6635 /*TCPDUMP_MPLS_PORT*/))
++ {
+ mpls_print(ndo, cp, length);
+- else if (ND_TTEST_1(((const struct LAP *)cp)->type) &&
+- GET_U_1(((const struct LAP *)cp)->type) == lapDDP &&
+- (atalk_port(sport) || atalk_port(dport))) {
++ return;
++ }
++ if (ND_TTEST_1(((const struct LAP *)cp)->type) &&
++ GET_U_1(((const struct LAP *)cp)->type) == lapDDP &&
++ (atalk_port(sport) || atalk_port(dport)))
++ {
+ if (ndo->ndo_vflag)
+ ND_PRINT("kip ");
+ llap_print(ndo, cp, length);
+- } else if (IS_SRC_OR_DST_PORT(PTP_EVENT_PORT) ||
+- IS_SRC_OR_DST_PORT(PTP_GENERAL_PORT)) {
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(319 /*TCPDUMP_PTP_EVENT_PORT*/) ||
++ IS_SRC_OR_DST_UDPPORT(320 /*TCPDUMP_PTP_GENERAL_PORT*/))
++
++ {
+ ptp_print(ndo, cp, length);
+- } else if (IS_SRC_OR_DST_PORT(SOMEIP_PORT))
++ return;
++ }
++ if (IS_SRC_OR_DST_UDPPORT(30490 /*TCPDUMP_SOMEIP_PORT*/))
+ someip_print(ndo, cp, length);
+- else {
++ else
++ {
+ if (ulen > length && !fragmented)
+ ND_PRINT("UDP, bad length %u > %u",
+- ulen, length);
++ ulen, length);
+ else
+ ND_PRINT("UDP, length %u", ulen);
+ }
+- } else {
++ }
++
++ else
++ {
+ if (ulen > length && !fragmented)
+ ND_PRINT("UDP, bad length %u > %u",
+- ulen, length);
++ ulen, length);
+ else
+ ND_PRINT("UDP, length %u", ulen);
+ }
+diff -uNr tcpdump-tcpdump-4.99.4/print-udp.h tcpdump-for-stellar/print-udp.h
+--- tcpdump-tcpdump-4.99.4/print-udp.h 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/print-udp.h 2024-10-30 17:34:06.265498697 +0800
+@@ -0,0 +1,184 @@
++/*
++ * Copyright (c) 1982, 1986, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)udp.h 8.1 (Berkeley) 6/10/93
++ */
++
++#pragma once
++#include "netdissect.h"
++/*
++ * Udp protocol header.
++ * Per RFC 768, September, 1981.
++ */
++struct udphdr
++{
++ nd_uint16_t uh_sport; /* source port */
++ nd_uint16_t uh_dport; /* destination port */
++ nd_uint16_t uh_ulen; /* udp length */
++ nd_uint16_t uh_sum; /* udp checksum */
++};
++
++// #define TCPDUMP_NAMESERVER_PORT 53
++
++// #define TCPDUMP_BOOTPS_PORT 67 /* RFC951 */
++
++// #define TCPDUMP_BOOTPC_PORT 68 /* RFC951 */
++
++// #define TCPDUMP_TFTP_PORT 69 /*XXX*/
++
++// #define TCPDUMP_KERBEROS_PORT 88 /*XXX*/
++
++// #define TCPDUMP_SUNRPC_PORT 111 /*XXX*/
++
++// #define TCPDUMP_NTP_PORT 123 /*XXX*/
++
++// #define TCPDUMP_NETBIOS_NS_PORT 137 /* RFC 1001, RFC 1002 */
++
++// #define TCPDUMP_NETBIOS_DGRAM_PORT 138 /* RFC 1001, RFC 1002 */
++
++// #define TCPDUMP_SNMP_PORT 161 /*XXX*/
++
++// #define TCPDUMP_SNMPTRAP_PORT 162 /*XXX*/
++
++// #define TCPDUMP_PTP_EVENT_PORT 319 /* IANA */
++
++// #define TCPDUMP_PTP_GENERAL_PORT 320 /* IANA */
++
++// #define TCPDUMP_CISCO_AUTORP_PORT 496 /*XXX*/
++
++// #define TCPDUMP_ISAKMP_PORT 500 /*XXX*/
++
++// #define TCPDUMP_SYSLOG_PORT 514 /* rfc3164 */
++
++// #define TCPDUMP_RIP_PORT 520 /*XXX*/
++
++// #define TCPDUMP_RIPNG_PORT 521 /* RFC 2080 */
++
++// #define TCPDUMP_TIMED_PORT 525 /*XXX*/
++
++// #define TCPDUMP_DHCP6_SERV_PORT 546 /*XXX*/
++
++// #define TCPDUMP_DHCP6_CLI_PORT 547 /*XXX*/
++
++// #define TCPDUMP_LDP_PORT 646
++
++// #define TCPDUMP_AQDV_PORT 654 /*XXX*/
++
++// #define TCPDUMP_OLSR_PORT 698 /* rfc3626 */
++
++// #define TCPDUMP_LMP_PORT 701 /* rfc4204 */
++
++// #define TCPDUMP_KERBEROS_SEC_PORT 750 /*XXX - Kerberos v4 */
++
++// #define TCPDUMP_LWRES_PORT 921 /*XXX*/
++
++// #define TCPDUMP_VQP_PORT 1589 /*XXX*/
++
++// #define TCPDUMP_RADIUS_PORT 1645 /*XXX*/
++
++// #define TCPDUMP_RADIUS_ACCOUNTING_PORT 1646
++
++// #define TCPDUMP_RADIUS_CISCO_COA_PORT 1700
++
++// #define TCPDUMP_L2TP_PORT 1701 /*XXX*/
++
++// #define TCPDUMP_RADIUS_NEW_PORT 1812 /*XXX*/
++
++// #define TCPDUMP_RADIUS_NEW_ACCOUNTING_PORT 1813
++
++// #define TCPDUMP_HSRP_PORT 1985 /*XXX*/
++
++// #define TCPDUMP_ZEPHYR_SRV_PORT 2103 /*XXX*/
++
++// #define TCPDUMP_ZEPHYR_CLI_PORT 2104 /*XXX*/
++
++// #define TCPDUMP_VAT_PORT 3456 /*XXX*/
++
++// #define TCPDUMP_MPLS_LSP_PING_PORT 3503 /* draft-ietf-mpls-lsp-ping-02.txt */
++
++// #define TCPDUMP_BCM_LI_PORT 49152 /* SDK default */
++
++#define TCPDUMP_BFD_CONTROL_PORT 3784 /* RFC 5881 */
++
++#define TCPDUMP_BFD_ECHO_PORT 3785 /* RFC 5881 */
++
++// #define TCPDUMP_RADIUS_COA_PORT 3799 /* RFC 5176 */
++
++// #define TCPDUMP_LISP_CONTROL_PORT 4342 /* RFC 6830 */
++
++// #define TCPDUMP_ISAKMP_PORT_NATT 4500 /* rfc3948 */
++
++// #define TCPDUMP_WB_PORT 4567
++
++#define TCPDUMP_BFD_MULTIHOP_PORT 4784 /* RFC 5883 */
++
++// #define TCPDUMP_VXLAN_PORT 4789 /* RFC 7348 */
++
++// #define TCPDUMP_VXLAN_GPE_PORT 4790 /* draft-ietf-nvo3-vxlan-gpe-01 */
++
++// #define TCPDUMP_SIP_PORT 5060
++
++// #define TCPDUMP_MULTICASTDNS_PORT 5353 /* RFC 6762 */
++
++// #define TCPDUMP_AHCP_PORT 5359 /* draft-chroboczek-ahcp-00 */
++
++// #define TCPDUMP_GENEVE_PORT 6081 /* draft-gross-geneve-02 */
++
++// #define TCPDUMP_SFLOW_PORT 6343 /* https://sflow.org/developers/specifications.php */
++
++// #define TCPDUMP_MPLS_PORT 6635 /* RFC 7510 */
++
++// #define TCPDUMP_BABEL_PORT 6696 /* RFC 6126 errata */
++
++// #define TCPDUMP_BABEL_PORT_OLD 6697 /* RFC 6126 */
++
++#define TCPDUMP_BFD_LAG_PORT 6784 /* RFC 7310 */
++
++// #define TCPDUMP_RX_PORT_LOW 7000 /*XXX*/
++
++// #define TCPDUMP_RX_PORT_HIGH 7009 /*XXX*/
++
++// #define TCPDUMP_ISAKMP_PORT_USER1 7500 /*XXX - nonstandard*/
++
++// #define TCPDUMP_HNCP_PORT 8231 /* RFC 7788 */
++
++// #define TCPDUMP_OTV_PORT 8472 /* draft-hasmit-otv-04 */
++
++// #define TCPDUMP_ISAKMP_PORT_USER2 8500 /*XXX - nonstandard*/
++
++// #define TCPDUMP_LWAPP_DATA_PORT 12222 /* RFC 5412 */
++
++// #define TCPDUMP_LWAPP_CONTROL_PORT 12223 /* RFC 5412 */
++
++// #define TCPDUMP_ZEP_PORT 17754 /* XXX */
++
++// #define TCPDUMP_SOMEIP_PORT 30490 /* https://www.autosar.org/standards/foundation */
+diff -uNr tcpdump-tcpdump-4.99.4/print-vrrp.c tcpdump-for-stellar/print-vrrp.c
+--- tcpdump-tcpdump-4.99.4/print-vrrp.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/print-vrrp.c 2024-10-30 17:34:05.631498702 +0800
+@@ -35,7 +35,7 @@
+ #include "extract.h"
+ #include "addrtoname.h"
+
+-#include "ip.h"
++#include "tcpdump_ip.h"
+ #include "ipproto.h"
+ /*
+ * RFC 2338 (VRRP v2):
+@@ -151,10 +151,10 @@
+ uint16_t cksum;
+
+ if (ver == 4)
+- cksum = nextproto4_cksum(ndo, (const struct ip *)bp2, bp,
++ cksum = nextproto4_cksum(ndo, (const struct tcpdump_ip *)bp2, bp,
+ len, len, IPPROTO_VRRP);
+ else
+- cksum = nextproto6_cksum(ndo, (const struct ip6_hdr *)bp2, bp,
++ cksum = nextproto6_cksum(ndo, (const struct tcpdump_ip6_hdr *)bp2, bp,
+ len, len, IPPROTO_VRRP);
+ if (cksum)
+ ND_PRINT(", (bad vrrp cksum %x)",
+diff -uNr tcpdump-tcpdump-4.99.4/stellar_dump_ctrl_link.c tcpdump-for-stellar/stellar_dump_ctrl_link.c
+--- tcpdump-tcpdump-4.99.4/stellar_dump_ctrl_link.c 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/stellar_dump_ctrl_link.c 2024-10-30 17:34:06.265498697 +0800
+@@ -0,0 +1,266 @@
++#include "stellar_dump.h"
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <pthread.h>
++#include <pcap/pcap.h>
++#include <sys/types.h> /* See NOTES */
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <event2/event.h>
++#include <event2/listener.h>
++#include <event2/bufferevent.h>
++#include <event2/buffer.h>
++#include <event2/thread.h>
++#include <event2/http.h>
++
++struct libevent_http_client
++{
++ struct event_base *base;
++ struct evhttp_connection *conn;
++ struct evhttp_request *req;
++ uint32_t response_code;
++ char *response_cstr;
++};
++
++static struct libevent_http_client *g_ctrllink_http_client = NULL;
++static pthread_t keepalive_thread_id;
++static volatile int keepalive_running = 0;
++
++static struct libevent_http_client *evhttp_client_new(const char *server_ip, unsigned short server_port_host)
++{
++ struct libevent_http_client *evh_client = (struct libevent_http_client *)calloc(1, sizeof(struct libevent_http_client));
++ evh_client->base = event_base_new();
++ evh_client->conn = evhttp_connection_base_new(evh_client->base, NULL, server_ip, server_port_host);
++ evhttp_connection_set_timeout(evh_client->conn, CTRLLINK_CONNECT_TIMEOUT);
++ return evh_client;
++}
++
++static void evhttp_response_cb(struct evhttp_request *req, void *arg)
++{
++ struct libevent_http_client *evh_client = (struct libevent_http_client *)arg;
++ if (req == NULL)
++ {
++ return;
++ }
++
++ struct evbuffer *input_buffer = evhttp_request_get_input_buffer(req);
++ size_t evbuf_len = evbuffer_get_length(input_buffer);
++ if (NULL == input_buffer || 0 == evbuf_len)
++ {
++ return;
++ }
++ evh_client->response_cstr = (char *)calloc(1, evbuf_len + 1);
++ evbuffer_remove(input_buffer, evh_client->response_cstr, evbuf_len);
++ evh_client->response_code = evhttp_request_get_response_code(req);
++ // terminate event_base_dispatch()
++ event_base_loopbreak(evh_client->base);
++}
++
++static void evhttp_request_error_cb(enum evhttp_request_error errnum, void *arg)
++{
++ (void)arg;
++ (void)errnum;
++ // fprintf(stderr, "ERR monitor connection error: %d\n", errnum);
++ exit(1);
++}
++
++static void evhttp_conn_close_cb(struct evhttp_connection *conn, void *arg)
++{
++ (void)conn;
++ event_base_loopexit((struct event_base *)arg, NULL);
++ static volatile int barrier = 0;
++ if (__sync_bool_compare_and_swap(&barrier, 0, 1))
++ {
++ fprintf(stderr, "monitor connection closed.\n");
++ exit(1);
++ }
++}
++
++static int evhttp_client_request_new(struct libevent_http_client *evh_client)
++{
++ evh_client->req = evhttp_request_new(evhttp_response_cb, evh_client);
++ evhttp_request_set_error_cb(evh_client->req, evhttp_request_error_cb);
++ evhttp_connection_set_closecb(evh_client->conn, evhttp_conn_close_cb, evh_client->base);
++ evh_client->response_cstr = NULL;
++ evh_client->response_code = 204;
++ return 0;
++}
++static void evhttp_client_add_header(struct libevent_http_client *evh_client,
++ const char *key, const char *value)
++{
++ struct evkeyvalq *output_headers = evhttp_request_get_output_headers(evh_client->req);
++ evhttp_add_header(output_headers, key, value);
++}
++
++static unsigned int evhttp_run_cmd(struct libevent_http_client *evh_client, const char *cmd_uri)
++{
++ evhttp_client_request_new(evh_client);
++ evhttp_client_add_header(evh_client, "Connection", "keep-alive");
++ evhttp_client_add_header(evh_client, "Content-Length", "0");
++ evhttp_make_request(evh_client->conn, evh_client->req, EVHTTP_REQ_GET, cmd_uri);
++
++ int ret = event_base_dispatch(evh_client->base);
++ if (ret < 0)
++ {
++ fprintf(stderr, "event_base_dispatch failed!\n");
++ exit(1);
++ }
++ // here we receive the response from monitor server.
++ return evh_client->response_code;
++}
++
++static void *stellar_dump_ctrllink_keepalive(void *arg)
++{
++ (void)arg;
++ struct libevent_http_client *evh_client = g_ctrllink_http_client;
++ char keepalive_cmd[256] = {0};
++ snprintf(keepalive_cmd, sizeof(keepalive_cmd), "%s%s", CTRLLINK_CMD_URI_PREFIX, "ping%20stellar-dump-keepalive");
++ int cmd_ret;
++ while (keepalive_running)
++ {
++ cmd_ret = evhttp_run_cmd(evh_client, keepalive_cmd);
++ if (cmd_ret != 200)
++ {
++ fprintf(stderr, "ERR connection failed!\n");
++ exit(1);
++ }
++ if (evh_client->response_cstr)
++ {
++ free(evh_client->response_cstr);
++ evh_client->response_cstr = NULL;
++ }
++ if (keepalive_running)
++ {
++ sleep(1);
++ }
++ }
++ return NULL;
++}
++
++static char *http_url_encode(const char *originalText)
++{
++ // allocate memory for the worst possible case (all characters need to be encoded)
++ char *encodedText = (char *)malloc(sizeof(char) * strlen(originalText) * 3 + 1);
++ const char *hex = "0123456789abcdef";
++ int pos = 0;
++ for (size_t i = 0; i < strlen(originalText); i++)
++ {
++ if (('a' <= originalText[i] && originalText[i] <= 'z') || ('A' <= originalText[i] && originalText[i] <= 'Z') || ('0' <= originalText[i] && originalText[i] <= '9'))
++ {
++ encodedText[pos++] = originalText[i];
++ }
++ else if (originalText[i] == ' ')
++ {
++ encodedText[pos++] = '%';
++ encodedText[pos++] = hex[originalText[i] >> 4];
++ encodedText[pos++] = hex[originalText[i] & 15];
++ }
++ else
++ {
++ encodedText[pos++] = originalText[i];
++ }
++ }
++ encodedText[pos] = '\0';
++ return encodedText;
++}
++
++int stellar_dump_ctrllink_init(const struct stellar_dump_args *st_dump_arg)
++{
++ g_ctrllink_http_client = evhttp_client_new(st_dump_arg->stellar_server_ip, st_dump_arg->stellar_server_port_host_order);
++ char probe_cmd[256] = {};
++ snprintf(probe_cmd, sizeof(probe_cmd), "%s%s", CTRLLINK_CMD_URI_PREFIX, "ping stellar-dump-start");
++ char *encoded_url = http_url_encode(probe_cmd);
++ evhttp_run_cmd(g_ctrllink_http_client, encoded_url);
++ free(encoded_url);
++ if (g_ctrllink_http_client->response_code != 200)
++ {
++ fprintf(stderr, "Could not connect to stellar monitor server %s:%u\n", st_dump_arg->stellar_server_ip, st_dump_arg->stellar_server_port_host_order);
++ exit(1);
++ }
++ if (g_ctrllink_http_client->response_cstr)
++ {
++ free(g_ctrllink_http_client->response_cstr);
++ g_ctrllink_http_client->response_cstr = NULL;
++ }
++ return 0;
++}
++
++void stellar_dump_ctrllink_free(void)
++{
++ if (g_ctrllink_http_client)
++ {
++ evhttp_connection_free(g_ctrllink_http_client->conn);
++ event_base_loopbreak(g_ctrllink_http_client->base);
++ event_base_free(g_ctrllink_http_client->base);
++ if (g_ctrllink_http_client->response_cstr)
++ {
++ free(g_ctrllink_http_client->response_cstr);
++ g_ctrllink_http_client->response_cstr = NULL;
++ }
++ free(g_ctrllink_http_client);
++ g_ctrllink_http_client = NULL;
++ }
++}
++
++int stellar_dump_start(const struct stellar_dump_args *st_dump_arg)
++{
++ char dump_cmd[4096] = {};
++ strcat(dump_cmd, CTRLLINK_CMD_URI_PREFIX);
++ strcat(dump_cmd, "tcpdump ");
++
++ /* necessary arg */
++ sprintf(dump_cmd, "%s%s%s%u ", CTRLLINK_CMD_URI_PREFIX, "tcpdump ", "datalinkport ", st_dump_arg->datalink_recv_port_host_order);
++
++ /* optional args */
++ if (st_dump_arg->greedy_seek)
++ {
++ strcat(dump_cmd, "greedy ");
++ }
++ if (st_dump_arg->thread_enable_expression)
++ {
++ strcat(dump_cmd, "threads ");
++ strcat(dump_cmd, st_dump_arg->thread_enable_expression);
++ strcat(dump_cmd, " ");
++ }
++ if (st_dump_arg->bpf)
++ {
++ strcat(dump_cmd, "bpf ");
++ strcat(dump_cmd, st_dump_arg->bpf);
++ strcat(dump_cmd, " ");
++ }
++ char *encoded_url = http_url_encode(dump_cmd);
++ evhttp_run_cmd(g_ctrllink_http_client, encoded_url);
++ free(encoded_url);
++ if (g_ctrllink_http_client->response_code != 200)
++ {
++ fprintf(stderr, "Starting packet dump from server %s:%u : %s\r\n",
++ st_dump_arg->stellar_server_ip, st_dump_arg->stellar_server_port_host_order, g_ctrllink_http_client->response_cstr);
++ exit(1);
++ }
++ if (g_ctrllink_http_client->response_cstr)
++ {
++ free(g_ctrllink_http_client->response_cstr);
++ g_ctrllink_http_client->response_cstr = NULL;
++ }
++ return 0;
++}
++
++int stellar_dump_ctrllink_keepalive_start(const struct stellar_dump_args *st_dump_arg)
++{
++ keepalive_running = 1;
++ pthread_create(&keepalive_thread_id, NULL, stellar_dump_ctrllink_keepalive, (void *)st_dump_arg);
++ return 0;
++}
++
++void stellar_dump_ctrllink_keepalive_stop(void)
++{
++ keepalive_running = 0;
++ if (keepalive_thread_id != 0)
++ {
++ pthread_cancel(keepalive_thread_id);
++ pthread_join(keepalive_thread_id, NULL);
++ keepalive_thread_id = 0;
++ }
++}
+diff -uNr tcpdump-tcpdump-4.99.4/stellar_dump_data_link.c tcpdump-for-stellar/stellar_dump_data_link.c
+--- tcpdump-tcpdump-4.99.4/stellar_dump_data_link.c 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/stellar_dump_data_link.c 2024-10-30 17:34:06.265498697 +0800
+@@ -0,0 +1,69 @@
++#include "stellar_dump.h"
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <pcap/pcap.h>
++#include <sys/types.h> /* See NOTES */
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <errno.h>
++static int sockfd;
++
++int stellar_dump_datalink_init(struct stellar_dump_args *out_arg)
++{
++ uint16_t port;
++ struct sockaddr_in datalink_addr = {};
++ datalink_addr.sin_family = AF_INET;
++ datalink_addr.sin_addr.s_addr = htonl(INADDR_ANY);
++
++ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
++ if (sockfd < 0)
++ {
++ perror("socket");
++ return -1;
++ }
++ // int opt = 1;
++ // if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1)
++ // {
++ // perror("setsockopt SO_REUSEADDR");
++ // return -1;
++ // }
++
++ struct timeval tv = {};
++ tv.tv_sec = 1;
++ tv.tv_usec = 0;
++ setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
++
++ /* find an available port */
++ for (port = 60000; port < 65535; port++)
++ {
++ datalink_addr.sin_port = htons(port);
++ if (bind(sockfd, (struct sockaddr *)&datalink_addr, sizeof(datalink_addr)) != -1)
++ {
++ break;
++ }
++ }
++ if (port >= 65535)
++ {
++ fprintf(stderr, "no available data link port found\n");
++ return -1;
++ }
++ out_arg->datalink_recv_port_host_order = port;
++ return port;
++}
++
++ssize_t stellar_dump_datalink_recv(void *buf, size_t len, char **raw_pkt_start_ptr, struct pcap_pkthdr *phdr)
++{
++ ssize_t n;
++ n = recv(sockfd, buf, len, 0);
++ if (n < TZSP_HDR_LEN)
++ {
++ return -1;
++ }
++ gettimeofday(&phdr->ts, NULL);
++ phdr->caplen = n - TZSP_HDR_LEN;
++ phdr->len = n - TZSP_HDR_LEN;
++ *raw_pkt_start_ptr = (char *)buf + TZSP_HDR_LEN;
++ return n;
++}
+\ No newline at end of file
+diff -uNr tcpdump-tcpdump-4.99.4/stellar_dump.h tcpdump-for-stellar/stellar_dump.h
+--- tcpdump-tcpdump-4.99.4/stellar_dump.h 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/stellar_dump.h 2024-10-30 17:34:06.265498697 +0800
+@@ -0,0 +1,42 @@
++#pragma once
++#ifndef __FAVOR_BSD
++#define __FAVOR_BSD 1
++#endif
++#include <stdio.h>
++#include <stdint.h>
++#include <pcap/pcap.h>
++
++#define STELLAR_DUMP (1)
++#define PROGRAM_NAME "stellar_dump"
++
++#define CTRLLINK_DEFAULT_IP "127.0.0.1"
++#define CTRLLINK_DEFAULT_PORT 80
++
++#define TZSP_UDP_PORT 37008 /* default port of TZSP protocol: https://en.wikipedia.org/wiki/TZSP# */
++#define TZSP_HDR_LEN 5 /* length of TZSP header */
++
++#define CTRLLINK_CONNECT_TIMEOUT 3 /* seconds */
++
++#define DATALINK_RECV_BUF_SIZE 4096
++
++#define CTRLLINK_CMD_URI_PREFIX "/v1/stellar_monitor?raw_cmd="
++
++struct stellar_dump_args
++{
++ char *stellar_server_ip;
++ unsigned short stellar_server_port_host_order;
++ unsigned short datalink_recv_port_host_order;
++ int greedy_seek;
++ char *thread_enable_expression;
++ char *bpf;
++ int expect_packet_count; // arg -c
++};
++
++int stellar_dump_ctrllink_init(const struct stellar_dump_args *st_dump_arg);
++void stellar_dump_ctrllink_free(void);
++int stellar_dump_ctrllink_keepalive_start(const struct stellar_dump_args *st_dump_arg);
++void stellar_dump_ctrllink_keepalive_stop(void);
++
++int stellar_dump_datalink_init(struct stellar_dump_args *out_arg);
++int stellar_dump_start(const struct stellar_dump_args *st_dump_arg);
++ssize_t stellar_dump_datalink_recv(void *buf, size_t len, char **raw_pkt_start_ptr, struct pcap_pkthdr *phdr);
+\ No newline at end of file
+diff -uNr tcpdump-tcpdump-4.99.4/tcpdump.c tcpdump-for-stellar/tcpdump.c
+--- tcpdump-tcpdump-4.99.4/tcpdump.c 2023-04-08 02:30:58.000000000 +0800
++++ tcpdump-for-stellar/tcpdump.c 2024-10-30 17:34:05.655498702 +0800
+@@ -1,4 +1,4 @@
+-/*
++/* PATCH FOR STELLAR-DUMP
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+@@ -33,6 +33,24 @@
+ * combined efforts of Van, Steve McCanne and Craig Leres of LBL.
+ */
+
++#if defined(__clang__)
++#pragma clang diagnostic ignored "-Wunused-function"
++#pragma clang diagnostic ignored "-Wunused-parameter"
++#pragma clang diagnostic ignored "-Wunused-label"
++#pragma clang diagnostic ignored "-Wunused-macros"
++#pragma clang diagnostic ignored "-Wunused-variable"
++#pragma clang diagnostic ignored "-Wunused-parameter"
++#elif defined(__GNUC__)
++#pragma GCC diagnostic ignored "-Wunused-function"
++#pragma GCC diagnostic ignored "-Wunused-parameter"
++#pragma GCC diagnostic ignored "-Wunused-label"
++#pragma GCC diagnostic ignored "-Wunused-macros"
++#pragma GCC diagnostic ignored "-Wunused-variable"
++#pragma GCC diagnostic ignored "-Wunused-parameter"
++#endif
++
++#include "stellar_dump.h"
++
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+@@ -57,7 +75,7 @@
+ */
+ #ifndef lint
+ static const char copyright[] _U_ =
+- "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
++ "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
+ The Regents of the University of California. All rights reserved.\n";
+ #endif
+
+@@ -91,8 +109,8 @@
+ #include <libcasper.h>
+ #include <casper/cap_dns.h>
+ #include <sys/nv.h>
+-#endif /* HAVE_CASPER */
+-#endif /* HAVE_CAPSICUM */
++#endif /* HAVE_CASPER */
++#endif /* HAVE_CAPSICUM */
+ #ifdef HAVE_PCAP_OPEN
+ /*
+ * We found pcap_open() in the capture library, so we'll be using
+@@ -116,6 +134,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <limits.h>
++#include <ctype.h>
+ #ifdef _WIN32
+ #include <windows.h>
+ #else
+@@ -131,9 +150,9 @@
+ * Use this in pathnames, but do *not* use it in URLs.
+ */
+ #ifdef _WIN32
+-#define PATH_SEPARATOR '\\'
++#define PATH_SEPARATOR '\\'
+ #else
+-#define PATH_SEPARATOR '/'
++#define PATH_SEPARATOR '/'
+ #endif
+
+ /* capabilities convenience library */
+@@ -182,19 +201,19 @@
+ #endif
+
+ #if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
+-static int Bflag; /* buffer size */
++static int Bflag; /* buffer size */
+ #endif
+ #ifdef HAVE_PCAP_DUMP_FTELL64
+-static int64_t Cflag; /* rotate dump files after this many bytes */
++static int64_t Cflag; /* rotate dump files after this many bytes */
+ #else
+-static long Cflag; /* rotate dump files after this many bytes */
++static long Cflag; /* rotate dump files after this many bytes */
+ #endif
+-static int Cflag_count; /* Keep track of which file number we're writing */
++static int Cflag_count; /* Keep track of which file number we're writing */
+ #ifdef HAVE_PCAP_FINDALLDEVS
+-static int Dflag; /* list available devices and exit */
++static int Dflag; /* list available devices and exit */
+ #endif
+ #ifdef HAVE_PCAP_FINDALLDEVS_EX
+-static char *remote_interfaces_source; /* list available devices from this source and exit */
++static char *remote_interfaces_source; /* list available devices from this source and exit */
+ #endif
+
+ /*
+@@ -210,28 +229,28 @@
+ * *export* a routine to set that flag.
+ */
+ extern int dflag;
+-int dflag; /* print filter code */
+-static int Gflag; /* rotate dump files after this many seconds */
+-static int Gflag_count; /* number of files created with Gflag rotation */
+-static time_t Gflag_time; /* The last time_t the dump file was rotated. */
+-static int Lflag; /* list available data link types and exit */
+-static int Iflag; /* rfmon (monitor) mode */
++int dflag; /* print filter code */
++static int Gflag; /* rotate dump files after this many seconds */
++static int Gflag_count; /* number of files created with Gflag rotation */
++static time_t Gflag_time; /* The last time_t the dump file was rotated. */
++static int Lflag; /* list available data link types and exit */
++static int Iflag; /* rfmon (monitor) mode */
+ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+-static int Jflag; /* list available time stamp types */
+-static int jflag = -1; /* packet time stamp source */
++static int Jflag; /* list available time stamp types */
++static int jflag = -1; /* packet time stamp source */
+ #endif
+-static int lflag; /* line-buffered output */
+-static int pflag; /* don't go promiscuous */
++static int lflag; /* line-buffered output */
++static int pflag; /* don't go promiscuous */
+ #ifdef HAVE_PCAP_SETDIRECTION
+-static int Qflag = -1; /* restrict captured packet by send/receive direction */
++static int Qflag = -1; /* restrict captured packet by send/receive direction */
+ #endif
+ #ifdef HAVE_PCAP_DUMP_FLUSH
+-static int Uflag; /* "unbuffered" output of dump files */
++static int Uflag; /* "unbuffered" output of dump files */
+ #endif
+-static int Wflag; /* recycle output files after this number of files */
++static int Wflag; /* recycle output files after this number of files */
+ static int WflagChars;
+-static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */
+-static int timeout = 1000; /* default timeout = 1000 ms = 1 s */
++static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */
++static int timeout = 1000; /* default timeout = 1000 ms = 1 s */
+ #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ static int immediate_mode;
+ #endif
+@@ -246,7 +265,7 @@
+ static NORETURN void error(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2);
+ static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2);
+ static NORETURN void exit_tcpdump(int);
+-static void (*setsignal (int sig, void (*func)(int)))(int);
++static void (*setsignal(int sig, void (*func)(int)))(int);
+ static void cleanup(int);
+ static void child_cleanup(int);
+ static void print_version(FILE *);
+@@ -262,9 +281,9 @@
+ static NORETURN void show_remote_devices_and_exit(void);
+ #endif
+
+-static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
++void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
+ static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *);
+-static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
++void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
+ static void droproot(const char *, const char *);
+
+ #ifdef SIGNAL_REQ_INFO
+@@ -276,10 +295,10 @@
+ #endif
+
+ #ifdef _WIN32
+- static HANDLE timer_handle = INVALID_HANDLE_VALUE;
+- static void CALLBACK verbose_stats_dump(PVOID param, BOOLEAN timer_fired);
+-#else /* _WIN32 */
+- static void verbose_stats_dump(int sig);
++static HANDLE timer_handle = INVALID_HANDLE_VALUE;
++static void CALLBACK verbose_stats_dump(PVOID param, BOOLEAN timer_fired);
++#else /* _WIN32 */
++static void verbose_stats_dump(int sig);
+ #endif /* _WIN32 */
+
+ static void info(int);
+@@ -288,38 +307,82 @@
+ #ifdef HAVE_PCAP_FINDALLDEVS
+ static const struct tok status_flags[] = {
+ #ifdef PCAP_IF_UP
+- { PCAP_IF_UP, "Up" },
++ {PCAP_IF_UP, "Up"},
+ #endif
+ #ifdef PCAP_IF_RUNNING
+- { PCAP_IF_RUNNING, "Running" },
++ {PCAP_IF_RUNNING, "Running"},
+ #endif
+- { PCAP_IF_LOOPBACK, "Loopback" },
++ {PCAP_IF_LOOPBACK, "Loopback"},
+ #ifdef PCAP_IF_WIRELESS
+- { PCAP_IF_WIRELESS, "Wireless" },
++ {PCAP_IF_WIRELESS, "Wireless"},
+ #endif
+- { 0, NULL }
+-};
++ {0, NULL}};
+ #endif
+
+ static pcap_t *pd;
+ static pcap_dumper_t *pdd = NULL;
+
+ static int supports_monitor_mode;
+-
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+-struct dump_info {
+- char *WFileName;
+- char *CurrentFileName;
+- pcap_t *pd;
++static netdissect_options Ndo;
++static netdissect_options *ndo = &Ndo;
++u_char *pcap_userdata;
++static char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName;
++pcap_handler callback;
++static int print = 0;
++static FILE *VFile;
++static char VFileLine[PATH_MAX + 1];
++static char ebuf[PCAP_ERRBUF_SIZE];
++int dlt, i;
++static char *endp;
++static int cnt = -1, op;
++static bpf_u_int32 localnet = 0, netmask = 0;
++
++static int Oflag = 1; /* run filter code optimizer */
++static int yflag_dlt = -1;
++static const char *yflag_dlt_name = NULL;
++
++static const char *dlt_name;
++static struct bpf_program fcode;
++#ifndef _WIN32
++static void (*oldhandler)(int);
++#endif
++
++static const char *username = NULL;
++#ifndef _WIN32
++static const char *chroot_dir = NULL;
++#endif
++// static char *ret = NULL;
++static char *end;
++#ifdef HAVE_PCAP_FINDALLDEVS
++static pcap_if_t *devlist;
++static long devnum;
++#endif
++static int status;
++
++#ifdef HAVE_CAPSICUM
++static cap_rights_t rights;
++#endif /* HAVE_CAPSICUM */
++static int cansandbox;
++
++static int argc;
++static char **argv;
++
++struct dump_info
++{
++ char *WFileName;
++ char *CurrentFileName;
++ pcap_t *pd;
+ pcap_dumper_t *pdd;
+ netdissect_options *ndo;
+ #ifdef HAVE_CAPSICUM
+- int dirfd;
++ int dirfd;
+ #endif
+ };
++static struct dump_info dumpinfo;
+
+ #if defined(HAVE_PCAP_SET_PARSER_DEBUG)
+ /*
+@@ -330,10 +393,11 @@
+ */
+ #ifdef _WIN32
+ __declspec(dllimport)
+-#else /* _WIN32 */
++#else /* _WIN32 */
+ extern
+ #endif /* _WIN32 */
+-void pcap_set_parser_debug(int);
++void
++pcap_set_parser_debug(int);
+ #elif defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG)
+ /*
+ * We don't have pcap_set_parser_debug() in libpcap, but we do have
+@@ -347,7 +411,7 @@
+ extern int pcap_debug;
+
+ pcap_debug = value;
+-#else /* HAVE_PCAP_DEBUG */
++#else /* HAVE_PCAP_DEBUG */
+ extern int yydebug;
+
+ yydebug = value;
+@@ -366,10 +430,11 @@
+ */
+ #ifdef _WIN32
+ __declspec(dllimport)
+-#else /* _WIN32 */
++#else /* _WIN32 */
+ extern
+ #endif /* _WIN32 */
+-void pcap_set_optimizer_debug(int);
++void
++pcap_set_optimizer_debug(int);
+ #endif
+
+ /* VARARGS */
+@@ -382,7 +447,8 @@
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+- if (*fmt) {
++ if (*fmt)
++ {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+@@ -401,15 +467,15 @@
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+- if (*fmt) {
++ if (*fmt)
++ {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+ }
+
+-static void
+-exit_tcpdump(int status)
++static void exit_tcpdump(int status)
+ {
+ nd_cleanup();
+ exit(status);
+@@ -428,20 +494,25 @@
+ if (n_tstamp_types < 0)
+ error("%s", pcap_geterr(pc));
+
+- if (n_tstamp_types == 0) {
++ if (n_tstamp_types == 0)
++ {
+ fprintf(stderr, "Time stamp type cannot be set for %s\n",
+- device);
++ device);
+ exit_tcpdump(S_SUCCESS);
+ }
+ fprintf(stderr, "Time stamp types for %s (use option -j to set):\n",
+- device);
+- for (i = 0; i < n_tstamp_types; i++) {
++ device);
++ for (i = 0; i < n_tstamp_types; i++)
++ {
+ tstamp_type_name = pcap_tstamp_type_val_to_name(tstamp_types[i]);
+- if (tstamp_type_name != NULL) {
+- (void) fprintf(stderr, " %s (%s)\n", tstamp_type_name,
+- pcap_tstamp_type_val_to_description(tstamp_types[i]));
+- } else {
+- (void) fprintf(stderr, " %d\n", tstamp_types[i]);
++ if (tstamp_type_name != NULL)
++ {
++ (void)fprintf(stderr, " %s (%s)\n", tstamp_type_name,
++ pcap_tstamp_type_val_to_description(tstamp_types[i]));
++ }
++ else
++ {
++ (void)fprintf(stderr, " %d\n", tstamp_types[i]);
+ }
+ }
+ pcap_free_tstamp_types(tstamp_types);
+@@ -471,28 +542,32 @@
+ * not in monitor mode).
+ */
+ if (supports_monitor_mode)
+- (void) fprintf(stderr, "Data link types for %s %s (use option -y to set):\n",
+- device,
+- Iflag ? "when in monitor mode" : "when not in monitor mode");
++ (void)fprintf(stderr, "Data link types for %s %s (use option -y to set):\n",
++ device,
++ Iflag ? "when in monitor mode" : "when not in monitor mode");
+ else
+- (void) fprintf(stderr, "Data link types for %s (use option -y to set):\n",
+- device);
++ (void)fprintf(stderr, "Data link types for %s (use option -y to set):\n",
++ device);
+
+- for (i = 0; i < n_dlts; i++) {
++ for (i = 0; i < n_dlts; i++)
++ {
+ dlt_name = pcap_datalink_val_to_name(dlts[i]);
+- if (dlt_name != NULL) {
+- (void) fprintf(stderr, " %s (%s)", dlt_name,
+- pcap_datalink_val_to_description(dlts[i]));
++ if (dlt_name != NULL)
++ {
++ (void)fprintf(stderr, " %s (%s)", dlt_name,
++ pcap_datalink_val_to_description(dlts[i]));
+
+ /*
+ * OK, does tcpdump handle that type?
+ */
+ if (!has_printer(dlts[i]))
+- (void) fprintf(stderr, " (printing not supported)");
++ (void)fprintf(stderr, " (printing not supported)");
+ fprintf(stderr, "\n");
+- } else {
+- (void) fprintf(stderr, " DLT %d (printing not supported)\n",
+- dlts[i]);
++ }
++ else
++ {
++ (void)fprintf(stderr, " DLT %d (printing not supported)\n",
++ dlts[i]);
+ }
+ }
+ #ifdef HAVE_PCAP_FREE_DATALINKS
+@@ -511,16 +586,20 @@
+
+ if (pcap_findalldevs(&devlist, ebuf) < 0)
+ error("%s", ebuf);
+- for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) {
+- printf("%d.%s", i+1, dev->name);
++ for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next)
++ {
++ printf("%d.%s", i + 1, dev->name);
+ if (dev->description != NULL)
+ printf(" (%s)", dev->description);
+- if (dev->flags != 0) {
++ if (dev->flags != 0)
++ {
+ printf(" [");
+ printf("%s", bittok2str(status_flags, "none", dev->flags));
+ #ifdef PCAP_IF_WIRELESS
+- if (dev->flags & PCAP_IF_WIRELESS) {
+- switch (dev->flags & PCAP_IF_CONNECTION_STATUS) {
++ if (dev->flags & PCAP_IF_WIRELESS)
++ {
++ switch (dev->flags & PCAP_IF_CONNECTION_STATUS)
++ {
+
+ case PCAP_IF_CONNECTION_STATUS_UNKNOWN:
+ printf(", Association status unknown");
+@@ -537,8 +616,11 @@
+ case PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE:
+ break;
+ }
+- } else {
+- switch (dev->flags & PCAP_IF_CONNECTION_STATUS) {
++ }
++ else
++ {
++ switch (dev->flags & PCAP_IF_CONNECTION_STATUS)
++ {
+
+ case PCAP_IF_CONNECTION_STATUS_UNKNOWN:
+ printf(", Connection status unknown");
+@@ -575,10 +657,11 @@
+ int i;
+
+ if (pcap_findalldevs_ex(remote_interfaces_source, NULL, &devlist,
+- ebuf) < 0)
++ ebuf) < 0)
+ error("%s", ebuf);
+- for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) {
+- printf("%d.%s", i+1, dev->name);
++ for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next)
++ {
++ printf("%d.%s", i + 1, dev->name);
+ if (dev->description != NULL)
+ printf(" (%s)", dev->description);
+ if (dev->flags != 0)
+@@ -625,29 +708,29 @@
+ * version of libpcap we're using.
+ */
+ #if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
+-#define B_FLAG "B:"
+-#define B_FLAG_USAGE " [ -B size ]"
++#define B_FLAG "B:"
++#define B_FLAG_USAGE " [ -B size ]"
+ #else /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
+ #define B_FLAG
+ #define B_FLAG_USAGE
+ #endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
+
+ #ifdef HAVE_PCAP_FINDALLDEVS
+-#define D_FLAG "D"
++#define D_FLAG "D"
+ #else
+ #define D_FLAG
+ #endif
+
+ #ifdef HAVE_PCAP_CREATE
+-#define I_FLAG "I"
++#define I_FLAG "I"
+ #else /* HAVE_PCAP_CREATE */
+ #define I_FLAG
+ #endif /* HAVE_PCAP_CREATE */
+
+ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+-#define j_FLAG "j:"
+-#define j_FLAG_USAGE " [ -j tstamptype ]"
+-#define J_FLAG "J"
++#define j_FLAG "j:"
++#define j_FLAG_USAGE " [ -j tstamptype ]"
++#define J_FLAG "J"
+ #else /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */
+ #define j_FLAG
+ #define j_FLAG_USAGE
+@@ -667,12 +750,18 @@
+ #endif
+
+ #ifdef HAVE_PCAP_DUMP_FLUSH
+-#define U_FLAG "U"
++#define U_FLAG "U"
+ #else
+ #define U_FLAG
+ #endif
+
++#if STELLAR_DUMP
++#define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:P:k:g#"
++static struct stellar_dump_args g_stellar_dump_arg = {};
++static volatile int g_stellar_dump_run = 1;
++#else
+ #define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:#"
++#endif
+
+ /*
+ * Long options.
+@@ -694,65 +783,70 @@
+ * component of the entry for the long option, and have a case for that
+ * option in the switch statement.
+ */
+-#define OPTION_VERSION 128
+-#define OPTION_TSTAMP_PRECISION 129
+-#define OPTION_IMMEDIATE_MODE 130
+-#define OPTION_PRINT 131
+-#define OPTION_LIST_REMOTE_INTERFACES 132
+-#define OPTION_TSTAMP_MICRO 133
+-#define OPTION_TSTAMP_NANO 134
+-#define OPTION_FP_TYPE 135
+-#define OPTION_COUNT 136
++#define OPTION_VERSION 128
++#define OPTION_TSTAMP_PRECISION 129
++#define OPTION_IMMEDIATE_MODE 130
++#define OPTION_PRINT 131
++#define OPTION_LIST_REMOTE_INTERFACES 132
++#define OPTION_TSTAMP_MICRO 133
++#define OPTION_TSTAMP_NANO 134
++#define OPTION_FP_TYPE 135
++#define OPTION_COUNT 136
+
+ static const struct option longopts[] = {
+ #if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
+- { "buffer-size", required_argument, NULL, 'B' },
++ {"buffer-size", required_argument, NULL, 'B'},
+ #endif
+- { "list-interfaces", no_argument, NULL, 'D' },
++ {"list-interfaces", no_argument, NULL, 'D'},
+ #ifdef HAVE_PCAP_FINDALLDEVS_EX
+- { "list-remote-interfaces", required_argument, NULL, OPTION_LIST_REMOTE_INTERFACES },
++ {"list-remote-interfaces", required_argument, NULL, OPTION_LIST_REMOTE_INTERFACES},
+ #endif
+- { "help", no_argument, NULL, 'h' },
+- { "interface", required_argument, NULL, 'i' },
++ {"help", no_argument, NULL, 'h'},
++ {"serverip", required_argument, NULL, 'i'},
+ #ifdef HAVE_PCAP_CREATE
+- { "monitor-mode", no_argument, NULL, 'I' },
++ {"monitor-mode", no_argument, NULL, 'I'},
+ #endif
+ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+- { "time-stamp-type", required_argument, NULL, 'j' },
+- { "list-time-stamp-types", no_argument, NULL, 'J' },
++ {"time-stamp-type", required_argument, NULL, 'j'},
++ {"list-time-stamp-types", no_argument, NULL, 'J'},
+ #endif
+ #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+- { "micro", no_argument, NULL, OPTION_TSTAMP_MICRO},
+- { "nano", no_argument, NULL, OPTION_TSTAMP_NANO},
+- { "time-stamp-precision", required_argument, NULL, OPTION_TSTAMP_PRECISION},
+-#endif
+- { "dont-verify-checksums", no_argument, NULL, 'K' },
+- { "list-data-link-types", no_argument, NULL, 'L' },
+- { "no-optimize", no_argument, NULL, 'O' },
+- { "no-promiscuous-mode", no_argument, NULL, 'p' },
++ {"micro", no_argument, NULL, OPTION_TSTAMP_MICRO},
++ {"nano", no_argument, NULL, OPTION_TSTAMP_NANO},
++ {"time-stamp-precision", required_argument, NULL, OPTION_TSTAMP_PRECISION},
++#endif
++ {"dont-verify-checksums", no_argument, NULL, 'K'},
++ {"list-data-link-types", no_argument, NULL, 'L'},
++ {"no-optimize", no_argument, NULL, 'O'},
++ {"no-promiscuous-mode", no_argument, NULL, 'p'},
+ #ifdef HAVE_PCAP_SETDIRECTION
+- { "direction", required_argument, NULL, 'Q' },
++ {"direction", required_argument, NULL, 'Q'},
+ #endif
+- { "snapshot-length", required_argument, NULL, 's' },
+- { "absolute-tcp-sequence-numbers", no_argument, NULL, 'S' },
++ {"snapshot-length", required_argument, NULL, 's'},
++ {"absolute-tcp-sequence-numbers", no_argument, NULL, 'S'},
+ #ifdef HAVE_PCAP_DUMP_FLUSH
+- { "packet-buffered", no_argument, NULL, 'U' },
++ {"packet-buffered", no_argument, NULL, 'U'},
+ #endif
+- { "linktype", required_argument, NULL, 'y' },
++ {"linktype", required_argument, NULL, 'y'},
+ #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+- { "immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE },
++ {"immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE},
+ #endif
+ #ifdef HAVE_PCAP_SET_PARSER_DEBUG
+- { "debug-filter-parser", no_argument, NULL, 'Y' },
++ {"debug-filter-parser", no_argument, NULL, 'Y'},
+ #endif
+- { "relinquish-privileges", required_argument, NULL, 'Z' },
+- { "count", no_argument, NULL, OPTION_COUNT },
+- { "fp-type", no_argument, NULL, OPTION_FP_TYPE },
+- { "number", no_argument, NULL, '#' },
+- { "print", no_argument, NULL, OPTION_PRINT },
+- { "version", no_argument, NULL, OPTION_VERSION },
+- { NULL, 0, NULL, 0 }
+-};
++ {"relinquish-privileges", required_argument, NULL, 'Z'},
++ {"count", no_argument, NULL, OPTION_COUNT},
++ {"fp-type", no_argument, NULL, OPTION_FP_TYPE},
++ {"number", no_argument, NULL, '#'},
++ {"print", no_argument, NULL, OPTION_PRINT},
++ {"version", no_argument, NULL, OPTION_VERSION},
++#if STELLAR_DUMP
++ {"greedy", no_argument, NULL, 'g'},
++ {"threads", required_argument, NULL, 'k'},
++ {"serverport", required_argument, NULL, 'P'},
++// {"classify", required_argument, NULL, 255},
++#endif
++ {NULL, 0, NULL, 0}};
+
+ #ifdef HAVE_PCAP_FINDALLDEVS_EX
+ #define LIST_REMOTE_INTERFACES_USAGE "[ --list-remote-interfaces remote-source ]"
+@@ -777,11 +871,13 @@
+ error("Chroot without dropping root is insecure");
+
+ pw = getpwnam(username);
+- if (pw) {
+- if (chroot_dir) {
+- if (chroot(chroot_dir) != 0 || chdir ("/") != 0)
++ if (pw)
++ {
++ if (chroot_dir)
++ {
++ if (chroot(chroot_dir) != 0 || chdir("/") != 0)
+ error("Couldn't chroot/chdir to '%.64s': %s",
+- chroot_dir, pcap_strerror(errno));
++ chroot_dir, pcap_strerror(errno));
+ }
+ #ifdef HAVE_LIBCAP_NG
+ {
+@@ -793,21 +889,23 @@
+ }
+ #else
+ if (initgroups(pw->pw_name, pw->pw_gid) != 0 ||
+- setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0)
++ setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0)
+ error("Couldn't change to '%.32s' uid=%lu gid=%lu: %s",
+- username,
+- (unsigned long)pw->pw_uid,
+- (unsigned long)pw->pw_gid,
+- pcap_strerror(errno));
+- else {
++ username,
++ (unsigned long)pw->pw_uid,
++ (unsigned long)pw->pw_gid,
++ pcap_strerror(errno));
++ else
++ {
+ fprintf(stderr, "dropped privs to %s\n", username);
+ }
+ #endif /* HAVE_LIBCAP_NG */
+- } else
++ }
++ else
+ error("Couldn't find user '%.32s'", username);
+ #ifdef HAVE_LIBCAP_NG
+ /* We don't need CAP_SETUID, CAP_SETGID and CAP_SYS_CHROOT any more. */
+-DIAG_OFF_ASSIGN_ENUM
++ DIAG_OFF_ASSIGN_ENUM
+ capng_updatev(
+ CAPNG_DROP,
+ CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+@@ -815,10 +913,9 @@
+ CAP_SETGID,
+ CAP_SYS_CHROOT,
+ -1);
+-DIAG_ON_ASSIGN_ENUM
++ DIAG_ON_ASSIGN_ENUM
+ capng_apply(CAPNG_SELECT_BOTH);
+ #endif /* HAVE_LIBCAP_NG */
+-
+ }
+ #endif /* _WIN32 */
+
+@@ -828,7 +925,8 @@
+ int c = 0;
+
+ x -= 1;
+- while (x > 0) {
++ while (x > 0)
++ {
+ c += 1;
+ x /= 10;
+ }
+@@ -836,56 +934,61 @@
+ return c;
+ }
+
+-
+ static void
+ MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars)
+ {
+- char *filename = malloc(PATH_MAX + 1);
+- if (filename == NULL)
+- error("%s: malloc", __func__);
+- if (strlen(orig_name) == 0)
+- error("an empty string is not a valid file name");
+-
+- /* Process with strftime if Gflag is set. */
+- if (Gflag != 0) {
+- struct tm *local_tm;
+-
+- /* Convert Gflag_time to a usable format */
+- if ((local_tm = localtime(&Gflag_time)) == NULL) {
+- error("%s: localtime", __func__);
+- }
+-
+- /* There's no good way to detect an error in strftime since a return
+- * value of 0 isn't necessarily failure; if orig_name is an empty
+- * string, the formatted string will be empty.
+- *
+- * However, the C90 standard says that, if there *is* a
+- * buffer overflow, the content of the buffer is undefined,
+- * so we must check for a buffer overflow.
+- *
+- * So we check above for an empty orig_name, and only call
+- * strftime() if it's non-empty, in which case the return
+- * value will only be 0 if the formatted date doesn't fit
+- * in the buffer.
+- *
+- * (We check above because, even if we don't use -G, we
+- * want a better error message than "tcpdump: : No such
+- * file or directory" for this case.)
+- */
+- if (strftime(filename, PATH_MAX, orig_name, local_tm) == 0) {
+- error("%s: strftime", __func__);
+- }
+- } else {
+- strncpy(filename, orig_name, PATH_MAX);
+- }
++ char *filename = malloc(PATH_MAX + 1);
++ if (filename == NULL)
++ error("%s: malloc", __func__);
++ if (strlen(orig_name) == 0)
++ error("an empty string is not a valid file name");
++
++ /* Process with strftime if Gflag is set. */
++ if (Gflag != 0)
++ {
++ struct tm *local_tm;
++
++ /* Convert Gflag_time to a usable format */
++ if ((local_tm = localtime(&Gflag_time)) == NULL)
++ {
++ error("%s: localtime", __func__);
++ return;
++ }
++
++ /* There's no good way to detect an error in strftime since a return
++ * value of 0 isn't necessarily failure; if orig_name is an empty
++ * string, the formatted string will be empty.
++ *
++ * However, the C90 standard says that, if there *is* a
++ * buffer overflow, the content of the buffer is undefined,
++ * so we must check for a buffer overflow.
++ *
++ * So we check above for an empty orig_name, and only call
++ * strftime() if it's non-empty, in which case the return
++ * value will only be 0 if the formatted date doesn't fit
++ * in the buffer.
++ *
++ * (We check above because, even if we don't use -G, we
++ * want a better error message than "tcpdump: : No such
++ * file or directory" for this case.)
++ */
++ if (strftime(filename, PATH_MAX, orig_name, local_tm) == 0)
++ {
++ error("%s: strftime", __func__);
++ return;
++ }
++ }
++ else
++ {
++ strncpy(filename, orig_name, PATH_MAX);
++ }
+
+ if (cnt == 0 && max_chars == 0)
+ strncpy(buffer, filename, PATH_MAX + 1);
+- else
+- if (snprintf(buffer, PATH_MAX + 1, "%s%0*d", filename, max_chars, cnt) > PATH_MAX)
+- /* Report an error if the filename is too large */
+- error("too many output files or filename is too long (> %d)", PATH_MAX);
+- free(filename);
++ else if (snprintf(buffer, PATH_MAX + 1, "%s%0*d", filename, max_chars, cnt) > PATH_MAX)
++ /* Report an error if the filename is too large */
++ error("too many output files or filename is too long (> %d)", PATH_MAX);
++ free(filename);
+ }
+
+ static char *
+@@ -898,7 +1001,7 @@
+ if (!ret)
+ return NULL;
+
+- len = strlen (ptr);
++ len = strlen(ptr);
+ if (len > 0 && ptr[len - 1] == '\n')
+ ptr[len - 1] = '\0';
+
+@@ -932,7 +1035,7 @@
+
+ return (capdnsloc);
+ }
+-#endif /* HAVE_CASPER */
++#endif /* HAVE_CASPER */
+
+ #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ static int
+@@ -950,7 +1053,8 @@
+ static const char *
+ tstamp_precision_to_string(int precision)
+ {
+- switch (precision) {
++ switch (precision)
++ {
+
+ case PCAP_TSTAMP_PRECISION_MICRO:
+ return "micro";
+@@ -962,6 +1066,7 @@
+ return "unknown";
+ }
+ }
++
+ #endif
+
+ #ifdef HAVE_CAPSICUM
+@@ -1025,10 +1130,12 @@
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_SEEK, CAP_WRITE, CAP_FCNTL);
+- if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS) {
++ if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS)
++ {
+ error("unable to limit dump descriptor");
+ }
+- if (cap_fcntls_limit(fd, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) {
++ if (cap_fcntls_limit(fd, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS)
++ {
+ error("unable to limit dump descriptor fcntls");
+ }
+ }
+@@ -1037,8 +1144,7 @@
+ /*
+ * Copy arg vector into a new buffer, concatenating arguments with spaces.
+ */
+-static char *
+-copy_argv(char **argv)
++static char *copy_argv(char **argv)
+ {
+ char **p;
+ size_t len = 0;
+@@ -1058,7 +1164,8 @@
+
+ p = argv;
+ dst = buf;
+- while ((src = *p++) != NULL) {
++ while ((src = *p++) != NULL)
++ {
+ while ((*dst++ = *src++) != '\0')
+ ;
+ dst[-1] = ' ';
+@@ -1075,7 +1182,7 @@
+ * we define it as 0 if it's not defined, so it does nothing.
+ */
+ #ifndef O_BINARY
+-#define O_BINARY 0
++#define O_BINARY 0
+ #endif
+
+ static char *
+@@ -1086,7 +1193,7 @@
+ char *cp;
+ our_statb buf;
+
+- fd = open(fname, O_RDONLY|O_BINARY);
++ fd = open(fname, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ error("can't open %s: %s", fname, pcap_strerror(errno));
+
+@@ -1103,17 +1210,18 @@
+ cp = malloc((u_int)buf.st_size + 1);
+ if (cp == NULL)
+ error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1,
+- fname, pcap_strerror(errno));
++ fname, pcap_strerror(errno));
+ cc = read(fd, cp, (u_int)buf.st_size);
+ if (cc < 0)
+ error("read %s: %s", fname, pcap_strerror(errno));
+ if (cc != buf.st_size)
+- error("short read %s (%d != %d)", fname, (int) cc,
+- (int)buf.st_size);
++ error("short read %s (%d != %d)", fname, (int)cc,
++ (int)buf.st_size);
+
+ close(fd);
+ /* replace "# comment" with spaces */
+- for (i = 0; i < cc; i++) {
++ for (i = 0; i < cc; i++)
++ {
+ if (cp[i] == '#')
+ while (i < cc && cp[i] != '\n')
+ cp[i++] = ' ';
+@@ -1135,19 +1243,22 @@
+ * of the device.
+ */
+ p = strchr(device, ':');
+- if (p != NULL) {
++ if (p != NULL)
++ {
+ /*
+ * We found it. Is it followed by "//"?
+ */
+- p++; /* skip the : */
+- if (strncmp(p, "//", 2) == 0) {
++ p++; /* skip the : */
++ if (strncmp(p, "//", 2) == 0)
++ {
+ /*
+ * Yes. Search for the next /, at the end of the
+ * authority part of the URL.
+ */
+- p += 2; /* skip the // */
++ p += 2; /* skip the // */
+ p = strchr(p, '/');
+- if (p != NULL) {
++ if (p != NULL)
++ {
+ /*
+ * OK, past the / is the path.
+ */
+@@ -1156,18 +1267,22 @@
+ }
+ }
+ devnum = strtol(device, &end, 10);
+- if (device != end && *end == '\0') {
++ if (device != end && *end == '\0')
++ {
+ /*
+ * It's all-numeric, but is it a valid number?
+ */
+- if (devnum <= 0) {
++ if (devnum <= 0)
++ {
+ /*
+ * No, it's not an ordinal.
+ */
+ error("Invalid adapter index");
+ }
+ return (devnum);
+- } else {
++ }
++ else
++ {
+ /*
+ * It's not all-numeric; return -1, so our caller
+ * knows that.
+@@ -1175,13 +1290,15 @@
+ return (-1);
+ }
+ }
++#endif
+
+ static char *
+ find_interface_by_number(const char *url
+ #ifndef HAVE_PCAP_FINDALLDEVS_EX
+-_U_
++ _U_
+ #endif
+-, long devnum)
++ ,
++ long devnum)
+ {
+ pcap_if_t *dev, *devlist;
+ long i;
+@@ -1199,27 +1316,31 @@
+ * of the URL.
+ */
+ endp = strchr(url, ':');
+- if (endp != NULL) {
++ if (endp != NULL)
++ {
+ /*
+ * We found it. Is it followed by "//"?
+ */
+- endp++; /* skip the : */
+- if (strncmp(endp, "//", 2) == 0) {
++ endp++; /* skip the : */
++ if (strncmp(endp, "//", 2) == 0)
++ {
+ /*
+ * Yes. Search for the next /, at the end of the
+ * authority part of the URL.
+ */
+- endp += 2; /* skip the // */
++ endp += 2; /* skip the // */
+ endp = strchr(endp, '/');
+- } else
++ }
++ else
+ endp = NULL;
+ }
+- if (endp != NULL) {
++ if (endp != NULL)
++ {
+ /*
+ * OK, everything from device to endp is a URL to hand
+ * to pcap_findalldevs_ex().
+ */
+- endp++; /* Include the trailing / in the URL; pcap_findalldevs_ex() requires it */
++ endp++; /* Include the trailing / in the URL; pcap_findalldevs_ex() requires it */
+ host_url = malloc(endp - url + 1);
+ if (host_url == NULL && (endp - url + 1) > 0)
+ error("Invalid allocation for host");
+@@ -1228,24 +1349,30 @@
+ host_url[endp - url] = '\0';
+ status = pcap_findalldevs_ex(host_url, NULL, &devlist, ebuf);
+ free(host_url);
+- } else
++ }
++ else
+ #endif
+- status = pcap_findalldevs(&devlist, ebuf);
++ status = pcap_findalldevs(&devlist, ebuf);
+ if (status < 0)
++ {
+ error("%s", ebuf);
++ return NULL;
++ }
+ /*
+ * Look for the devnum-th entry in the list of devices (1-based).
+ */
+- for (i = 0, dev = devlist; i < devnum-1 && dev != NULL;
+- i++, dev = dev->next)
++ for (i = 0, dev = devlist; i < devnum - 1 && dev != NULL;
++ i++, dev = dev->next)
+ ;
+ if (dev == NULL)
++ {
+ error("Invalid adapter index");
++ return NULL;
++ }
+ device = strdup(dev->name);
+ pcap_freealldevs(devlist);
+ return (device);
+ }
+-#endif
+
+ #ifdef HAVE_PCAP_OPEN
+ /*
+@@ -1269,15 +1396,17 @@
+ * Is this an rpcap URL?
+ */
+ if (strncmp(device, rpcap_prefix, sizeof(rpcap_prefix) - 1) == 0 ||
+- strncmp(device, rpcap_ssl_prefix, sizeof(rpcap_ssl_prefix) - 1) == 0) {
++ strncmp(device, rpcap_ssl_prefix, sizeof(rpcap_ssl_prefix) - 1) == 0)
++ {
+ /*
+ * Yes. Open it with pcap_open().
+ */
+ *ebuf = '\0';
+ pc = pcap_open(device, ndo->ndo_snaplen,
+- pflag ? 0 : PCAP_OPENFLAG_PROMISCUOUS, timeout, NULL,
+- ebuf);
+- if (pc == NULL) {
++ pflag ? 0 : PCAP_OPENFLAG_PROMISCUOUS, timeout, NULL,
++ ebuf);
++ if (pc == NULL)
++ {
+ /*
+ * If this failed with "No such device" or "The system
+ * cannot find the device specified", that means
+@@ -1286,7 +1415,7 @@
+ * actually an interface index.
+ */
+ if (strstr(ebuf, "No such device") != NULL ||
+- strstr(ebuf, "The system cannot find the device specified") != NULL)
++ strstr(ebuf, "The system cannot find the device specified") != NULL)
+ return (NULL);
+ error("%s", ebuf);
+ }
+@@ -1298,7 +1427,8 @@
+
+ #ifdef HAVE_PCAP_CREATE
+ pc = pcap_create(device, ebuf);
+- if (pc == NULL) {
++ if (pc == NULL)
++ {
+ /*
+ * If this failed with "No such device", that means
+ * the interface doesn't exist; return NULL, so that
+@@ -1317,17 +1447,18 @@
+ status = pcap_set_tstamp_precision(pc, ndo->ndo_tstamp_precision);
+ if (status != 0)
+ error("%s: Can't set %ssecond time stamp precision: %s",
+- device,
+- tstamp_precision_to_string(ndo->ndo_tstamp_precision),
+- pcap_statustostr(status));
++ device,
++ tstamp_precision_to_string(ndo->ndo_tstamp_precision),
++ pcap_statustostr(status));
+ #endif
+
+ #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+- if (immediate_mode) {
++ if (immediate_mode)
++ {
+ status = pcap_set_immediate_mode(pc, 1);
+ if (status != 0)
+ error("%s: Can't set immediate mode: %s",
+- device, pcap_statustostr(status));
++ device, pcap_statustostr(status));
+ }
+ #endif
+ /*
+@@ -1337,7 +1468,8 @@
+ supports_monitor_mode = 1;
+ else
+ supports_monitor_mode = 0;
+- if (ndo->ndo_snaplen != 0) {
++ if (ndo->ndo_snaplen != 0)
++ {
+ /*
+ * A snapshot length was explicitly specified;
+ * use it.
+@@ -1345,66 +1477,73 @@
+ status = pcap_set_snaplen(pc, ndo->ndo_snaplen);
+ if (status != 0)
+ error("%s: Can't set snapshot length: %s",
+- device, pcap_statustostr(status));
++ device, pcap_statustostr(status));
+ }
+ status = pcap_set_promisc(pc, !pflag);
+ if (status != 0)
+ error("%s: Can't set promiscuous mode: %s",
+- device, pcap_statustostr(status));
+- if (Iflag) {
++ device, pcap_statustostr(status));
++ if (Iflag)
++ {
+ status = pcap_set_rfmon(pc, 1);
+ if (status != 0)
+ error("%s: Can't set monitor mode: %s",
+- device, pcap_statustostr(status));
++ device, pcap_statustostr(status));
+ }
+ status = pcap_set_timeout(pc, timeout);
+ if (status != 0)
+ error("%s: pcap_set_timeout failed: %s",
+- device, pcap_statustostr(status));
+- if (Bflag != 0) {
++ device, pcap_statustostr(status));
++ if (Bflag != 0)
++ {
+ status = pcap_set_buffer_size(pc, Bflag);
+ if (status != 0)
+ error("%s: Can't set buffer size: %s",
+- device, pcap_statustostr(status));
++ device, pcap_statustostr(status));
+ }
+ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+- if (jflag != -1) {
++ if (jflag != -1)
++ {
+ status = pcap_set_tstamp_type(pc, jflag);
+ if (status < 0)
+ error("%s: Can't set time stamp type: %s",
+- device, pcap_statustostr(status));
++ device, pcap_statustostr(status));
+ else if (status > 0)
+ warning("When trying to set timestamp type '%s' on %s: %s",
+- pcap_tstamp_type_val_to_name(jflag), device,
+- pcap_statustostr(status));
++ pcap_tstamp_type_val_to_name(jflag), device,
++ pcap_statustostr(status));
+ }
+ #endif
+ status = pcap_activate(pc);
+- if (status < 0) {
++ if (status < 0)
++ {
+ /*
+ * pcap_activate() failed.
+ */
+ cp = pcap_geterr(pc);
+ if (status == PCAP_ERROR)
+ error("%s", cp);
+- else if (status == PCAP_ERROR_NO_SUCH_DEVICE) {
++ else if (status == PCAP_ERROR_NO_SUCH_DEVICE)
++ {
+ /*
+ * Return an error for our caller to handle.
+ */
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s\n(%s)",
+- device, pcap_statustostr(status), cp);
+- } else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0')
++ device, pcap_statustostr(status), cp);
++ }
++ else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0')
+ error("%s: %s\n(%s)", device,
+- pcap_statustostr(status), cp);
++ pcap_statustostr(status), cp);
+ #ifdef __FreeBSD__
+ else if (status == PCAP_ERROR_RFMON_NOTSUP &&
+- strncmp(device, "wlan", 4) == 0) {
++ strncmp(device, "wlan", 4) == 0)
++ {
+ char parent[8], newdev[8];
+ char sysctl[32];
+ size_t s = sizeof(parent);
+
+ snprintf(sysctl, sizeof(sysctl),
+- "net.wlan.%d.%%parent", atoi(device + 4));
++ "net.wlan.%d.%%parent", atoi(device + 4));
+ sysctlbyname(sysctl, parent, &s, NULL, 0);
+ strlcpy(newdev, device, sizeof(newdev));
+ /* Suggest a new wlan device. */
+@@ -1412,20 +1551,22 @@
+ * when the index is 9 or greater but the only consequence in this
+ * specific case would be an error message that looks a bit odd.
+ */
+- newdev[strlen(newdev)-1]++;
++ newdev[strlen(newdev) - 1]++;
+ error("%s is not a monitor mode VAP\n"
+- "To create a new monitor mode VAP use:\n"
+- " ifconfig %s create wlandev %s wlanmode monitor\n"
+- "and use %s as the tcpdump interface",
+- device, newdev, parent, newdev);
++ "To create a new monitor mode VAP use:\n"
++ " ifconfig %s create wlandev %s wlanmode monitor\n"
++ "and use %s as the tcpdump interface",
++ device, newdev, parent, newdev);
+ }
+ #endif
+ else
+ error("%s: %s", device,
+- pcap_statustostr(status));
++ pcap_statustostr(status));
+ pcap_close(pc);
+ return (NULL);
+- } else if (status > 0) {
++ }
++ else if (status > 0)
++ {
+ /*
+ * pcap_activate() succeeded, but it's warning us
+ * of a problem it had.
+@@ -1434,22 +1575,23 @@
+ if (status == PCAP_WARNING)
+ warning("%s", cp);
+ else if (status == PCAP_WARNING_PROMISC_NOTSUP &&
+- *cp != '\0')
++ *cp != '\0')
+ warning("%s: %s\n(%s)", device,
+- pcap_statustostr(status), cp);
++ pcap_statustostr(status), cp);
+ else
+ warning("%s: %s", device,
+- pcap_statustostr(status));
++ pcap_statustostr(status));
+ }
+ #ifdef HAVE_PCAP_SETDIRECTION
+- if (Qflag != -1) {
++ if (Qflag != -1)
++ {
+ status = pcap_setdirection(pc, Qflag);
+ if (status != 0)
+ error("%s: pcap_setdirection() failed: %s",
+- device, pcap_geterr(pc));
+- }
++ device, pcap_geterr(pc));
++ }
+ #endif /* HAVE_PCAP_SETDIRECTION */
+-#else /* HAVE_PCAP_CREATE */
++#else /* HAVE_PCAP_CREATE */
+ *ebuf = '\0';
+ /*
+ * If no snapshot length was specified, or a length of 0 was
+@@ -1458,7 +1600,8 @@
+ if (ndo->ndo_snaplen == 0)
+ ndo->ndo_snaplen = MAXIMUM_SNAPLEN;
+ pc = pcap_open_live(device, ndo->ndo_snaplen, !pflag, timeout, ebuf);
+- if (pc == NULL) {
++ if (pc == NULL)
++ {
+ /*
+ * If this failed with "No such device", that means
+ * the interface doesn't exist; return NULL, so that
+@@ -1476,48 +1619,743 @@
+ return (pc);
+ }
+
+-int
+-main(int argc, char **argv)
++/*
++ * Catch a signal.
++ */
++static void (*setsignal(int sig, void (*func)(int)))(int)
+ {
+- int cnt, op, i;
+- bpf_u_int32 localnet = 0, netmask = 0;
+- char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName;
+- char *endp;
+- pcap_handler callback;
+- int dlt;
+- const char *dlt_name;
+- struct bpf_program fcode;
+-#ifndef _WIN32
+- void (*oldhandler)(int);
++#ifdef _WIN32
++ return (signal(sig, func));
++#else
++ struct sigaction old, new;
++
++ memset(&new, 0, sizeof(new));
++ new.sa_handler = func;
++ if ((sig == SIGCHLD)
++#ifdef SIGNAL_REQ_INFO
++ || (sig == SIGNAL_REQ_INFO)
+ #endif
+- struct dump_info dumpinfo;
+- u_char *pcap_userdata;
+- char ebuf[PCAP_ERRBUF_SIZE];
+- char VFileLine[PATH_MAX + 1];
+- const char *username = NULL;
+-#ifndef _WIN32
+- const char *chroot_dir = NULL;
++#ifdef SIGNAL_FLUSH_PCAP
++ || (sig == SIGNAL_FLUSH_PCAP)
+ #endif
+- char *ret = NULL;
+- char *end;
+-#ifdef HAVE_PCAP_FINDALLDEVS
+- pcap_if_t *devlist;
+- long devnum;
++ )
++ new.sa_flags = SA_RESTART;
++ if (sigaction(sig, &new, &old) < 0)
++ return (SIG_ERR);
++ return (old.sa_handler);
+ #endif
+- int status;
+- FILE *VFile;
++}
++
++/* make a clean exit on interrupts */
++static void cleanup(int signo _U_)
++{
++#ifdef _WIN32
++ if (timer_handle != INVALID_HANDLE_VALUE)
++ {
++ DeleteTimerQueueTimer(NULL, timer_handle, NULL);
++ CloseHandle(timer_handle);
++ timer_handle = INVALID_HANDLE_VALUE;
++ }
++#else /* _WIN32 */
++ struct itimerval timer;
++
++ timer.it_interval.tv_sec = 0;
++ timer.it_interval.tv_usec = 0;
++ timer.it_value.tv_sec = 0;
++ timer.it_value.tv_usec = 0;
++ setitimer(ITIMER_REAL, &timer, NULL);
++#endif /* _WIN32 */
++
++#ifdef HAVE_PCAP_BREAKLOOP
++ /*
++ * We have "pcap_breakloop()"; use it, so that we do as little
++ * as possible in the signal handler (it's probably not safe
++ * to do anything with standard I/O streams in a signal handler -
++ * the ANSI C standard doesn't say it is).
++ */
++ if (pd)
++ {
++ pcap_breakloop(pd);
++ pcap_close(pd);
++#ifdef HAVE_PCAP_DUMP_FLUSH
++ if (WFileName != NULL)
++ {
++ pcap_dump_flush(dumpinfo.pdd);
++ }
++#endif
++ pd = NULL;
++ }
++#else
++ /*
++ * We don't have "pcap_breakloop()"; this isn't safe, but
++ * it's the best we can do. Print the summary if we're
++ * not reading from a savefile - i.e., if we're doing a
++ * live capture - and exit.
++ */
++ if (pd != NULL && pcap_file(pd) == NULL)
++ {
++ /*
++ * We got interrupted, so perhaps we didn't
++ * manage to finish a line we were printing.
++ * Print an extra newline, just in case.
++ */
++ putchar('\n');
++ (void)fflush(stdout);
++ info(1);
++ }
++ exit_tcpdump(S_SUCCESS);
++#endif
++
++#if STELLAR_DUMP
++ if (fcode.bf_len != 0)
++ {
++ pcap_freecode(&fcode);
++ memset(&fcode, 0, sizeof(fcode));
++ }
++ g_stellar_dump_run = 0;
++ if (device != NULL) // for memleak check
++ {
++ free(device);
++ device = NULL;
++ }
++ if (RFileName == NULL && VFileName == NULL)
++ {
++ stellar_dump_ctrllink_keepalive_stop();
++ stellar_dump_ctrllink_free();
++ }
++ exit_tcpdump(S_SUCCESS);
++#endif
++}
++
++/*
++ On windows, we do not use a fork, so we do not care less about
++ waiting a child processes to die
++ */
++#if defined(HAVE_FORK) || defined(HAVE_VFORK)
++static void
++child_cleanup(int signo _U_)
++{
++ wait(NULL);
++}
++#endif /* HAVE_FORK && HAVE_VFORK */
++
++static void
++info(int verbose)
++{
++ struct pcap_stat stats;
++
++ /*
++ * Older versions of libpcap didn't set ps_ifdrop on some
++ * platforms; initialize it to 0 to handle that.
++ */
++ stats.ps_ifdrop = 0;
++ if (pcap_stats(pd, &stats) < 0)
++ {
++ (void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pd));
++ infoprint = 0;
++ return;
++ }
++
++ if (!verbose)
++ fprintf(stderr, "%s: ", program_name);
++
++ (void)fprintf(stderr, "%u packet%s captured", packets_captured,
++ PLURAL_SUFFIX(packets_captured));
++ if (!verbose)
++ fputs(", ", stderr);
++ else
++ putc('\n', stderr);
++ (void)fprintf(stderr, "%u packet%s received by filter", stats.ps_recv,
++ PLURAL_SUFFIX(stats.ps_recv));
++ if (!verbose)
++ fputs(", ", stderr);
++ else
++ putc('\n', stderr);
++ (void)fprintf(stderr, "%u packet%s dropped by kernel", stats.ps_drop,
++ PLURAL_SUFFIX(stats.ps_drop));
++ if (stats.ps_ifdrop != 0)
++ {
++ if (!verbose)
++ fputs(", ", stderr);
++ else
++ putc('\n', stderr);
++ (void)fprintf(stderr, "%u packet%s dropped by interface\n",
++ stats.ps_ifdrop, PLURAL_SUFFIX(stats.ps_ifdrop));
++ }
++ else
++ putc('\n', stderr);
++ infoprint = 0;
++}
++
++#if defined(HAVE_FORK) || defined(HAVE_VFORK)
++#ifdef HAVE_FORK
++#define fork_subprocess() fork()
++#else
++#define fork_subprocess() vfork()
++#endif
++static void
++compress_savefile(const char *filename)
++{
++ pid_t child;
++
++ child = fork_subprocess();
++ if (child == -1)
++ {
++ fprintf(stderr,
++ "compress_savefile: fork failed: %s\n",
++ pcap_strerror(errno));
++ return;
++ }
++ if (child != 0)
++ {
++ /* Parent process. */
++ return;
++ }
++
++ /*
++ * Child process.
++ * Set to lowest priority so that this doesn't disturb the capture.
++ */
++#ifdef NZERO
++ setpriority(PRIO_PROCESS, 0, NZERO - 1);
++#else
++ setpriority(PRIO_PROCESS, 0, 19);
++#endif
++ if (execlp(zflag, zflag, filename, (char *)NULL) == -1)
++ fprintf(stderr,
++ "compress_savefile: execlp(%s, %s) failed: %s\n",
++ zflag,
++ filename,
++ pcap_strerror(errno));
++#ifdef HAVE_FORK
++ exit(S_ERR_HOST_PROGRAM);
++#else
++ _exit(S_ERR_HOST_PROGRAM);
++#endif
++}
++#else /* HAVE_FORK && HAVE_VFORK */
++static void
++compress_savefile(const char *filename)
++{
++ fprintf(stderr,
++ "compress_savefile failed. Functionality not implemented under your system\n");
++}
++#endif /* HAVE_FORK && HAVE_VFORK */
++
++static void
++dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
++{
++ struct dump_info *dump_info;
++
++ ++packets_captured;
++
++ ++infodelay;
++
++ dump_info = (struct dump_info *)user;
++
++ /*
++ * XXX - this won't force the file to rotate on the specified time
++ * boundary, but it will rotate on the first packet received after the
++ * specified Gflag number of seconds. Note: if a Gflag time boundary
++ * and a Cflag size boundary coincide, the time rotation will occur
++ * first thereby cancelling the Cflag boundary (since the file should
++ * be 0).
++ */
++ if (Gflag != 0)
++ {
++ /* Check if it is time to rotate */
++ time_t t;
++
++ /* Get the current time */
++ if ((t = time(NULL)) == (time_t)-1)
++ {
++ error("%s: can't get current_time: %s",
++ __func__, pcap_strerror(errno));
++ }
++
++ /* If the time is greater than the specified window, rotate */
++ if (t - Gflag_time >= Gflag)
++ {
+ #ifdef HAVE_CAPSICUM
+- cap_rights_t rights;
+- int cansandbox;
+-#endif /* HAVE_CAPSICUM */
+- int Oflag = 1; /* run filter code optimizer */
+- int yflag_dlt = -1;
+- const char *yflag_dlt_name = NULL;
+- int print = 0;
++ FILE *fp;
++ int fd;
++#endif
++
++ /* Update the Gflag_time */
++ Gflag_time = t;
++ /* Update Gflag_count */
++ Gflag_count++;
++ /*
++ * Close the current file and open a new one.
++ */
++ pcap_dump_close(dump_info->pdd);
++
++ /*
++ * Compress the file we just closed, if the user asked for it
++ */
++ if (zflag != NULL)
++ compress_savefile(dump_info->CurrentFileName);
++
++ /*
++ * Check to see if we've exceeded the Wflag (when
++ * not using Cflag).
++ */
++ if (Cflag == 0 && Wflag > 0 && Gflag_count >= Wflag)
++ {
++ (void)fprintf(stderr, "Maximum file limit reached: %d\n",
++ Wflag);
++ info(1);
++ exit_tcpdump(S_SUCCESS);
++ /* NOTREACHED */
++ }
++ if (dump_info->CurrentFileName != NULL)
++ free(dump_info->CurrentFileName);
++ /* Allocate space for max filename + \0. */
++ dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
++ if (dump_info->CurrentFileName == NULL)
++ error("dump_packet_and_trunc: malloc");
++ /*
++ * Gflag was set otherwise we wouldn't be here. Reset the count
++ * so multiple files would end with 1,2,3 in the filename.
++ * The counting is handled with the -C flow after this.
++ */
++ Cflag_count = 0;
++
++ /*
++ * This is always the first file in the Cflag
++ * rotation: e.g. 0
++ * We also don't need numbering if Cflag is not set.
++ */
++ if (Cflag != 0)
++ MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0,
++ WflagChars);
++ else
++ MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0);
++
++#ifdef HAVE_LIBCAP_NG
++ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
++ capng_apply(CAPNG_SELECT_BOTH);
++#endif /* HAVE_LIBCAP_NG */
++#ifdef HAVE_CAPSICUM
++ fd = openat(dump_info->dirfd,
++ dump_info->CurrentFileName,
++ O_CREAT | O_WRONLY | O_TRUNC, 0644);
++ if (fd < 0)
++ {
++ error("unable to open file %s",
++ dump_info->CurrentFileName);
++ }
++ fp = fdopen(fd, "w");
++ if (fp == NULL)
++ {
++ error("unable to fdopen file %s",
++ dump_info->CurrentFileName);
++ }
++ dump_info->pdd = pcap_dump_fopen(dump_info->pd, fp);
++#else /* !HAVE_CAPSICUM */
++ dump_info->pdd = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
++#endif
++#ifdef HAVE_LIBCAP_NG
++ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
++ capng_apply(CAPNG_SELECT_BOTH);
++#endif /* HAVE_LIBCAP_NG */
++ if (dump_info->pdd == NULL)
++ error("%s", pcap_geterr(pd));
++#ifdef HAVE_CAPSICUM
++ set_dumper_capsicum_rights(dump_info->pdd);
++#endif
++ }
++ }
++
++ /*
++ * XXX - this won't prevent capture files from getting
++ * larger than Cflag - the last packet written to the
++ * file could put it over Cflag.
++ */
++ if (Cflag != 0)
++ {
++#ifdef HAVE_PCAP_DUMP_FTELL64
++ int64_t size = pcap_dump_ftell64(dump_info->pdd);
++#else
++ /*
++ * XXX - this only handles a Cflag value > 2^31-1 on
++ * LP64 platforms; to handle ILP32 (32-bit UN*X and
++ * Windows) or LLP64 (64-bit Windows) would require
++ * a version of libpcap with pcap_dump_ftell64().
++ */
++ long size = pcap_dump_ftell(dump_info->pdd);
++#endif
++
++ if (size == -1)
++ error("ftell fails on output file");
++ if (size > Cflag)
++ {
++#ifdef HAVE_CAPSICUM
++ FILE *fp;
++ int fd;
++#endif
++
++ /*
++ * Close the current file and open a new one.
++ */
++ pcap_dump_close(dump_info->pdd);
++
++ /*
++ * Compress the file we just closed, if the user
++ * asked for it.
++ */
++ if (zflag != NULL)
++ compress_savefile(dump_info->CurrentFileName);
++
++ Cflag_count++;
++ if (Wflag > 0)
++ {
++ if (Cflag_count >= Wflag)
++ Cflag_count = 0;
++ }
++ if (dump_info->CurrentFileName != NULL)
++ free(dump_info->CurrentFileName);
++ dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
++ if (dump_info->CurrentFileName == NULL)
++ error("%s: malloc", __func__);
++ MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
++#ifdef HAVE_LIBCAP_NG
++ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
++ capng_apply(CAPNG_SELECT_BOTH);
++#endif /* HAVE_LIBCAP_NG */
++#ifdef HAVE_CAPSICUM
++ fd = openat(dump_info->dirfd, dump_info->CurrentFileName,
++ O_CREAT | O_WRONLY | O_TRUNC, 0644);
++ if (fd < 0)
++ {
++ error("unable to open file %s",
++ dump_info->CurrentFileName);
++ }
++ fp = fdopen(fd, "w");
++ if (fp == NULL)
++ {
++ error("unable to fdopen file %s",
++ dump_info->CurrentFileName);
++ }
++ dump_info->pdd = pcap_dump_fopen(dump_info->pd, fp);
++#else /* !HAVE_CAPSICUM */
++ dump_info->pdd = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
++#endif
++#ifdef HAVE_LIBCAP_NG
++ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
++ capng_apply(CAPNG_SELECT_BOTH);
++#endif /* HAVE_LIBCAP_NG */
++ if (dump_info->pdd == NULL)
++ error("%s", pcap_geterr(pd));
++#ifdef HAVE_CAPSICUM
++ set_dumper_capsicum_rights(dump_info->pdd);
++#endif
++ }
++ }
++
++ pcap_dump((u_char *)dump_info->pdd, h, sp);
++#ifdef HAVE_PCAP_DUMP_FLUSH
++ if (Uflag)
++ pcap_dump_flush(dump_info->pdd);
++#endif
++
++ if (dump_info->ndo != NULL)
++ pretty_print_packet(dump_info->ndo, h, sp, packets_captured);
++
++ --infodelay;
++ if (infoprint)
++ info(0);
++}
++
++//#include "stellar/packet_manager.h"
++#include "packet_manager/packet_internal.h"
++#include "packet_manager/packet_parser.h"
++#include "stellar/log.h"
++#include "stellar/exdata.h"
++
++// fake variable and function
++__thread struct logger *__thread_local_logger = NULL;
++int exdata_set(struct exdata_runtime *rt, int idx, void *ex_ptr)
++{
++ return 0;
++}
++void *exdata_get(struct exdata_runtime *rt, int idx)
++{
++ return NULL;
++}
++int log_check_level(struct logger *logger, enum log_level level)
++{
++ return 0;
++}
++void log_print(struct logger *logger, enum log_level level, const char *module, const char *fmt, ...)
++{
++ return;
++}
++/*
++ * 0: not match
++ * not 0: match
++ */
++static int packet_filter_greedy(const struct pcap_pkthdr *h, const u_char *sp)
++{
++ if (NULL == cmdbuf || fcode.bf_len == 0)
++ {
++ return 1;
++ }
++
++ struct packet _pkt = {};
++ if (NULL == packet_parse(&_pkt, (char *)sp, h->caplen))
++ {
++ return 0;
++ }
++ struct packet *pkt = &_pkt;
++ const char *raw_pkt_hdr = packet_get_raw_data(pkt);
++ int raw_pkt_len = (int)packet_get_raw_len(pkt);
++ int pkt_layer = packet_get_layer_count(pkt);
++ int match = 0;
++ for (int i = 0; i < pkt_layer; i++)
++ {
++ const struct layer *layer = packet_get_layer_by_idx(pkt, i);
++ if (layer->proto == LAYER_PROTO_IPV4 || layer->proto == LAYER_PROTO_IPV6)
++ {
++ int remain_len = raw_pkt_len - (layer->hdr.raw - raw_pkt_hdr);
++ if (remain_len <= 0)
++ {
++ continue;
++ }
++ match = bpf_filter(fcode.bf_insns, (unsigned char *)layer->hdr.raw, remain_len, remain_len);
++ if (match)
++ {
++ break;
++ }
++ }
++ }
++ return match;
++}
++
++void dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
++{
++ struct dump_info *dump_info;
++ if ((g_stellar_dump_arg.greedy_seek != 0) && packet_filter_greedy(h, sp) == 0)
++ {
++ return;
++ }
++
++ ++packets_captured;
++
++ ++infodelay;
++
++ dump_info = (struct dump_info *)user;
++
++ pcap_dump((u_char *)dump_info->pdd, h, sp);
++#ifdef HAVE_PCAP_DUMP_FLUSH
++ if (Uflag)
++ pcap_dump_flush(dump_info->pdd);
++#endif
++
++ if (dump_info->ndo != NULL)
++ pretty_print_packet(dump_info->ndo, h, sp, packets_captured);
++
++ --infodelay;
++ if (infoprint)
++ info(0);
++}
++
++void print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
++{
++ if ((g_stellar_dump_arg.greedy_seek != 0) && packet_filter_greedy(h, sp) == 0)
++ {
++ return;
++ }
++ ++packets_captured;
++
++ ++infodelay;
++
++ if (!count_mode)
++ pretty_print_packet((netdissect_options *)user, h, sp, packets_captured);
++
++ --infodelay;
++ if (infoprint)
++ info(0);
++}
++
++#ifdef SIGNAL_REQ_INFO
++static void
++requestinfo(int signo _U_)
++{
++ if (infodelay)
++ ++infoprint;
++ else
++ info(0);
++}
++#endif
++
++#ifdef SIGNAL_FLUSH_PCAP
++static void
++flushpcap(int signo _U_)
++{
++ if (pdd != NULL)
++ pcap_dump_flush(pdd);
++}
++#endif
++
++static void
++print_packets_captured(void)
++{
++ static u_int prev_packets_captured, first = 1;
++
++ if (infodelay == 0 && (first || packets_captured != prev_packets_captured))
++ {
++ fprintf(stderr, "Got %u\r", packets_captured);
++ first = 0;
++ prev_packets_captured = packets_captured;
++ }
++}
++
++/*
++ * Called once each second in verbose mode while dumping to file
++ */
++#ifdef _WIN32
++static void CALLBACK verbose_stats_dump(PVOID param _U_,
++ BOOLEAN timer_fired _U_)
++{
++ print_packets_captured();
++}
++#else /* _WIN32 */
++static void verbose_stats_dump(int sig _U_)
++{
++ print_packets_captured();
++}
++#endif /* _WIN32 */
++
++DIAG_OFF_DEPRECATION
++static void
++print_version(FILE *f)
++{
++#ifndef HAVE_PCAP_LIB_VERSION
++#ifdef HAVE_PCAP_VERSION
++ extern char pcap_version[];
++#else /* HAVE_PCAP_VERSION */
++ static char pcap_version[] = "unknown";
++#endif /* HAVE_PCAP_VERSION */
++#endif /* HAVE_PCAP_LIB_VERSION */
++ const char *smi_version_string;
++
++ (void)fprintf(f, "%s version " PACKAGE_VERSION "\n", program_name);
++#ifdef HAVE_PCAP_LIB_VERSION
++ (void)fprintf(f, "%s\n", pcap_lib_version());
++#else /* HAVE_PCAP_LIB_VERSION */
++ (void)fprintf(f, "libpcap version %s\n", pcap_version);
++#endif /* HAVE_PCAP_LIB_VERSION */
++
++#if defined(HAVE_LIBCRYPTO) && defined(SSLEAY_VERSION)
++ (void)fprintf(f, "%s\n", SSLeay_version(SSLEAY_VERSION));
++#endif
++
++ smi_version_string = nd_smi_version_string();
++ if (smi_version_string != NULL)
++ (void)fprintf(f, "SMI-library: %s\n", smi_version_string);
++
++#if defined(__SANITIZE_ADDRESS__)
++ (void)fprintf(f, "Compiled with AddressSanitizer/GCC.\n");
++#elif defined(__has_feature)
++#if __has_feature(address_sanitizer)
++ (void)fprintf(f, "Compiled with AddressSanitizer/Clang.\n");
++#elif __has_feature(memory_sanitizer)
++ (void)fprintf(f, "Compiled with MemorySanitizer/Clang.\n");
++#endif
++#endif /* __SANITIZE_ADDRESS__ or __has_feature */
++}
++DIAG_ON_DEPRECATION
++
++static void
++print_usage(FILE *f)
++{
++ print_version(f);
++ (void)fprintf(f,
++ "Usage: %s [-Abd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ] [--count]\n", program_name);
++ (void)fprintf(f,
++ "\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n");
++#if STELLAR_DUMP == 0
++ (void)fprintf(f, "\t\t[ -i interface ]" IMMEDIATE_MODE_USAGE j_FLAG_USAGE "\n");
++#endif
++#ifdef HAVE_PCAP_FINDALLDEVS_EX
++ (void)fprintf(f,
++ "\t\t" LIST_REMOTE_INTERFACES_USAGE "\n");
++#endif
++#ifdef USE_LIBSMI
++ (void)fprintf(f,
++ "\t\t" m_FLAG_USAGE "\n");
++#endif
++
++ (void)fprintf(f, "\t\t[ -M secret ] [ --number ] [ --print ]" Q_FLAG_USAGE "\n");
++ (void)fprintf(f,
++ "\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]\n");
++ (void)fprintf(f,
++ "\t\t[ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ]\n");
++#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
++ (void)fprintf(f,
++ "\t\t[ --time-stamp-precision precision ] [ --micro ] [ --nano ]\n");
++#endif
++ (void)fprintf(f,
++ "\t\t[ -z postrotate-command ] [ -Z user ] [ expression ]\n");
++#if STELLAR_DUMP
++ (void)fprintf(f, "\n --- \033[1m%s %s ---\033[0m\n", "The following options are customized for", PROGRAM_NAME);
++ (void)fprintf(f, "\t\t[ -i ] not interface, is stellar monitor ip (default: 127.0.0.1)\n");
++ (void)fprintf(f, "\t\t[ -P ] stellar monitor port (default: 80)\n");
++ (void)fprintf(f, "\t\t[ -g ] enable greedy seek to most inner IP layer for tunnel protocol (default: disable)\n");
++ (void)fprintf(f, "\t\t[ -k ] which threads enable packet dump, support comma-separated values, for example: 1,3,5 (default: all threads)\n");
++#endif
++}
+
+- netdissect_options Ndo;
+- netdissect_options *ndo = &Ndo;
++// static netdissect_options Ndo;
++// static netdissect_options *ndo = &Ndo;
++// static struct dump_info dumpinfo;
++// u_char *pcap_userdata;
++// static char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName;
++// pcap_handler callback;
++// static int print = 0;
++// static FILE *VFile;
++// static char VFileLine[PATH_MAX + 1];
++// static char ebuf[PCAP_ERRBUF_SIZE];
++// int dlt, i;
++// static char *endp;
++// static int cnt, op;
++// static bpf_u_int32 localnet = 0, netmask = 0;
++
++// static int Oflag = 1; /* run filter code optimizer */
++// static int yflag_dlt = -1;
++// static const char *yflag_dlt_name = NULL;
++
++// static const char *dlt_name;
++// static struct bpf_program fcode;
++// #ifndef _WIN32
++// static void (*oldhandler)(int);
++// #endif
++
++// static const char *username = NULL;
++// #ifndef _WIN32
++// static const char *chroot_dir = NULL;
++// #endif
++// static char *ret = NULL;
++// static char *end;
++// #ifdef HAVE_PCAP_FINDALLDEVS
++// static pcap_if_t *devlist;
++// static long devnum;
++// #endif
++// static int status;
++
++// #ifdef HAVE_CAPSICUM
++// static cap_rights_t rights;
++// static int cansandbox;
++// #endif /* HAVE_CAPSICUM */
+
++// static int argc;
++// static char **argv;
++
++int tcpdump_parse_args(int f_argc, char **f_argv)
++{
++ argc = f_argc;
++ argv = f_argv;
+ /*
+ * Initialize the netdissect code.
+ */
+@@ -1558,9 +2396,9 @@
+ if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0)
+ error("%s", ebuf);
+
+- while (
+- (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1)
+- switch (op) {
++ while ((op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1)
++ switch (op)
++ {
+
+ case 'a':
+ /* compatibility for old -a */
+@@ -1576,7 +2414,7 @@
+
+ #if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
+ case 'B':
+- Bflag = atoi(optarg)*1024;
++ Bflag = atoi(optarg) * 1024;
+ if (Bflag <= 0)
+ error("invalid packet buffer size %s", optarg);
+ break;
+@@ -1586,6 +2424,7 @@
+ cnt = atoi(optarg);
+ if (cnt <= 0)
+ error("invalid packet count %s", optarg);
++ g_stellar_dump_arg.expect_packet_count = cnt;
+ break;
+
+ case 'C':
+@@ -1595,12 +2434,11 @@
+ #else
+ Cflag = strtol(optarg, &endp, 10);
+ #endif
+- if (endp == optarg || *endp != '\0' || errno != 0
+- || Cflag <= 0)
++ if (endp == optarg || *endp != '\0' || errno != 0 || Cflag <= 0)
+ error("invalid file size %s", optarg);
+- /*
+- * Will multiplying it by 1000000 overflow?
+- */
++ /*
++ * Will multiplying it by 1000000 overflow?
++ */
+ #ifdef HAVE_PCAP_DUMP_FTELL64
+ if (Cflag > INT64_T_CONSTANT(0x7fffffffffffffff) / 1000000)
+ #else
+@@ -1649,18 +2487,24 @@
+ infile = optarg;
+ break;
+
++#if STELLAR_DUMP
++ case 'g':
++ g_stellar_dump_arg.greedy_seek = 1;
++ break;
++#endif
+ case 'G':
+ Gflag = atoi(optarg);
+ if (Gflag < 0)
+ error("invalid number of seconds %s", optarg);
+
+- /* We will create one file initially. */
+- Gflag_count = 0;
++ /* We will create one file initially. */
++ Gflag_count = 0;
+
+ /* Grab the current time for rotation use. */
+- if ((Gflag_time = time(NULL)) == (time_t)-1) {
++ if ((Gflag_time = time(NULL)) == (time_t)-1)
++ {
+ error("%s: can't get current time: %s",
+- __func__, pcap_strerror(errno));
++ __func__, pcap_strerror(errno));
+ }
+ break;
+
+@@ -1674,8 +2518,20 @@
+ break;
+
+ case 'i':
++ {
++#if STELLAR_DUMP
++ uint32_t tmp_ip;
++ g_stellar_dump_arg.stellar_server_ip = optarg;
++ if (inet_pton(AF_INET, g_stellar_dump_arg.stellar_server_ip, &tmp_ip) != 1)
++ {
++ printf("invalid ip address %s\n", g_stellar_dump_arg.stellar_server_ip);
++ exit(1);
++ }
++#else
+ device = optarg;
+- break;
++#endif
++ }
++ break;
+
+ #ifdef HAVE_PCAP_CREATE
+ case 'I':
+@@ -1717,17 +2573,38 @@
+ lflag = 1;
+ break;
+
++#if STELLAR_DUMP
++ case 'k':
++ {
++ const char *ptr = optarg;
++ size_t len = strlen(optarg);
++ for (i = 0; i < (int)len; i++)
++ {
++ if (isxdigit(ptr[i]) == 0 && ptr[i] != ',')
++ {
++ printf("invalid threads expression: %s\n", optarg);
++ exit(1);
++ }
++ }
++ g_stellar_dump_arg.thread_enable_expression = optarg;
++ }
++ break;
++#endif
++
+ case 'K':
+ ++ndo->ndo_Kflag;
+ break;
+
+ case 'm':
+- if (nd_have_smi_support()) {
++ if (nd_have_smi_support())
++ {
+ if (nd_load_smi_module(optarg, ebuf, sizeof(ebuf)) == -1)
+ error("%s", ebuf);
+- } else {
++ }
++ else
++ {
+ (void)fprintf(stderr, "%s: ignoring option `-m %s' ",
+- program_name, optarg);
++ program_name, optarg);
+ (void)fprintf(stderr, "(no libsmi support)\n");
+ }
+ break;
+@@ -1756,6 +2633,19 @@
+ ++pflag;
+ break;
+
++#if STELLAR_DUMP
++ case 'P':
++ {
++ int tmp_port = atoi(optarg);
++ if (tmp_port <= 0 || tmp_port > 65535)
++ {
++ printf("invalid port %s\n", optarg);
++ exit(1);
++ }
++ g_stellar_dump_arg.stellar_server_port_host_order = (unsigned short)tmp_port;
++ }
++ break;
++#endif
+ case 'q':
+ ++ndo->ndo_qflag;
+ ++ndo->ndo_suppress_default_print;
+@@ -1763,6 +2653,7 @@
+
+ #ifdef HAVE_PCAP_SETDIRECTION
+ case 'Q':
++#if STELLAR_DUMP == 0
+ if (ascii_strcasecmp(optarg, "in") == 0)
+ Qflag = PCAP_D_IN;
+ else if (ascii_strcasecmp(optarg, "out") == 0)
+@@ -1771,6 +2662,10 @@
+ Qflag = PCAP_D_INOUT;
+ else
+ error("unknown capture direction `%s'", optarg);
++#else
++ fprintf(stderr, "unsupport '-Q' option for %s\n", PROGRAM_NAME);
++ return -1;
++#endif
+ break;
+ #endif /* HAVE_PCAP_SETDIRECTION */
+
+@@ -1780,10 +2675,9 @@
+
+ case 's':
+ ndo->ndo_snaplen = (int)strtol(optarg, &end, 0);
+- if (optarg == end || *end != '\0'
+- || ndo->ndo_snaplen < 0 || ndo->ndo_snaplen > MAXIMUM_SNAPLEN)
++ if (optarg == end || *end != '\0' || ndo->ndo_snaplen < 0 || ndo->ndo_snaplen > MAXIMUM_SNAPLEN)
+ error("invalid snaplen %s (must be >= 0 and <= %d)",
+- optarg, MAXIMUM_SNAPLEN);
++ optarg, MAXIMUM_SNAPLEN);
+ break;
+
+ case 'S':
+@@ -1888,11 +2782,11 @@
+
+ #ifdef HAVE_PCAP_SET_PARSER_DEBUG
+ case 'Y':
+- {
++ {
+ /* Undocumented flag */
+ pcap_set_parser_debug(1);
+- }
+- break;
++ }
++ break;
+ #endif
+ case 'z':
+ zflag = optarg;
+@@ -1970,15 +2864,31 @@
+ #endif
+
+ #if defined(DLT_LINUX_SLL2) && defined(HAVE_PCAP_SET_DATALINK)
+-/* Set default linktype DLT_LINUX_SLL2 when capturing on the "any" device */
+- if (device != NULL &&
+- strncmp (device, "any", strlen("any")) == 0
+- && yflag_dlt == -1)
+- yflag_dlt = DLT_LINUX_SLL2;
++ /* Set default linktype DLT_LINUX_SLL2 when capturing on the "any" device */
++ if (device != NULL &&
++ strncmp(device, "any", strlen("any")) == 0 && yflag_dlt == -1)
++ yflag_dlt = DLT_LINUX_SLL2;
+ #endif
+
+- switch (ndo->ndo_tflag) {
++#if STELLAR_DUMP
++ /* set default value */
++ if (g_stellar_dump_arg.stellar_server_ip == NULL)
++ {
++ g_stellar_dump_arg.stellar_server_ip = CTRLLINK_DEFAULT_IP;
++ }
++ if (g_stellar_dump_arg.stellar_server_port_host_order == 0)
++ {
++ g_stellar_dump_arg.stellar_server_port_host_order = CTRLLINK_DEFAULT_PORT;
++ }
++#endif
++ return 0;
++}
+
++int tcpdump_prepare(void)
++{
++ char *ret;
++ switch (ndo->ndo_tflag)
++ {
+ case 0: /* Default */
+ case 1: /* No time stamp */
+ case 2: /* Unix timeval style */
+@@ -2013,7 +2923,8 @@
+
+ #ifdef WITH_CHROOT
+ /* if run as root, prepare for chrooting */
+- if (getuid() == 0 || geteuid() == 0) {
++ if (getuid() == 0 || geteuid() == 0)
++ {
+ /* future extensibility for cmd-line arguments */
+ if (!chroot_dir)
+ chroot_dir = WITH_CHROOT;
+@@ -2022,14 +2933,16 @@
+
+ #ifdef WITH_USER
+ /* if run as root, prepare for dropping root privileges */
+- if (getuid() == 0 || geteuid() == 0) {
++ if (getuid() == 0 || geteuid() == 0)
++ {
+ /* Run with '-Z root' to restore old behaviour */
+ if (!username)
+ username = WITH_USER;
+ }
+ #endif
+
+- if (RFileName != NULL || VFileName != NULL) {
++ if (RFileName != NULL || VFileName != NULL)
++ {
+ /*
+ * If RFileName is non-null, it's the pathname of a
+ * savefile to read. If VFileName is non-null, it's
+@@ -2049,10 +2962,11 @@
+ * people's trace files (especially if we're set-UID
+ * root).
+ */
+- if (setgid(getgid()) != 0 || setuid(getuid()) != 0 )
++ if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
+ fprintf(stderr, "Warning: setgid/setuid failed !\n");
+ #endif /* _WIN32 */
+- if (VFileName != NULL) {
++ if (VFileName != NULL)
++ {
+ if (VFileName[0] == '-' && VFileName[1] == '\0')
+ VFile = stdin;
+ else
+@@ -2069,7 +2983,7 @@
+
+ #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ pd = pcap_open_offline_with_tstamp_precision(RFileName,
+- ndo->ndo_tstamp_precision, ebuf);
++ ndo->ndo_tstamp_precision, ebuf);
+ #else
+ pd = pcap_open_offline(RFileName, ebuf);
+ #endif
+@@ -2079,25 +2993,31 @@
+ #ifdef HAVE_CAPSICUM
+ cap_rights_init(&rights, CAP_READ);
+ if (cap_rights_limit(fileno(pcap_file(pd)), &rights) < 0 &&
+- errno != ENOSYS) {
++ errno != ENOSYS)
++ {
+ error("unable to limit pcap descriptor");
+ }
+ #endif
+ dlt = pcap_datalink(pd);
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ fprintf(stderr, "reading from file %s", RFileName);
+- if (dlt_name == NULL) {
+- fprintf(stderr, ", link-type %u", dlt);
+- } else {
++ if (dlt_name == NULL)
++ {
++ fprintf(stderr, ", link-type %d", dlt);
++ }
++ else
++ {
+ fprintf(stderr, ", link-type %s (%s)", dlt_name,
+- pcap_datalink_val_to_description(dlt));
++ pcap_datalink_val_to_description(dlt));
+ }
+ fprintf(stderr, ", snapshot length %d\n", pcap_snapshot(pd));
+ #ifdef DLT_LINUX_SLL2
+ if (dlt == DLT_LINUX_SLL2)
+ fprintf(stderr, "Warning: interface names might be incorrect\n");
+ #endif
+- } else if (dflag && !device) {
++ }
++ else if (dflag && !device)
++ {
+ int dump_dlt = DLT_EN10MB;
+ /*
+ * We're dumping the compiled code without an explicit
+@@ -2119,12 +3039,16 @@
+ dump_dlt = yflag_dlt;
+ else
+ fprintf(stderr, "Warning: assuming Ethernet\n");
+- pd = pcap_open_dead(dump_dlt, ndo->ndo_snaplen);
+- } else {
++ pd = pcap_open_dead(dump_dlt, ndo->ndo_snaplen);
++ }
++ else
++ {
+ /*
+ * We're doing a live capture.
+ */
+- if (device == NULL) {
++ if (device == NULL)
++ {
++#if STELLAR_DUMP == 0
+ /*
+ * No interface was specified. Pick one.
+ */
+@@ -2148,13 +3072,21 @@
+ if (device == NULL)
+ error("%s", ebuf);
+ #endif
++#else
++ device = strdup("lo");
++#endif
+ }
+
+ /*
+ * Try to open the interface with the specified name.
+ */
++#if STELLAR_DUMP == 0
+ pd = open_interface(device, ndo, ebuf);
+- if (pd == NULL) {
++#else
++ pd = pcap_open_dead(DLT_EN10MB, 65535);
++#endif
++ if (pd == NULL)
++ {
+ /*
+ * That failed. If we can get a list of
+ * interfaces, and the interface name
+@@ -2164,7 +3096,8 @@
+ */
+ #ifdef HAVE_PCAP_FINDALLDEVS
+ devnum = parse_interface_number(device);
+- if (devnum == -1) {
++ if (devnum == -1)
++ {
+ /*
+ * It's not a number; just report
+ * the open error and fail.
+@@ -2184,7 +3117,7 @@
+ pd = open_interface(device, ndo, ebuf);
+ if (pd == NULL)
+ error("%s", ebuf);
+-#else /* HAVE_PCAP_FINDALLDEVS */
++#else /* HAVE_PCAP_FINDALLDEVS */
+ /*
+ * We can't get a list of interfaces; just
+ * fail.
+@@ -2202,14 +3135,16 @@
+ fprintf(stderr, "Warning: setgid/setuid failed !\n");
+ #endif /* _WIN32 */
+ #if !defined(HAVE_PCAP_CREATE) && defined(_WIN32)
+- if(Bflag != 0)
+- if(pcap_setbuff(pd, Bflag)==-1){
++ if (Bflag != 0)
++ if (pcap_setbuff(pd, Bflag) == -1)
++ {
+ error("%s", pcap_geterr(pd));
+ }
+ #endif /* !defined(HAVE_PCAP_CREATE) && defined(_WIN32) */
+ if (Lflag)
+ show_dlts_and_exit(pd, device);
+- if (yflag_dlt >= 0) {
++ if (yflag_dlt >= 0)
++ {
+ #ifdef HAVE_PCAP_SET_DATALINK
+ if (pcap_set_datalink(pd, yflag_dlt) < 0)
+ error("%s", pcap_geterr(pd));
+@@ -2219,31 +3154,36 @@
+ * data link type, so we only let them
+ * set it to what it already is.
+ */
+- if (yflag_dlt != pcap_datalink(pd)) {
++ if (yflag_dlt != pcap_datalink(pd))
++ {
+ error("%s is not one of the DLTs supported by this device\n",
+- yflag_dlt_name);
++ yflag_dlt_name);
+ }
+ #endif
+ (void)fprintf(stderr, "%s: data link type %s\n",
+- program_name,
+- pcap_datalink_val_to_name(yflag_dlt));
++ program_name,
++ pcap_datalink_val_to_name(yflag_dlt));
+ (void)fflush(stderr);
+ }
+ i = pcap_snapshot(pd);
+- if (ndo->ndo_snaplen < i) {
++ if (ndo->ndo_snaplen < i)
++ {
+ if (ndo->ndo_snaplen != 0)
+ warning("snaplen raised from %d to %d", ndo->ndo_snaplen, i);
+ ndo->ndo_snaplen = i;
+- } else if (ndo->ndo_snaplen > i) {
++ }
++ else if (ndo->ndo_snaplen > i)
++ {
+ warning("snaplen lowered from %d to %d", ndo->ndo_snaplen, i);
+ ndo->ndo_snaplen = i;
+ }
+- if(ndo->ndo_fflag != 0) {
+- if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) {
+- warning("foreign (-f) flag used but: %s", ebuf);
+- }
+- }
+-
++ if (ndo->ndo_fflag != 0)
++ {
++ if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0)
++ {
++ warning("foreign (-f) flag used but: %s", ebuf);
++ }
++ }
+ }
+ if (infile)
+ cmdbuf = read_infile(infile);
+@@ -2253,9 +3193,32 @@
+ #ifdef HAVE_PCAP_SET_OPTIMIZER_DEBUG
+ pcap_set_optimizer_debug(dflag);
+ #endif
+- if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
+- error("%s", pcap_geterr(pd));
+- if (dflag) {
++
++ if (cmdbuf != NULL)
++ {
++ int compile_ret;
++ if (RFileName != NULL)
++ {
++ if (g_stellar_dump_arg.greedy_seek == 0)
++ {
++ compile_ret = pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask);
++ }
++ else
++ {
++ compile_ret = pcap_compile_nopcap(65535, DLT_RAW, &fcode, cmdbuf, Oflag, netmask);
++ }
++ }
++ else
++ {
++ compile_ret = pcap_compile_nopcap(65535, DLT_RAW, &fcode, cmdbuf, Oflag, netmask);
++ }
++ if (compile_ret < 0)
++ {
++ error("pcap_compile: %s", pcap_geterr(pd));
++ }
++ }
++ if (dflag)
++ {
+ bpf_dump(&fcode, dflag);
+ pcap_close(pd);
+ free(cmdbuf);
+@@ -2263,10 +3226,14 @@
+ exit_tcpdump(S_SUCCESS);
+ }
+
++#if STELLAR_DUMP
++ g_stellar_dump_arg.bpf = cmdbuf;
++#endif
++
+ #ifdef HAVE_CASPER
+ if (!ndo->ndo_nflag)
+ capdns = capdns_setup();
+-#endif /* HAVE_CASPER */
++#endif /* HAVE_CASPER */
+
+ init_print(ndo, localnet, netmask);
+
+@@ -2303,52 +3270,60 @@
+ * savefile doesn't handle the general case.
+ */
+
+- if (getuid() == 0 || geteuid() == 0) {
++ if (getuid() == 0 || geteuid() == 0)
++ {
+ #ifdef HAVE_LIBCAP_NG
+ /* Initialize capng */
+ capng_clear(CAPNG_SELECT_BOTH);
+- if (username) {
+-DIAG_OFF_ASSIGN_ENUM
++ if (username)
++ {
++ DIAG_OFF_ASSIGN_ENUM
+ capng_updatev(
+ CAPNG_ADD,
+ CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+ CAP_SETUID,
+ CAP_SETGID,
+ -1);
+-DIAG_ON_ASSIGN_ENUM
++ DIAG_ON_ASSIGN_ENUM
+ }
+- if (chroot_dir) {
+-DIAG_OFF_ASSIGN_ENUM
++ if (chroot_dir)
++ {
++ DIAG_OFF_ASSIGN_ENUM
+ capng_update(
+ CAPNG_ADD,
+ CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+- CAP_SYS_CHROOT
+- );
+-DIAG_ON_ASSIGN_ENUM
++ CAP_SYS_CHROOT);
++ DIAG_ON_ASSIGN_ENUM
+ }
+
+- if (WFileName) {
+-DIAG_OFF_ASSIGN_ENUM
++ if (WFileName)
++ {
++ DIAG_OFF_ASSIGN_ENUM
+ capng_update(
+ CAPNG_ADD,
+ CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+- CAP_DAC_OVERRIDE
+- );
+-DIAG_ON_ASSIGN_ENUM
++ CAP_DAC_OVERRIDE);
++ DIAG_ON_ASSIGN_ENUM
+ }
+ capng_apply(CAPNG_SELECT_BOTH);
+ #endif /* HAVE_LIBCAP_NG */
+ if (username || chroot_dir)
+ droproot(username, chroot_dir);
+-
+ }
+ #endif /* _WIN32 */
+
+- if (pcap_setfilter(pd, &fcode) < 0)
+- error("%s", pcap_geterr(pd));
++ if (RFileName)
++ {
++ if ((pd != NULL) && (cmdbuf != NULL) && (0 == g_stellar_dump_arg.greedy_seek) && pcap_setfilter(pd, &fcode) < 0)
++ {
++ error("%s", pcap_geterr(pd));
++ return -1;
++ }
++ }
+ #ifdef HAVE_CAPSICUM
+- if (RFileName == NULL && VFileName == NULL && pcap_fileno(pd) != -1) {
+- static const unsigned long cmds[] = { BIOCGSTATS, BIOCROTZBUF };
++ if (RFileName == NULL && VFileName == NULL && pcap_fileno(pd) != -1)
++ {
++ static const unsigned long cmds[] = {BIOCGSTATS, BIOCROTZBUF};
+
+ /*
+ * The various libpcap devices use a combination of
+@@ -2357,16 +3332,20 @@
+ */
+ cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_EVENT);
+ if (cap_rights_limit(pcap_fileno(pd), &rights) < 0 &&
+- errno != ENOSYS) {
++ errno != ENOSYS)
++ {
+ error("unable to limit pcap descriptor");
+ }
+ if (cap_ioctls_limit(pcap_fileno(pd), cmds,
+- sizeof(cmds) / sizeof(cmds[0])) < 0 && errno != ENOSYS) {
++ sizeof(cmds) / sizeof(cmds[0])) < 0 &&
++ errno != ENOSYS)
++ {
+ error("unable to limit ioctls on pcap descriptor");
+ }
+ }
+ #endif
+- if (WFileName) {
++ if (WFileName)
++ {
+ /* Do not exceed the default PATH_MAX for files. */
+ dumpinfo.CurrentFileName = (char *)malloc(PATH_MAX + 1);
+
+@@ -2375,9 +3354,9 @@
+
+ /* We do not need numbering for dumpfiles if Cflag isn't set. */
+ if (Cflag != 0)
+- MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, WflagChars);
++ MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, WflagChars);
+ else
+- MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0);
++ MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0);
+
+ pdd = pcap_dump_open(pd, dumpinfo.CurrentFileName);
+ #ifdef HAVE_LIBCAP_NG
+@@ -2387,10 +3366,8 @@
+ */
+ capng_update(
+ CAPNG_DROP,
+- (Cflag || Gflag ? 0 : CAPNG_PERMITTED)
+- | CAPNG_EFFECTIVE,
+- CAP_DAC_OVERRIDE
+- );
++ (Cflag || Gflag ? 0 : CAPNG_PERMITTED) | CAPNG_EFFECTIVE,
++ CAP_DAC_OVERRIDE);
+ capng_apply(CAPNG_SELECT_BOTH);
+ #endif /* HAVE_LIBCAP_NG */
+ if (pdd == NULL)
+@@ -2398,7 +3375,8 @@
+ #ifdef HAVE_CAPSICUM
+ set_dumper_capsicum_rights(pdd);
+ #endif
+- if (Cflag != 0 || Gflag != 0) {
++ if (Cflag != 0 || Gflag != 0)
++ {
+ #ifdef HAVE_CAPSICUM
+ /*
+ * basename() and dirname() may modify their input buffer
+@@ -2409,71 +3387,83 @@
+ */
+ char *WFileName_copy;
+
+- if ((WFileName_copy = strdup(WFileName)) == NULL) {
++ if ((WFileName_copy = strdup(WFileName)) == NULL)
++ {
+ error("Unable to allocate memory for file %s",
+- WFileName);
++ WFileName);
+ }
+ DIAG_OFF_C11_EXTENSIONS
+ dumpinfo.WFileName = strdup(basename(WFileName_copy));
+ DIAG_ON_C11_EXTENSIONS
+- if (dumpinfo.WFileName == NULL) {
++ if (dumpinfo.WFileName == NULL)
++ {
+ error("Unable to allocate memory for file %s",
+- WFileName);
++ WFileName);
+ }
+ free(WFileName_copy);
+
+- if ((WFileName_copy = strdup(WFileName)) == NULL) {
++ if ((WFileName_copy = strdup(WFileName)) == NULL)
++ {
+ error("Unable to allocate memory for file %s",
+- WFileName);
++ WFileName);
+ }
+ DIAG_OFF_C11_EXTENSIONS
+ char *WFileName_dirname = dirname(WFileName_copy);
+ DIAG_ON_C11_EXTENSIONS
+ dumpinfo.dirfd = open(WFileName_dirname,
+- O_DIRECTORY | O_RDONLY);
+- if (dumpinfo.dirfd < 0) {
++ O_DIRECTORY | O_RDONLY);
++ if (dumpinfo.dirfd < 0)
++ {
+ error("unable to open directory %s",
+- WFileName_dirname);
++ WFileName_dirname);
+ }
+ free(WFileName_dirname);
+ free(WFileName_copy);
+
+ cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL,
+- CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE);
++ CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE);
+ if (cap_rights_limit(dumpinfo.dirfd, &rights) < 0 &&
+- errno != ENOSYS) {
++ errno != ENOSYS)
++ {
+ error("unable to limit directory rights");
+ }
+ if (cap_fcntls_limit(dumpinfo.dirfd, CAP_FCNTL_GETFL) < 0 &&
+- errno != ENOSYS) {
++ errno != ENOSYS)
++ {
+ error("unable to limit dump descriptor fcntls");
+ }
+-#else /* !HAVE_CAPSICUM */
++#else /* !HAVE_CAPSICUM */
+ dumpinfo.WFileName = WFileName;
+ #endif
+ callback = dump_packet_and_trunc;
+ dumpinfo.pd = pd;
+ dumpinfo.pdd = pdd;
+ pcap_userdata = (u_char *)&dumpinfo;
+- } else {
++ }
++ else
++ {
+ callback = dump_packet;
+ dumpinfo.WFileName = WFileName;
+ dumpinfo.pd = pd;
+ dumpinfo.pdd = pdd;
+ pcap_userdata = (u_char *)&dumpinfo;
+ }
+- if (print) {
++ if (print)
++ {
+ dlt = pcap_datalink(pd);
+ ndo->ndo_if_printer = get_if_printer(dlt);
+ dumpinfo.ndo = ndo;
+- } else
++ }
++ else
+ dumpinfo.ndo = NULL;
+
+ #ifdef HAVE_PCAP_DUMP_FLUSH
+ if (Uflag)
+ pcap_dump_flush(pdd);
+ #endif
+- } else {
++ }
++ else
++ {
+ dlt = pcap_datalink(pd);
+ ndo->ndo_if_printer = get_if_printer(dlt);
+ callback = print_packet;
+@@ -2492,7 +3482,8 @@
+ (void)setsignal(SIGNAL_FLUSH_PCAP, flushpcap);
+ #endif
+
+- if (ndo->ndo_vflag > 0 && WFileName && RFileName == NULL && !print) {
++ if (ndo->ndo_vflag > 0 && WFileName && RFileName == NULL && !print)
++ {
+ /*
+ * When capturing to a file, if "--print" wasn't specified,
+ *"-v" means tcpdump should, once per second,
+@@ -2511,10 +3502,10 @@
+ * that printing the stats could be a "long wait".
+ */
+ CreateTimerQueueTimer(&timer_handle, NULL,
+- verbose_stats_dump, NULL, 1000, 1000,
+- WT_EXECUTEDEFAULT|WT_EXECUTELONGFUNCTION);
++ verbose_stats_dump, NULL, 1000, 1000,
++ WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION);
+ setvbuf(stderr, NULL, _IONBF, 0);
+-#else /* _WIN32 */
++#else /* _WIN32 */
+ /*
+ * Assume this is UN*X, and that it has setitimer(); that
+ * dates back to UNIX 95.
+@@ -2529,30 +3520,38 @@
+ #endif /* _WIN32 */
+ }
+
+- if (RFileName == NULL) {
++#if 0 == STELLAR_DUMP
++ if (RFileName == NULL)
++ {
+ /*
+ * Live capture (if -V was specified, we set RFileName
+ * to a file from the -V file). Print a message to
+ * the standard error on UN*X.
+ */
+- if (!ndo->ndo_vflag && !WFileName) {
++ if (!ndo->ndo_vflag && !WFileName)
++ {
+ (void)fprintf(stderr,
+- "%s: verbose output suppressed, use -v[v]... for full protocol decode\n",
+- program_name);
+- } else
++ "%s: verbose output suppressed, use -v[v]... for full protocol decode\n",
++ program_name);
++ }
++ else
+ (void)fprintf(stderr, "%s: ", program_name);
+ dlt = pcap_datalink(pd);
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ (void)fprintf(stderr, "listening on %s", device);
+- if (dlt_name == NULL) {
++ if (dlt_name == NULL)
++ {
+ (void)fprintf(stderr, ", link-type %u", dlt);
+- } else {
++ }
++ else
++ {
+ (void)fprintf(stderr, ", link-type %s (%s)", dlt_name,
+- pcap_datalink_val_to_description(dlt));
++ pcap_datalink_val_to_description(dlt));
+ }
+ (void)fprintf(stderr, ", snapshot length %d bytes\n", ndo->ndo_snaplen);
+ (void)fflush(stderr);
+ }
++#endif
+
+ #ifdef HAVE_CAPSICUM
+ cansandbox = (VFileName == NULL && zflag == NULL);
+@@ -2563,16 +3562,24 @@
+ #endif /* HAVE_CASPER */
+ if (cansandbox && cap_enter() < 0 && errno != ENOSYS)
+ error("unable to enter the capability mode");
+-#endif /* HAVE_CAPSICUM */
++#endif /* HAVE_CAPSICUM */
++ return 0;
++}
+
+- do {
++void tcpdump_run(void)
++{
++ char *ret = NULL;
++ do
++ {
+ status = pcap_loop(pd, cnt, callback, pcap_userdata);
+- if (WFileName == NULL) {
++ if (WFileName == NULL)
++ {
+ /*
+ * We're printing packets. Flush the printed output,
+ * so it doesn't get intermingled with error output.
+ */
+- if (status == -2) {
++ if (status == -2)
++ {
+ /*
+ * We got interrupted, so perhaps we didn't
+ * manage to finish a line we were printing.
+@@ -2582,7 +3589,8 @@
+ }
+ (void)fflush(stdout);
+ }
+- if (status == -2) {
++ if (status == -2)
++ {
+ /*
+ * We got interrupted. If we are reading multiple
+ * files (via -V) set these so that we stop.
+@@ -2590,24 +3598,32 @@
+ VFileName = NULL;
+ ret = NULL;
+ }
+- if (status == -1) {
++ if (status == -1)
++ {
+ /*
+ * Error. Report it.
+ */
+ (void)fprintf(stderr, "%s: pcap_loop: %s\n",
+- program_name, pcap_geterr(pd));
++ program_name, pcap_geterr(pd));
+ }
+- if (RFileName == NULL) {
++ if (RFileName == NULL)
++ {
+ /*
+ * We're doing a live capture. Report the capture
+ * statistics.
+ */
+ info(1);
+ }
+- pcap_close(pd);
+- if (VFileName != NULL) {
++ if (pd)
++ {
++ pcap_close(pd);
++ pd = NULL;
++ }
++ if (VFileName != NULL)
++ {
+ ret = get_next_file(VFile, VFileLine);
+- if (ret) {
++ if (ret)
++ {
+ int new_dlt;
+
+ RFileName = VFileLine;
+@@ -2617,18 +3633,22 @@
+ #ifdef HAVE_CAPSICUM
+ cap_rights_init(&rights, CAP_READ);
+ if (cap_rights_limit(fileno(pcap_file(pd)),
+- &rights) < 0 && errno != ENOSYS) {
++ &rights) < 0 &&
++ errno != ENOSYS)
++ {
+ error("unable to limit pcap descriptor");
+ }
+ #endif
+ new_dlt = pcap_datalink(pd);
+- if (new_dlt != dlt) {
++ if (new_dlt != dlt)
++ {
+ /*
+ * The new file has a different
+ * link-layer header type from the
+ * previous one.
+ */
+- if (WFileName != NULL) {
++ if (WFileName != NULL)
++ {
+ /*
+ * We're writing raw packets
+ * that match the filter to
+@@ -2667,586 +3687,86 @@
+ */
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ fprintf(stderr, "reading from file %s", RFileName);
+- if (dlt_name == NULL) {
+- fprintf(stderr, ", link-type %u", dlt);
+- } else {
++ if (dlt_name == NULL)
++ {
++ fprintf(stderr, ", link-type %d", dlt);
++ }
++ else
++ {
+ fprintf(stderr, ", link-type %s (%s)",
+- dlt_name,
+- pcap_datalink_val_to_description(dlt));
++ dlt_name,
++ pcap_datalink_val_to_description(dlt));
+ }
+ fprintf(stderr, ", snapshot length %d\n", pcap_snapshot(pd));
+ }
+ }
+- }
+- while (ret != NULL);
++ } while (ret != NULL);
+
+ if (count_mode && RFileName != NULL)
+ fprintf(stdout, "%u packet%s\n", packets_captured,
+- PLURAL_SUFFIX(packets_captured));
++ PLURAL_SUFFIX(packets_captured));
+
+ free(cmdbuf);
+ pcap_freecode(&fcode);
+ exit_tcpdump(status == -1 ? S_ERR_HOST_PROGRAM : S_SUCCESS);
+ }
+
+-/*
+- * Catch a signal.
+- */
+-static void
+-(*setsignal (int sig, void (*func)(int)))(int)
+-{
+-#ifdef _WIN32
+- return (signal(sig, func));
+-#else
+- struct sigaction old, new;
+-
+- memset(&new, 0, sizeof(new));
+- new.sa_handler = func;
+- if ((sig == SIGCHLD)
+-# ifdef SIGNAL_REQ_INFO
+- || (sig == SIGNAL_REQ_INFO)
+-# endif
+-# ifdef SIGNAL_FLUSH_PCAP
+- || (sig == SIGNAL_FLUSH_PCAP)
+-# endif
+- )
+- new.sa_flags = SA_RESTART;
+- if (sigaction(sig, &new, &old) < 0)
+- return (SIG_ERR);
+- return (old.sa_handler);
+-#endif
+-}
+-
+-/* make a clean exit on interrupts */
+-static void
+-cleanup(int signo _U_)
++static void stellar_dump_run(void)
+ {
+-#ifdef _WIN32
+- if (timer_handle != INVALID_HANDLE_VALUE) {
+- DeleteTimerQueueTimer(NULL, timer_handle, NULL);
+- CloseHandle(timer_handle);
+- timer_handle = INVALID_HANDLE_VALUE;
+- }
+-#else /* _WIN32 */
+- struct itimerval timer;
+-
+- timer.it_interval.tv_sec = 0;
+- timer.it_interval.tv_usec = 0;
+- timer.it_value.tv_sec = 0;
+- timer.it_value.tv_usec = 0;
+- setitimer(ITIMER_REAL, &timer, NULL);
+-#endif /* _WIN32 */
+-
+-#ifdef HAVE_PCAP_BREAKLOOP
+- /*
+- * We have "pcap_breakloop()"; use it, so that we do as little
+- * as possible in the signal handler (it's probably not safe
+- * to do anything with standard I/O streams in a signal handler -
+- * the ANSI C standard doesn't say it is).
+- */
+- pcap_breakloop(pd);
+-#else
+- /*
+- * We don't have "pcap_breakloop()"; this isn't safe, but
+- * it's the best we can do. Print the summary if we're
+- * not reading from a savefile - i.e., if we're doing a
+- * live capture - and exit.
+- */
+- if (pd != NULL && pcap_file(pd) == NULL) {
++ if (stellar_dump_datalink_init(&g_stellar_dump_arg) < 0)
++ {
++ exit(1);
++ }
++ if (stellar_dump_ctrllink_init(&g_stellar_dump_arg) < 0)
++ {
++ exit(1);
++ }
++ if (stellar_dump_start(&g_stellar_dump_arg) < 0)
++ {
++ exit(1);
++ }
++ stellar_dump_ctrllink_keepalive_start(&g_stellar_dump_arg);
++ char recv_buf[DATALINK_RECV_BUF_SIZE];
++ struct pcap_pkthdr pcaphdr = {};
++ char *pkt_ptr;
++ int total_recv_pkt_num = 0;
++ while (g_stellar_dump_run)
++ {
++ if (stellar_dump_datalink_recv(recv_buf, DATALINK_RECV_BUF_SIZE, &pkt_ptr, &pcaphdr) < 0)
++ {
++ continue;
++ }
+ /*
+- * We got interrupted, so perhaps we didn't
+- * manage to finish a line we were printing.
+- * Print an extra newline, just in case.
+- */
+- putchar('\n');
+- (void)fflush(stdout);
+- info(1);
+- }
+- exit_tcpdump(S_SUCCESS);
+-#endif
+-}
+-
+-/*
+- On windows, we do not use a fork, so we do not care less about
+- waiting a child processes to die
+- */
+-#if defined(HAVE_FORK) || defined(HAVE_VFORK)
+-static void
+-child_cleanup(int signo _U_)
+-{
+- wait(NULL);
+-}
+-#endif /* HAVE_FORK && HAVE_VFORK */
+-
+-static void
+-info(int verbose)
+-{
+- struct pcap_stat stats;
+-
+- /*
+- * Older versions of libpcap didn't set ps_ifdrop on some
+- * platforms; initialize it to 0 to handle that.
+- */
+- stats.ps_ifdrop = 0;
+- if (pcap_stats(pd, &stats) < 0) {
+- (void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pd));
+- infoprint = 0;
+- return;
++ dump_packet: if set -w option, write packet to file
++ print_packet: print packet to stdout
++ */
++ callback(pcap_userdata, &pcaphdr, (u_char *)pkt_ptr);
++ if (g_stellar_dump_arg.expect_packet_count > 0 && ++total_recv_pkt_num >= g_stellar_dump_arg.expect_packet_count)
++ {
++ break;
++ }
+ }
+-
+- if (!verbose)
+- fprintf(stderr, "%s: ", program_name);
+-
+- (void)fprintf(stderr, "%u packet%s captured", packets_captured,
+- PLURAL_SUFFIX(packets_captured));
+- if (!verbose)
+- fputs(", ", stderr);
+- else
+- putc('\n', stderr);
+- (void)fprintf(stderr, "%u packet%s received by filter", stats.ps_recv,
+- PLURAL_SUFFIX(stats.ps_recv));
+- if (!verbose)
+- fputs(", ", stderr);
+- else
+- putc('\n', stderr);
+- (void)fprintf(stderr, "%u packet%s dropped by kernel", stats.ps_drop,
+- PLURAL_SUFFIX(stats.ps_drop));
+- if (stats.ps_ifdrop != 0) {
+- if (!verbose)
+- fputs(", ", stderr);
+- else
+- putc('\n', stderr);
+- (void)fprintf(stderr, "%u packet%s dropped by interface\n",
+- stats.ps_ifdrop, PLURAL_SUFFIX(stats.ps_ifdrop));
+- } else
+- putc('\n', stderr);
+- infoprint = 0;
++ exit(0);
+ }
+
+-#if defined(HAVE_FORK) || defined(HAVE_VFORK)
+-#ifdef HAVE_FORK
+-#define fork_subprocess() fork()
+-#else
+-#define fork_subprocess() vfork()
+-#endif
+-static void
+-compress_savefile(const char *filename)
++int stellar_dump(int argc, char **argv)
+ {
+- pid_t child;
+-
+- child = fork_subprocess();
+- if (child == -1) {
+- fprintf(stderr,
+- "compress_savefile: fork failed: %s\n",
+- pcap_strerror(errno));
+- return;
++ atexit((void (*)(void))cleanup);
++ if (tcpdump_parse_args(argc, argv) < 0)
++ {
++ return -1;
+ }
+- if (child != 0) {
+- /* Parent process. */
+- return;
+- }
+-
+- /*
+- * Child process.
+- * Set to lowest priority so that this doesn't disturb the capture.
+- */
+-#ifdef NZERO
+- setpriority(PRIO_PROCESS, 0, NZERO - 1);
+-#else
+- setpriority(PRIO_PROCESS, 0, 19);
+-#endif
+- if (execlp(zflag, zflag, filename, (char *)NULL) == -1)
+- fprintf(stderr,
+- "compress_savefile: execlp(%s, %s) failed: %s\n",
+- zflag,
+- filename,
+- pcap_strerror(errno));
+-#ifdef HAVE_FORK
+- exit(S_ERR_HOST_PROGRAM);
+-#else
+- _exit(S_ERR_HOST_PROGRAM);
+-#endif
+-}
+-#else /* HAVE_FORK && HAVE_VFORK */
+-static void
+-compress_savefile(const char *filename)
+-{
+- fprintf(stderr,
+- "compress_savefile failed. Functionality not implemented under your system\n");
+-}
+-#endif /* HAVE_FORK && HAVE_VFORK */
+-
+-static void
+-dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
+-{
+- struct dump_info *dump_info;
+-
+- ++packets_captured;
+-
+- ++infodelay;
+-
+- dump_info = (struct dump_info *)user;
+-
+- /*
+- * XXX - this won't force the file to rotate on the specified time
+- * boundary, but it will rotate on the first packet received after the
+- * specified Gflag number of seconds. Note: if a Gflag time boundary
+- * and a Cflag size boundary coincide, the time rotation will occur
+- * first thereby cancelling the Cflag boundary (since the file should
+- * be 0).
+- */
+- if (Gflag != 0) {
+- /* Check if it is time to rotate */
+- time_t t;
+-
+- /* Get the current time */
+- if ((t = time(NULL)) == (time_t)-1) {
+- error("%s: can't get current_time: %s",
+- __func__, pcap_strerror(errno));
+- }
+-
+-
+- /* If the time is greater than the specified window, rotate */
+- if (t - Gflag_time >= Gflag) {
+-#ifdef HAVE_CAPSICUM
+- FILE *fp;
+- int fd;
+-#endif
+-
+- /* Update the Gflag_time */
+- Gflag_time = t;
+- /* Update Gflag_count */
+- Gflag_count++;
+- /*
+- * Close the current file and open a new one.
+- */
+- pcap_dump_close(dump_info->pdd);
+-
+- /*
+- * Compress the file we just closed, if the user asked for it
+- */
+- if (zflag != NULL)
+- compress_savefile(dump_info->CurrentFileName);
+-
+- /*
+- * Check to see if we've exceeded the Wflag (when
+- * not using Cflag).
+- */
+- if (Cflag == 0 && Wflag > 0 && Gflag_count >= Wflag) {
+- (void)fprintf(stderr, "Maximum file limit reached: %d\n",
+- Wflag);
+- info(1);
+- exit_tcpdump(S_SUCCESS);
+- /* NOTREACHED */
+- }
+- if (dump_info->CurrentFileName != NULL)
+- free(dump_info->CurrentFileName);
+- /* Allocate space for max filename + \0. */
+- dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
+- if (dump_info->CurrentFileName == NULL)
+- error("dump_packet_and_trunc: malloc");
+- /*
+- * Gflag was set otherwise we wouldn't be here. Reset the count
+- * so multiple files would end with 1,2,3 in the filename.
+- * The counting is handled with the -C flow after this.
+- */
+- Cflag_count = 0;
+-
+- /*
+- * This is always the first file in the Cflag
+- * rotation: e.g. 0
+- * We also don't need numbering if Cflag is not set.
+- */
+- if (Cflag != 0)
+- MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0,
+- WflagChars);
+- else
+- MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0);
+-
+-#ifdef HAVE_LIBCAP_NG
+- capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+- capng_apply(CAPNG_SELECT_BOTH);
+-#endif /* HAVE_LIBCAP_NG */
+-#ifdef HAVE_CAPSICUM
+- fd = openat(dump_info->dirfd,
+- dump_info->CurrentFileName,
+- O_CREAT | O_WRONLY | O_TRUNC, 0644);
+- if (fd < 0) {
+- error("unable to open file %s",
+- dump_info->CurrentFileName);
+- }
+- fp = fdopen(fd, "w");
+- if (fp == NULL) {
+- error("unable to fdopen file %s",
+- dump_info->CurrentFileName);
+- }
+- dump_info->pdd = pcap_dump_fopen(dump_info->pd, fp);
+-#else /* !HAVE_CAPSICUM */
+- dump_info->pdd = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
+-#endif
+-#ifdef HAVE_LIBCAP_NG
+- capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+- capng_apply(CAPNG_SELECT_BOTH);
+-#endif /* HAVE_LIBCAP_NG */
+- if (dump_info->pdd == NULL)
+- error("%s", pcap_geterr(pd));
+-#ifdef HAVE_CAPSICUM
+- set_dumper_capsicum_rights(dump_info->pdd);
+-#endif
+- }
++ if (tcpdump_prepare() < 0)
++ {
++ return -1;
+ }
+-
+- /*
+- * XXX - this won't prevent capture files from getting
+- * larger than Cflag - the last packet written to the
+- * file could put it over Cflag.
+- */
+- if (Cflag != 0) {
+-#ifdef HAVE_PCAP_DUMP_FTELL64
+- int64_t size = pcap_dump_ftell64(dump_info->pdd);
+-#else
+- /*
+- * XXX - this only handles a Cflag value > 2^31-1 on
+- * LP64 platforms; to handle ILP32 (32-bit UN*X and
+- * Windows) or LLP64 (64-bit Windows) would require
+- * a version of libpcap with pcap_dump_ftell64().
+- */
+- long size = pcap_dump_ftell(dump_info->pdd);
+-#endif
+-
+- if (size == -1)
+- error("ftell fails on output file");
+- if (size > Cflag) {
+-#ifdef HAVE_CAPSICUM
+- FILE *fp;
+- int fd;
+-#endif
+-
+- /*
+- * Close the current file and open a new one.
+- */
+- pcap_dump_close(dump_info->pdd);
+-
+- /*
+- * Compress the file we just closed, if the user
+- * asked for it.
+- */
+- if (zflag != NULL)
+- compress_savefile(dump_info->CurrentFileName);
+-
+- Cflag_count++;
+- if (Wflag > 0) {
+- if (Cflag_count >= Wflag)
+- Cflag_count = 0;
+- }
+- if (dump_info->CurrentFileName != NULL)
+- free(dump_info->CurrentFileName);
+- dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
+- if (dump_info->CurrentFileName == NULL)
+- error("%s: malloc", __func__);
+- MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
+-#ifdef HAVE_LIBCAP_NG
+- capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+- capng_apply(CAPNG_SELECT_BOTH);
+-#endif /* HAVE_LIBCAP_NG */
+-#ifdef HAVE_CAPSICUM
+- fd = openat(dump_info->dirfd, dump_info->CurrentFileName,
+- O_CREAT | O_WRONLY | O_TRUNC, 0644);
+- if (fd < 0) {
+- error("unable to open file %s",
+- dump_info->CurrentFileName);
+- }
+- fp = fdopen(fd, "w");
+- if (fp == NULL) {
+- error("unable to fdopen file %s",
+- dump_info->CurrentFileName);
+- }
+- dump_info->pdd = pcap_dump_fopen(dump_info->pd, fp);
+-#else /* !HAVE_CAPSICUM */
+- dump_info->pdd = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
+-#endif
+-#ifdef HAVE_LIBCAP_NG
+- capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+- capng_apply(CAPNG_SELECT_BOTH);
+-#endif /* HAVE_LIBCAP_NG */
+- if (dump_info->pdd == NULL)
+- error("%s", pcap_geterr(pd));
+-#ifdef HAVE_CAPSICUM
+- set_dumper_capsicum_rights(dump_info->pdd);
+-#endif
+- }
++ if (RFileName != NULL || VFileName != NULL)
++ {
++ tcpdump_run();
+ }
+-
+- pcap_dump((u_char *)dump_info->pdd, h, sp);
+-#ifdef HAVE_PCAP_DUMP_FLUSH
+- if (Uflag)
+- pcap_dump_flush(dump_info->pdd);
+-#endif
+-
+- if (dump_info->ndo != NULL)
+- pretty_print_packet(dump_info->ndo, h, sp, packets_captured);
+-
+- --infodelay;
+- if (infoprint)
+- info(0);
+-}
+-
+-static void
+-dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
+-{
+- struct dump_info *dump_info;
+-
+- ++packets_captured;
+-
+- ++infodelay;
+-
+- dump_info = (struct dump_info *)user;
+-
+- pcap_dump((u_char *)dump_info->pdd, h, sp);
+-#ifdef HAVE_PCAP_DUMP_FLUSH
+- if (Uflag)
+- pcap_dump_flush(dump_info->pdd);
+-#endif
+-
+- if (dump_info->ndo != NULL)
+- pretty_print_packet(dump_info->ndo, h, sp, packets_captured);
+-
+- --infodelay;
+- if (infoprint)
+- info(0);
+-}
+-
+-static void
+-print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
+-{
+- ++packets_captured;
+-
+- ++infodelay;
+-
+- if (!count_mode)
+- pretty_print_packet((netdissect_options *)user, h, sp, packets_captured);
+-
+- --infodelay;
+- if (infoprint)
+- info(0);
+-}
+-
+-#ifdef SIGNAL_REQ_INFO
+-static void
+-requestinfo(int signo _U_)
+-{
+- if (infodelay)
+- ++infoprint;
+ else
+- info(0);
+-}
+-#endif
+-
+-#ifdef SIGNAL_FLUSH_PCAP
+-static void
+-flushpcap(int signo _U_)
+-{
+- if (pdd != NULL)
+- pcap_dump_flush(pdd);
+-}
+-#endif
+-
+-static void
+-print_packets_captured (void)
+-{
+- static u_int prev_packets_captured, first = 1;
+-
+- if (infodelay == 0 && (first || packets_captured != prev_packets_captured)) {
+- fprintf(stderr, "Got %u\r", packets_captured);
+- first = 0;
+- prev_packets_captured = packets_captured;
++ {
++ stellar_dump_run();
+ }
+-}
+-
+-/*
+- * Called once each second in verbose mode while dumping to file
+- */
+-#ifdef _WIN32
+-static void CALLBACK verbose_stats_dump(PVOID param _U_,
+- BOOLEAN timer_fired _U_)
+-{
+- print_packets_captured();
+-}
+-#else /* _WIN32 */
+-static void verbose_stats_dump(int sig _U_)
+-{
+- print_packets_captured();
+-}
+-#endif /* _WIN32 */
+-
+-DIAG_OFF_DEPRECATION
+-static void
+-print_version(FILE *f)
+-{
+-#ifndef HAVE_PCAP_LIB_VERSION
+- #ifdef HAVE_PCAP_VERSION
+- extern char pcap_version[];
+- #else /* HAVE_PCAP_VERSION */
+- static char pcap_version[] = "unknown";
+- #endif /* HAVE_PCAP_VERSION */
+-#endif /* HAVE_PCAP_LIB_VERSION */
+- const char *smi_version_string;
+-
+- (void)fprintf(f, "%s version " PACKAGE_VERSION "\n", program_name);
+-#ifdef HAVE_PCAP_LIB_VERSION
+- (void)fprintf(f, "%s\n", pcap_lib_version());
+-#else /* HAVE_PCAP_LIB_VERSION */
+- (void)fprintf(f, "libpcap version %s\n", pcap_version);
+-#endif /* HAVE_PCAP_LIB_VERSION */
+-
+-#if defined(HAVE_LIBCRYPTO) && defined(SSLEAY_VERSION)
+- (void)fprintf (f, "%s\n", SSLeay_version(SSLEAY_VERSION));
+-#endif
+-
+- smi_version_string = nd_smi_version_string();
+- if (smi_version_string != NULL)
+- (void)fprintf (f, "SMI-library: %s\n", smi_version_string);
+-
+-#if defined(__SANITIZE_ADDRESS__)
+- (void)fprintf (f, "Compiled with AddressSanitizer/GCC.\n");
+-#elif defined(__has_feature)
+-# if __has_feature(address_sanitizer)
+- (void)fprintf (f, "Compiled with AddressSanitizer/Clang.\n");
+-# elif __has_feature(memory_sanitizer)
+- (void)fprintf (f, "Compiled with MemorySanitizer/Clang.\n");
+-# endif
+-#endif /* __SANITIZE_ADDRESS__ or __has_feature */
+-}
+-DIAG_ON_DEPRECATION
+-
+-static void
+-print_usage(FILE *f)
+-{
+- print_version(f);
+- (void)fprintf(f,
+-"Usage: %s [-Abd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ] [--count]\n", program_name);
+- (void)fprintf(f,
+-"\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n");
+- (void)fprintf(f,
+-"\t\t[ -i interface ]" IMMEDIATE_MODE_USAGE j_FLAG_USAGE "\n");
+-#ifdef HAVE_PCAP_FINDALLDEVS_EX
+- (void)fprintf(f,
+-"\t\t" LIST_REMOTE_INTERFACES_USAGE "\n");
+-#endif
+-#ifdef USE_LIBSMI
+- (void)fprintf(f,
+-"\t\t" m_FLAG_USAGE "\n");
+-#endif
+- (void)fprintf(f,
+-"\t\t[ -M secret ] [ --number ] [ --print ]" Q_FLAG_USAGE "\n");
+- (void)fprintf(f,
+-"\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]\n");
+- (void)fprintf(f,
+-"\t\t[ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ]\n");
+-#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+- (void)fprintf(f,
+-"\t\t[ --time-stamp-precision precision ] [ --micro ] [ --nano ]\n");
+-#endif
+- (void)fprintf(f,
+-"\t\t[ -z postrotate-command ] [ -Z user ] [ expression ]\n");
++ return 0;
+ }
+diff -uNr tcpdump-tcpdump-4.99.4/tcpdump_ip6.h tcpdump-for-stellar/tcpdump_ip6.h
+--- tcpdump-tcpdump-4.99.4/tcpdump_ip6.h 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/tcpdump_ip6.h 2024-10-30 17:34:06.265498697 +0800
+@@ -0,0 +1,231 @@
++/* NetBSD: ip6.h,v 1.9 2000/07/13 05:34:21 itojun Exp */
++/* $KAME: ip6.h,v 1.9 2000/07/02 21:01:32 itojun Exp $ */
++
++/*
++ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. Neither the name of the project nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Copyright (c) 1982, 1986, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)ip.h 8.1 (Berkeley) 6/10/93
++ */
++
++#ifndef ND_IP6_H_
++#define ND_IP6_H_
++
++/*
++ * Definition for internet protocol version 6.
++ * RFC 2460
++ */
++
++// struct ip6_hdr
++struct tcpdump_ip6_hdr
++{
++ union
++ {
++ // struct ip6_hdrctl
++ struct tcpdump_ip6_hdrctl
++ {
++ nd_uint32_t ip6_un1_flow; /* 20 bits of flow-ID */
++ nd_uint16_t ip6_un1_plen; /* payload length */
++ nd_uint8_t ip6_un1_nxt; /* next header */
++ nd_uint8_t ip6_un1_hlim; /* hop limit */
++ } ip6_un1;
++ nd_uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */
++ } ip6_ctlun;
++ nd_ipv6 ip6_src; /* source address */
++ nd_ipv6 ip6_dst; /* destination address */
++};
++
++#define ip6_vfc ip6_ctlun.ip6_un2_vfc
++#define IP6_VERSION(ip6_hdr) ((GET_U_1((ip6_hdr)->ip6_vfc) & 0xf0) >> 4)
++#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
++#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
++#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
++#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
++#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
++
++/* in network endian */
++#define IPV6_FLOWINFO_MASK ((uint32_t)htonl(0x0fffffff)) /* flow info (28 bits) */
++#define IPV6_FLOWLABEL_MASK ((uint32_t)htonl(0x000fffff)) /* flow label (20 bits) */
++
++/*
++ * Extension Headers
++ */
++
++// struct ip6_ext
++struct tcpdump_ip6_ext
++{
++ nd_uint8_t ip6e_nxt;
++ nd_uint8_t ip6e_len;
++};
++
++/* Hop-by-Hop options header */
++// struct ip6_hbh
++struct tcpdump_ip6_hbh
++{
++ nd_uint8_t ip6h_nxt; /* next header */
++ nd_uint8_t ip6h_len; /* length in units of 8 octets */
++ /* followed by options */
++};
++
++/* Destination options header */
++// struct ip6_dest
++struct tcpdump_ip6_dest
++{
++ nd_uint8_t ip6d_nxt; /* next header */
++ nd_uint8_t ip6d_len; /* length in units of 8 octets */
++ /* followed by options */
++};
++
++/* https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml */
++
++/* Option types and related macros */
++#define IP6OPT_PAD1 0x00 /* 00 0 00000 */
++#define IP6OPT_PADN 0x01 /* 00 0 00001 */
++#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */
++#define IP6OPT_JUMBO_LEN 6
++#define IP6OPT_RPL 0x63 /* 01 1 00011 */
++#define IP6OPT_TUN_ENC_LIMIT 0x04 /* 00 0 00100 */
++#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */
++
++#define IP6OPT_RTALERT_LEN 4
++#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */
++#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */
++#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */
++#define IP6OPT_MINLEN 2
++
++#define IP6OPT_QUICK_START 0x26 /* 00 1 00110 */
++#define IP6OPT_CALIPSO 0x07 /* 00 0 00111 */
++#define IP6OPT_SMF_DPD 0x08 /* 00 0 01000 */
++#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */
++#define IP6OPT_HOMEADDR_MINLEN 18
++#define IP6OPT_EID 0x8a /* 10 0 01010 */
++#define IP6OPT_ILNP_NOTICE 0x8b /* 10 0 01011 */
++#define IP6OPT_LINE_ID 0x8c /* 10 0 01100 */
++#define IP6OPT_MPL 0x6d /* 01 1 01101 */
++#define IP6OPT_IP_DFF 0xee /* 11 1 01110 */
++
++#define IP6OPT_TYPE(o) ((o) & 0xC0)
++#define IP6OPT_TYPE_SKIP 0x00
++#define IP6OPT_TYPE_DISCARD 0x40
++#define IP6OPT_TYPE_FORCEICMP 0x80
++#define IP6OPT_TYPE_ICMP 0xC0
++
++#define IP6OPT_MUTABLE 0x20
++
++/* Routing header */
++// struct ip6_rthdr
++struct tcpdump_ip6_rthdr
++{
++ nd_uint8_t ip6r_nxt; /* next header */
++ nd_uint8_t ip6r_len; /* length in units of 8 octets */
++ nd_uint8_t ip6r_type; /* routing type */
++ nd_uint8_t ip6r_segleft; /* segments left */
++ /* followed by routing type specific data */
++};
++
++#define IPV6_RTHDR_TYPE_0 0
++#define IPV6_RTHDR_TYPE_2 2
++#define IPV6_RTHDR_TYPE_4 4
++
++/* Type 0 Routing header */
++/* Also used for Type 2 */
++// struct ip6_rthdr0
++struct tcpdump_ip6_rthdr0
++{
++ nd_uint8_t ip6r0_nxt; /* next header */
++ nd_uint8_t ip6r0_len; /* length in units of 8 octets */
++ nd_uint8_t ip6r0_type; /* always zero */
++ nd_uint8_t ip6r0_segleft; /* segments left */
++ nd_uint32_t ip6r0_reserved; /* reserved field */
++ nd_ipv6 ip6r0_addr[1]; /* up to 23 addresses */
++};
++
++/**
++ * Type 4 Routing header
++ * known as Segment Routing Header 'SRH'
++ */
++// struct ip6_srh
++struct tcpdump_ip6_srh
++{
++ nd_uint8_t srh_nxt; /* next header */
++ nd_uint8_t srh_len; /* length in units of 8 octets */
++ nd_uint8_t srh_type; /* Routing Type 4 */
++ nd_uint8_t srh_segleft; /* segments left */
++ nd_uint8_t srh_last_ent; /* Last Entry*/
++ nd_uint8_t srh_flags; /* Flags */
++ nd_uint16_t srh_tag; /* Tag */
++ nd_ipv6 srh_segments[1]; /* SRH segments list*/
++};
++
++/* Fragment header */
++// struct ip6_frag
++struct tcpdump_ip6_frag
++{
++ nd_uint8_t ip6f_nxt; /* next header */
++ nd_uint8_t ip6f_reserved; /* reserved field */
++ nd_uint16_t ip6f_offlg; /* offset, reserved, and flag */
++ nd_uint32_t ip6f_ident; /* identification */
++};
++
++#define IP6F_OFF_MASK 0xfff8 /* mask out offset from ip6f_offlg */
++#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */
++#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */
++
++#endif /* not ND_IP6_H_ */
+diff -uNr tcpdump-tcpdump-4.99.4/tcpdump_ip.h tcpdump-for-stellar/tcpdump_ip.h
+--- tcpdump-tcpdump-4.99.4/tcpdump_ip.h 1970-01-01 08:00:00.000000000 +0800
++++ tcpdump-for-stellar/tcpdump_ip.h 2024-10-30 17:34:06.265498697 +0800
+@@ -0,0 +1,169 @@
++/*
++ * Copyright (c) 1982, 1986, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)ip.h 8.2 (Berkeley) 6/1/94
++ */
++
++#ifndef netdissect_ip_h
++#define netdissect_ip_h
++/*
++ * Definitions for internet protocol version 4.
++ * Per RFC 791, September 1981.
++ */
++#define IPVERSION 4
++
++/*
++ * Structure of an internet header, naked of options.
++ *
++ * We declare ip_len and ip_off to be short, rather than u_short
++ * pragmatically since otherwise unsigned comparisons can result
++ * against negative integers quite easily, and fail in subtle ways.
++ */
++// struct ip
++struct tcpdump_ip
++{
++ nd_uint8_t ip_vhl; /* header length, version */
++#define IP_V(ip) ((GET_U_1((ip)->ip_vhl) & 0xf0) >> 4)
++#define IP_HL(ip) (GET_U_1((ip)->ip_vhl) & 0x0f)
++ nd_uint8_t ip_tos; /* type of service */
++ nd_uint16_t ip_len; /* total length */
++ nd_uint16_t ip_id; /* identification */
++ nd_uint16_t ip_off; /* fragment offset field */
++#define IP_DF 0x4000 /* don't fragment flag */
++#define IP_MF 0x2000 /* more fragments flag */
++#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
++ nd_uint8_t ip_ttl; /* time to live */
++ nd_uint8_t ip_p; /* protocol */
++ nd_uint16_t ip_sum; /* checksum */
++ nd_ipv4 ip_src, ip_dst; /* source and dest address */
++};
++
++#define IP_MAXPACKET 65535 /* maximum packet size */
++
++/*
++ * Definitions for IP type of service (ip_tos)
++ */
++#define IPTOS_LOWDELAY 0x10
++#define IPTOS_THROUGHPUT 0x08
++#define IPTOS_RELIABILITY 0x04
++
++/*
++ * Definitions for IP precedence (also in ip_tos) (hopefully unused)
++ */
++#define IPTOS_PREC_NETCONTROL 0xe0
++#define IPTOS_PREC_INTERNETCONTROL 0xc0
++#define IPTOS_PREC_CRITIC_ECP 0xa0
++#define IPTOS_PREC_FLASHOVERRIDE 0x80
++#define IPTOS_PREC_FLASH 0x60
++#define IPTOS_PREC_IMMEDIATE 0x40
++#define IPTOS_PREC_PRIORITY 0x20
++#define IPTOS_PREC_ROUTINE 0x00
++
++/*
++ * Definitions for options.
++ */
++#define IPOPT_COPIED(o) ((o) & 0x80)
++#define IPOPT_CLASS(o) ((o) & 0x60)
++#define IPOPT_NUMBER(o) ((o) & 0x1f)
++
++#define IPOPT_CONTROL 0x00
++#define IPOPT_RESERVED1 0x20
++#define IPOPT_DEBMEAS 0x40
++#define IPOPT_RESERVED2 0x60
++
++#define IPOPT_EOL 0 /* end of option list */
++#define IPOPT_NOP 1 /* no operation */
++
++#define IPOPT_RR 7 /* record packet route */
++#define IPOPT_TS 68 /* timestamp */
++#define IPOPT_RFC1393 82 /* traceroute RFC 1393 */
++#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
++#define IPOPT_LSRR 131 /* loose source route */
++#define IPOPT_SATID 136 /* satnet id */
++#define IPOPT_SSRR 137 /* strict source route */
++#define IPOPT_RA 148 /* router-alert, rfc2113 */
++
++/*
++ * Offsets to fields in options other than EOL and NOP.
++ */
++#define IPOPT_OPTVAL 0 /* option ID */
++#define IPOPT_OLEN 1 /* option length */
++#define IPOPT_OFFSET 2 /* offset within option */
++#define IPOPT_MINOFF 4 /* min value of above */
++
++/*
++ * Time stamp option structure.
++ */
++// struct ip_timestamp
++struct tcpdump_ip_timestamp
++{
++ nd_uint8_t ipt_code; /* IPOPT_TS */
++ nd_uint8_t ipt_len; /* size of structure (variable) */
++ nd_uint8_t ipt_ptr; /* index of current entry */
++ nd_uint8_t ipt_oflwflg; /* flags, overflow counter */
++#define IPTS_OFLW(ip) (((ipt)->ipt_oflwflg & 0xf0) >> 4)
++#define IPTS_FLG(ip) ((ipt)->ipt_oflwflg & 0x0f)
++ union ipt_timestamp
++ {
++ nd_uint32_t ipt_time[1];
++ struct ipt_ta
++ {
++ nd_ipv4 ipt_addr;
++ nd_uint32_t ipt_time;
++ } ipt_ta[1];
++ } ipt_timestamp;
++};
++
++/* flag bits for ipt_flg */
++#define IPOPT_TS_TSONLY 0 /* timestamps only */
++#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
++#define IPOPT_TS_PRESPEC 3 /* specified modules only */
++
++/* bits for security (not byte swapped) */
++#define IPOPT_SECUR_UNCLASS 0x0000
++#define IPOPT_SECUR_CONFID 0xf135
++#define IPOPT_SECUR_EFTO 0x789a
++#define IPOPT_SECUR_MMMM 0xbc4d
++#define IPOPT_SECUR_RESTR 0xaf13
++#define IPOPT_SECUR_SECRET 0xd788
++#define IPOPT_SECUR_TOPSECRET 0x6bc5
++
++/*
++ * Internet implementation parameters.
++ */
++#define MAXTTL 255 /* maximum time to live (seconds) */
++#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
++#define IPFRAGTTL 60 /* time to live for frags, slowhz */
++#define IPTTLDEC 1 /* subtracted when forwarding */
++
++#define IP_MSS 576 /* default maximum segment size */
++#endif /* netdissect_ip_h */
+\ No newline at end of file
diff --git a/vendors/CMakeLists.txt b/vendors/CMakeLists.txt
index 3c5e067..db34d81 100644
--- a/vendors/CMakeLists.txt
+++ b/vendors/CMakeLists.txt
@@ -85,7 +85,7 @@ ExternalProject_Add(libevent
PREFIX libevent
URL ${CMAKE_CURRENT_SOURCE_DIR}/libevent-2.1.12-stable.tar.gz
URL_MD5 b5333f021f880fe76490d8a799cd79f4
- CMAKE_ARGS -DEVENT__DISABLE_OPENSSL=ON -DEVENT__DISABLE_BENCHMARK=ON -DBUILD_TESTING=OFF -DEVENT__DISABLE_TESTS=ON -DEVENT__LIBRARY_TYPE=STATIC
+ CMAKE_ARGS -DEVENT__DISABLE_OPENSSL=ON -DEVENT__DISABLE_BENCHMARK=ON -DBUILD_TESTING=OFF -DEVENT__DISABLE_TESTS=ON -DEVENT__LIBRARY_TYPE=BOTH -DCMAKE_C_FLAGS="-fPIC"
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
diff --git a/vendors/tcpdump-4.99.4.tar.gz b/vendors/tcpdump-4.99.4.tar.gz
new file mode 100644
index 0000000..9b20513
--- /dev/null
+++ b/vendors/tcpdump-4.99.4.tar.gz
Binary files differ