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 + * Copyright (c) 2010-2013, Pieter Noordhuis + * + * 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+ 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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 . 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(""); + 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 + * Copyright (c) 2010-2013, Pieter Noordhuis + * + * 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 + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 + * 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 + +/* + * 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 + * 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 +#include +#include +#include +#include +#include +#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 %. */ + 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". + * + * 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 +#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 + * 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 +#include +#include + +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 + * 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 + * 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 +#include +#include +#include +#include +#include +#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 */ +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 \n"); + ss = sdscatprintf(ss, " count \n"); + ss = sdscatprintf(ss, " state \n"); + ss = sdscatprintf(ss, " protocol \n"); + ss = sdscatprintf(ss, " sip \n"); + ss = sdscatprintf(ss, " dip \n"); + ss = sdscatprintf(ss, " sport \n"); + ss = sdscatprintf(ss, " dport \n"); + ss = sdscatprintf(ss, " snet \texample 192.168.1.0/24\n"); + ss = sdscatprintf(ss, " dnet \texample 1234::abcd/48\n"); + ss = sdscatprintf(ss, " create-time-in \texample last-1-hours\n"); + ss = sdscatprintf(ss, " last-pkt-time-in \texample last-7-days\n"); + ss = sdscatprintf(ss, " 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 \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 \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 \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: \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: \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 ", + "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 +#include +#include +#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, ®ister_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, ®ister_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, ®ister_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 +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +extern "C" +{ +#endif +#include "stellar/monitor.h" +#include "sds/sds.h" + +#include "stellar/module.h" +#include "stellar/log.h" + +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#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 + +#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 +#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 +#include +#include "stellar/monitor.h" +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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", "", "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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "monitor_private.h" +#include + +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 +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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], ©_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 +#include +#include +#include +#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 #include -#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 +#include +#include +#include +#include +#include "stellar/utils.h" +#ifdef __cplusplus +extern "C" +{ +#endif +#include +#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(¶.llhttp_parser, HTTP_REQUEST, ¶.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(¶.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(¶.llhttp_parser, HTTP_RESPONSE, ¶.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(¶.llhttp_parser, request, strlen(request)); + ASSERT_TRUE(lerr != HPE_OK); +} + +TEST(HTTP_llhttp, request_uncompleted) +{ + struct gtest_http_parm para = {}; + gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); + const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length:"; + enum llhttp_errno lerr = llhttp_execute(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.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(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.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(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.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(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); + const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n\r\nsome content"; + enum llhttp_errno lerr = llhttp_execute(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); + const char *request = "GET /path/index.html HTTP/1.0\r\nHost: example.com\r\n\r\n\r\nsome content"; + enum llhttp_errno lerr = llhttp_execute(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); + const char *request = "POST /path/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n\r\nsome content"; + enum llhttp_errno lerr = llhttp_execute(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); + const char *request = "POST /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-type: text/html\r\n\r\nsome content"; + enum llhttp_errno lerr = llhttp_execute(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); + const char *request = "POST /path/index.html HTTP/1.0\r\nHost: example.com\r\nContent-type: text/html\r\n\r\nsome content"; + enum llhttp_errno lerr = llhttp_execute(¶.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(¶.llhttp_parser, HTTP_RESPONSE, ¶.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(¶.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(¶.llhttp_parser, HTTP_REQUEST, ¶.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(¶.llhttp_parser, response, strlen(response)); + ASSERT_TRUE(lerr != HPE_OK); +} + +TEST(HTTP_llhttp, response_uncompleted) +{ + struct gtest_http_parm para = {}; + gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.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(¶.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(¶.llhttp_parser, HTTP_RESPONSE, ¶.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(¶.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(¶.llhttp_parser, HTTP_RESPONSE, ¶.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(¶.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(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); + const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\n\r\nxxx"; + enum llhttp_errno lerr = llhttp_execute(¶.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(¶.llhttp_parser, HTTP_RESPONSE, ¶.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(¶.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(¶.llhttp_parser, HTTP_RESPONSE, ¶.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(¶.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(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); + const char *response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n"; + enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); + ASSERT_EQ(lerr, HPE_OK); + + lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); + ASSERT_EQ(lerr, HPE_OK); + + lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); + ASSERT_EQ(lerr, HPE_OK); + + llhttp_reset(¶.llhttp_parser); + memset(¶.count, 0, sizeof(para.count)); + response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n"; + + lerr = llhttp_execute(¶.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 +#include +#include +#include +#include +#include +#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, ®ister_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, ®ister_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, ®ister_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, ®ister_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, ®ister_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 Binary files /dev/null and b/test/monitor/pcap/monitor_benchmark.pcap 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 Binary files /dev/null and b/test/monitor/pcap/monitor_packet_dump.pcap differ diff --git a/test/monitor/pcap/monitor_tunnel.pcap b/test/monitor/pcap/monitor_tunnel.pcap new file mode 100644 index 0000000..1492d7c Binary files /dev/null and b/test/monitor/pcap/monitor_tunnel.pcap 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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", "", + 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 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 ++#include ++#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 + #include + #include + #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 + #include + +-#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 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 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 ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#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 + #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 + #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 ++#include ++#include ++#include ++#include ++#include ++#include /* See NOTES */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include /* See NOTES */ ++#include ++#include ++#include ++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 ++#include ++#include ++ ++#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 + #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 + #include + #include +-#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 + #include + #include ++#include + #ifdef _WIN32 + #include + #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= -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 Binary files /dev/null and b/vendors/tcpdump-4.99.4.tar.gz differ