Compare commits

...

12 Commits

Author SHA1 Message Date
lirazyehezkel
bf8d5ed069 Support multiple workspaces (TRA-4365) (#945)
* support multiple workspaces

* reopen by websocket url dep

* open websocket only when websocketURL is changed

* upgrade common version

Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-03-29 09:56:59 +03:00
Nimrod Gilboa Markevich
1f6e539590 Add commit message and committer to acceptance tests slack alert (#946)
* Add commit message and committer username to slack alerts
* Use name instead of username
* Use name and email
2022-03-29 09:15:42 +03:00
David Levanon
590fa08c81 EBPF error handling 2022-03-28 14:19:06 +03:00
Igor Gov
0a9be1884a Traffic viewer list item ebpf to lock icon (#939)
* Traffic viewer changing ebpf to lock icon
2022-03-28 11:10:54 +03:00
Igor Gov
40c745068e Traffic viewer changing ebpf to lock icon (#938) 2022-03-28 10:45:52 +03:00
AmitUp9
10dffd9331 TRA-4419 remove headers from OAS select (#936) 2022-03-28 10:01:57 +03:00
leon-up9
0a800e8d8a passed containerId in the object config (#935)
* passed containerId in the object config

* pkg version

* tmp: run acceptance tests, revert before merge

* reverte

Co-authored-by: Leon <>
2022-03-27 19:41:51 +03:00
RamiBerm
068a4ff86e TRA-4422 line numbers fix (#934)
* fix line numbering in body syntax highlighter

* Packages
2022-03-27 16:17:34 +03:00
leon-up9
c45a869b75 Fix/regration-fixes-24.3 (#933)
* link open in a new tab

* fixed double toast

* added const

* javiers new icon

* epbg img fix

* pkg update

* changed import

* formated

Co-authored-by: Leon <>
2022-03-27 15:47:46 +03:00
leon-up9
0a793cd9e0 added props (#924) 2022-03-27 11:31:40 +03:00
Adrian Gąsior
8d19080c11 Updated Dockerfile and fixed typo in Makefile (#929)
* Updated Dockerfile and fixed typo in Makefile

* PR request

* added small hotfix to basenine
2022-03-27 01:39:50 +03:00
Nimrod Gilboa Markevich
319c3c7a8d Initialize tapper before ws (#932) 2022-03-26 21:25:18 +03:00
39 changed files with 4381 additions and 1090 deletions

View File

@@ -43,7 +43,7 @@ jobs:
with: with:
status: ${{ job.status }} status: ${{ job.status }}
notification_title: 'Mizu {workflow} has {status_message}' notification_title: 'Mizu {workflow} has {status_message}'
message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit: <{commit_url}|{commit_sha}>' message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit <{commit_url}|{commit_sha} ${{ github.event.head_commit.message }}> ${{ github.event.head_commit.committer.name }} <${{ github.event.head_commit.committer.email }}>'
footer: 'Linked Repo <{repo_url}|{repo}>' footer: 'Linked Repo <{repo_url}|{repo}>'
notify_when: 'failure' notify_when: 'failure'
env: env:

View File

@@ -6,8 +6,7 @@ FROM node:16 AS front-end
WORKDIR /app/ui-build WORKDIR /app/ui-build
COPY ui/package.json . COPY ui/package.json ui/package-lock.json ./
COPY ui/package-lock.json .
RUN npm i RUN npm i
COPY ui . COPY ui .
RUN npm run build RUN npm run build
@@ -15,7 +14,7 @@ RUN npm run build
### Base builder image for native builds architecture ### Base builder image for native builds architecture
FROM golang:1.17-alpine AS builder-native-base FROM golang:1.17-alpine AS builder-native-base
ENV CGO_ENABLED=1 GOOS=linux ENV CGO_ENABLED=1 GOOS=linux
RUN apk add libpcap-dev g++ perl-utils RUN apk add --no-cache libpcap-dev g++ perl-utils
### Intermediate builder image for x86-64 to x86-64 native builds ### Intermediate builder image for x86-64 to x86-64 native builds
@@ -79,15 +78,14 @@ RUN go build -ldflags="-extldflags=-static -s -w \
# Download Basenine executable, verify the sha1sum # Download Basenine executable, verify the sha1sum
ADD https://github.com/up9inc/basenine/releases/download/v0.6.6/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH} ADD https://github.com/up9inc/basenine/releases/download/v0.6.6/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
ADD https://github.com/up9inc/basenine/releases/download/v0.6.6/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256 ADD https://github.com/up9inc/basenine/releases/download/v0.6.6/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
RUN shasum -a 256 -c basenine_linux_${GOARCH}.sha256
RUN chmod +x ./basenine_linux_${GOARCH}
RUN mv ./basenine_linux_${GOARCH} ./basenine
RUN shasum -a 256 -c basenine_linux_"${GOARCH}".sha256 && \
chmod +x ./basenine_linux_"${GOARCH}" && \
mv ./basenine_linux_"${GOARCH}" ./basenine
### The shipped image ### The shipped image
ARG TARGETARCH=amd64 ARG TARGETARCH=amd64
FROM ${TARGETARCH}/busybox:latest FROM ${TARGETARCH}/busybox:latest
# gin-gonic runs in debug mode without this # gin-gonic runs in debug mode without this
ENV GIN_MODE=release ENV GIN_MODE=release

View File

@@ -73,7 +73,7 @@ clean-agent: ## Clean agent.
clean-cli: ## Clean CLI. clean-cli: ## Clean CLI.
@(cd cli; make clean ; echo "CLI cleanup done" ) @(cd cli; make clean ; echo "CLI cleanup done" )
clean-docker: ## Run clen docker clean-docker: ## Run clean docker
@(echo "DOCKER cleanup - NOT IMPLEMENTED YET " ) @(echo "DOCKER cleanup - NOT IMPLEMENTED YET " )
test-lint: ## Run lint on all modules test-lint: ## Run lint on all modules

View File

@@ -103,7 +103,8 @@ func StartPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem,
diagnose.StartMemoryProfiler(os.Getenv(MemoryProfilingDumpPath), os.Getenv(MemoryProfilingTimeIntervalSeconds)) diagnose.StartMemoryProfiler(os.Getenv(MemoryProfilingDumpPath), os.Getenv(MemoryProfilingTimeIntervalSeconds))
} }
go startPassiveTapper(opts, outputItems) streamsMap, assembler := initializePassiveTapper(opts, outputItems)
go startPassiveTapper(streamsMap, assembler)
} }
func UpdateTapTargets(newTapTargets []v1.Pod) { func UpdateTapTargets(newTapTargets []v1.Pod) {
@@ -205,9 +206,8 @@ func initializePacketSources() error {
} }
} }
func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem) { func initializePassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem) (*tcpStreamMap, *tcpAssembler) {
streamsMap := NewTcpStreamMap() streamsMap := NewTcpStreamMap()
go streamsMap.closeTimedoutTcpStreamChannels()
diagnose.InitializeErrorsMap(*debug, *verbose, *quiet) diagnose.InitializeErrorsMap(*debug, *verbose, *quiet)
diagnose.InitializeTapperInternalStats() diagnose.InitializeTapperInternalStats()
@@ -220,6 +220,12 @@ func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem)
assembler := NewTcpAssembler(outputItems, streamsMap, opts) assembler := NewTcpAssembler(outputItems, streamsMap, opts)
return streamsMap, assembler
}
func startPassiveTapper(streamsMap *tcpStreamMap, assembler *tcpAssembler) {
go streamsMap.closeTimedoutTcpStreamChannels()
diagnose.AppStats.SetStartTime(time.Now()) diagnose.AppStats.SetStartTime(time.Now())
staleConnectionTimeout := time.Second * time.Duration(*staleTimeoutSeconds) staleConnectionTimeout := time.Second * time.Duration(*staleTimeoutSeconds)
@@ -253,9 +259,10 @@ func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem)
func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChannelItem, options *api.TrafficFilteringOptions) *tlstapper.TlsTapper { func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChannelItem, options *api.TrafficFilteringOptions) *tlstapper.TlsTapper {
tls := tlstapper.TlsTapper{} tls := tlstapper.TlsTapper{}
tlsPerfBufferSize := os.Getpagesize() * 100 chunksBufferSize := os.Getpagesize() * 100
logBufferSize := os.Getpagesize()
if err := tls.Init(tlsPerfBufferSize, *procfs, extension); err != nil { if err := tls.Init(chunksBufferSize, logBufferSize, *procfs, extension); err != nil {
tlstapper.LogError(err) tlstapper.LogError(err)
return nil return nil
} }
@@ -279,6 +286,7 @@ func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChanne
OutputChannel: outputItems, OutputChannel: outputItems,
} }
go tls.PollForLogging()
go tls.Poll(emitter, options) go tls.Poll(emitter, options)
return &tls return &tls

View File

@@ -7,8 +7,12 @@ Copyright (C) UP9 Inc.
#include "include/headers.h" #include "include/headers.h"
#include "include/util.h" #include "include/util.h"
#include "include/maps.h" #include "include/maps.h"
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h" #include "include/pids.h"
#define IPV4_ADDR_LEN (16)
struct accept_info { struct accept_info {
__u64* sockaddr; __u64* sockaddr;
__u32* addrlen; __u32* addrlen;
@@ -41,9 +45,7 @@ void sys_enter_accept4(struct sys_enter_accept4_ctx *ctx) {
long err = bpf_map_update_elem(&accept_syscall_context, &id, &info, BPF_ANY); long err = bpf_map_update_elem(&accept_syscall_context, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting accept info (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_ACCEPT_INFO, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return;
} }
} }
@@ -70,6 +72,7 @@ void sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
struct accept_info *infoPtr = bpf_map_lookup_elem(&accept_syscall_context, &id); struct accept_info *infoPtr = bpf_map_lookup_elem(&accept_syscall_context, &id);
if (infoPtr == NULL) { if (infoPtr == NULL) {
log_error(ctx, LOG_ERROR_GETTING_ACCEPT_INFO, id, 0l, 0l);
return; return;
} }
@@ -79,15 +82,14 @@ void sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
bpf_map_delete_elem(&accept_syscall_context, &id); bpf_map_delete_elem(&accept_syscall_context, &id);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading accept info from accept syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_ACCEPT_INFO, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
__u32 addrlen; __u32 addrlen;
bpf_probe_read(&addrlen, sizeof(__u32), info.addrlen); bpf_probe_read(&addrlen, sizeof(__u32), info.addrlen);
if (addrlen != 16) { if (addrlen != IPV4_ADDR_LEN) {
// Currently only ipv4 is supported linux-src/include/linux/inet.h // Currently only ipv4 is supported linux-src/include/linux/inet.h
return; return;
} }
@@ -105,9 +107,7 @@ void sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY); err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting fd to address mapping from accept (key: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_FD_MAPPING, id, err, ORIGIN_SYS_EXIT_ACCEPT4_CODE);
bpf_trace_printk(msg, sizeof(msg), key, err);
return;
} }
} }
@@ -145,9 +145,7 @@ void sys_enter_connect(struct sys_enter_connect_ctx *ctx) {
long err = bpf_map_update_elem(&connect_syscall_info, &id, &info, BPF_ANY); long err = bpf_map_update_elem(&connect_syscall_info, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting connect info (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_CONNECT_INFO, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return;
} }
} }
@@ -176,6 +174,7 @@ void sys_exit_connect(struct sys_exit_connect_ctx *ctx) {
struct connect_info *infoPtr = bpf_map_lookup_elem(&connect_syscall_info, &id); struct connect_info *infoPtr = bpf_map_lookup_elem(&connect_syscall_info, &id);
if (infoPtr == NULL) { if (infoPtr == NULL) {
log_error(ctx, LOG_ERROR_GETTING_CONNECT_INFO, id, 0l, 0l);
return; return;
} }
@@ -185,12 +184,11 @@ void sys_exit_connect(struct sys_exit_connect_ctx *ctx) {
bpf_map_delete_elem(&connect_syscall_info, &id); bpf_map_delete_elem(&connect_syscall_info, &id);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading connect info from connect syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_CONNECT_INFO, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
if (info.addrlen != 16) { if (info.addrlen != IPV4_ADDR_LEN) {
// Currently only ipv4 is supported linux-src/include/linux/inet.h // Currently only ipv4 is supported linux-src/include/linux/inet.h
return; return;
} }
@@ -208,8 +206,6 @@ void sys_exit_connect(struct sys_exit_connect_ctx *ctx) {
err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY); err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting fd to address mapping from connect (key: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_FD_MAPPING, id, err, ORIGIN_SYS_EXIT_CONNECT_CODE);
bpf_trace_printk(msg, sizeof(msg), key, err);
return;
} }
} }

View File

@@ -7,6 +7,8 @@ Copyright (C) UP9 Inc.
#include "include/headers.h" #include "include/headers.h"
#include "include/util.h" #include "include/util.h"
#include "include/maps.h" #include "include/maps.h"
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h" #include "include/pids.h"
struct sys_enter_read_ctx { struct sys_enter_read_ctx {
@@ -36,8 +38,7 @@ void sys_enter_read(struct sys_enter_read_ctx *ctx) {
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr); long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading read info from read syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SYS_ENTER_READ_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
@@ -46,9 +47,7 @@ void sys_enter_read(struct sys_enter_read_ctx *ctx) {
err = bpf_map_update_elem(&ssl_read_context, &id, &info, BPF_ANY); err = bpf_map_update_elem(&ssl_read_context, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting file descriptor from read syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, ORIGIN_SYS_ENTER_READ_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return;
} }
} }
@@ -79,8 +78,7 @@ void sys_enter_write(struct sys_enter_write_ctx *ctx) {
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr); long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading write context from write syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SYS_ENTER_WRITE_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
@@ -89,8 +87,6 @@ void sys_enter_write(struct sys_enter_write_ctx *ctx) {
err = bpf_map_update_elem(&ssl_write_context, &id, &info, BPF_ANY); err = bpf_map_update_elem(&ssl_write_context, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting file descriptor from write syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, ORIGIN_SYS_ENTER_WRITE_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return;
} }
} }

View File

@@ -0,0 +1,79 @@
/*
Note: This file is licenced differently from the rest of the project
SPDX-License-Identifier: GPL-2.0
Copyright (C) UP9 Inc.
*/
#ifndef __LOG__
#define __LOG__
// The same consts defined in bpf_logger.go
//
#define LOG_LEVEL_ERROR (0)
#define LOG_LEVEL_INFO (1)
#define LOG_LEVEL_DEBUG (2)
// The same struct can be found in bpf_logger.go
//
// Be careful when editing, alignment and padding should be exactly the same in go/c.
//
struct log_message {
__u32 level;
__u32 message_code;
__u64 arg1;
__u64 arg2;
__u64 arg3;
};
static __always_inline void log_error(void* ctx, __u16 message_code, __u64 arg1, __u64 arg2, __u64 arg3) {
struct log_message entry = {};
entry.level = LOG_LEVEL_ERROR;
entry.message_code = message_code;
entry.arg1 = arg1;
entry.arg2 = arg2;
entry.arg3 = arg3;
long err = bpf_perf_event_output(ctx, &log_buffer, BPF_F_CURRENT_CPU, &entry, sizeof(struct log_message));
if (err != 0) {
char msg[] = "Error writing log error to perf buffer - %ld";
bpf_trace_printk(msg, sizeof(msg), err);
}
}
static __always_inline void log_info(void* ctx, __u16 message_code, __u64 arg1, __u64 arg2, __u64 arg3) {
struct log_message entry = {};
entry.level = LOG_LEVEL_INFO;
entry.message_code = message_code;
entry.arg1 = arg1;
entry.arg2 = arg2;
entry.arg3 = arg3;
long err = bpf_perf_event_output(ctx, &log_buffer, BPF_F_CURRENT_CPU, &entry, sizeof(struct log_message));
if (err != 0) {
char msg[] = "Error writing log info to perf buffer - %ld";
bpf_trace_printk(msg, sizeof(msg), arg1, err);
}
}
static __always_inline void log_debug(void* ctx, __u16 message_code, __u64 arg1, __u64 arg2, __u64 arg3) {
struct log_message entry = {};
entry.level = LOG_LEVEL_DEBUG;
entry.message_code = message_code;
entry.arg1 = arg1;
entry.arg2 = arg2;
entry.arg3 = arg3;
long err = bpf_perf_event_output(ctx, &log_buffer, BPF_F_CURRENT_CPU, &entry, sizeof(struct log_message));
if (err != 0) {
char msg[] = "Error writing log debug to perf buffer - %ld";
bpf_trace_printk(msg, sizeof(msg), arg1, err);
}
}
#endif /* __LOG__ */

View File

@@ -0,0 +1,42 @@
/*
Note: This file is licenced differently from the rest of the project
SPDX-License-Identifier: GPL-2.0
Copyright (C) UP9 Inc.
*/
#ifndef __LOG_MESSAGES__
#define __LOG_MESSAGES__
// Must be synced with bpf_logger_messages.go
//
#define LOG_ERROR_READING_BYTES_COUNT (0)
#define LOG_ERROR_READING_FD_ADDRESS (1)
#define LOG_ERROR_READING_FROM_SSL_BUFFER (2)
#define LOG_ERROR_BUFFER_TOO_BIG (3)
#define LOG_ERROR_ALLOCATING_CHUNK (4)
#define LOG_ERROR_READING_SSL_CONTEXT (5)
#define LOG_ERROR_PUTTING_SSL_CONTEXT (6)
#define LOG_ERROR_GETTING_SSL_CONTEXT (7)
#define LOG_ERROR_MISSING_FILE_DESCRIPTOR (8)
#define LOG_ERROR_PUTTING_FILE_DESCRIPTOR (9)
#define LOG_ERROR_PUTTING_ACCEPT_INFO (10)
#define LOG_ERROR_GETTING_ACCEPT_INFO (11)
#define LOG_ERROR_READING_ACCEPT_INFO (12)
#define LOG_ERROR_PUTTING_FD_MAPPING (13)
#define LOG_ERROR_PUTTING_CONNECT_INFO (14)
#define LOG_ERROR_GETTING_CONNECT_INFO (15)
#define LOG_ERROR_READING_CONNECT_INFO (16)
// Sometimes we have the same error, happening from different locations.
// in order to be able to distinct between them in the log, we add an
// extra number that identify the location. The number can be anything,
// but do not give the same number to different origins.
//
#define ORIGIN_SSL_UPROBE_CODE (0l)
#define ORIGIN_SSL_URETPROBE_CODE (1l)
#define ORIGIN_SYS_ENTER_READ_CODE (2l)
#define ORIGIN_SYS_ENTER_WRITE_CODE (3l)
#define ORIGIN_SYS_EXIT_ACCEPT4_CODE (4l)
#define ORIGIN_SYS_EXIT_CONNECT_CODE (5l)
#endif /* __LOG_MESSAGES__ */

View File

@@ -70,5 +70,6 @@ BPF_LRU_HASH(ssl_write_context, __u64, struct ssl_info);
BPF_LRU_HASH(ssl_read_context, __u64, struct ssl_info); BPF_LRU_HASH(ssl_read_context, __u64, struct ssl_info);
BPF_HASH(file_descriptor_to_ipv4, __u64, struct fd_info); BPF_HASH(file_descriptor_to_ipv4, __u64, struct fd_info);
BPF_PERF_OUTPUT(chunks_buffer); BPF_PERF_OUTPUT(chunks_buffer);
BPF_PERF_OUTPUT(log_buffer);
#endif /* __MAPS__ */ #endif /* __MAPS__ */

View File

@@ -7,6 +7,8 @@ Copyright (C) UP9 Inc.
#include "include/headers.h" #include "include/headers.h"
#include "include/util.h" #include "include/util.h"
#include "include/maps.h" #include "include/maps.h"
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h" #include "include/pids.h"
// Heap-like area for eBPF programs - stack size limited to 512 bytes, we must use maps for bigger (chunk) objects. // Heap-like area for eBPF programs - stack size limited to 512 bytes, we must use maps for bigger (chunk) objects.
@@ -39,15 +41,14 @@ static __always_inline int get_count_bytes(struct pt_regs *ctx, struct ssl_info*
long err = bpf_probe_read(&countBytes, sizeof(size_t), (void*) info->count_ptr); long err = bpf_probe_read(&countBytes, sizeof(size_t), (void*) info->count_ptr);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading bytes count of _ex (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_BYTES_COUNT, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return 0; return 0;
} }
return countBytes; return countBytes;
} }
static __always_inline void add_address_to_chunk(struct tlsChunk* chunk, __u64 id, __u32 fd) { static __always_inline void add_address_to_chunk(struct pt_regs *ctx, struct tlsChunk* chunk, __u64 id, __u32 fd) {
__u32 pid = id >> 32; __u32 pid = id >> 32;
__u64 key = (__u64) pid << 32 | fd; __u64 key = (__u64) pid << 32 | fd;
@@ -61,8 +62,7 @@ static __always_inline void add_address_to_chunk(struct tlsChunk* chunk, __u64 i
chunk->flags |= (fdinfo->flags & FLAGS_IS_CLIENT_BIT); chunk->flags |= (fdinfo->flags & FLAGS_IS_CLIENT_BIT);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading from fd address %ld - %ld"; log_error(ctx, LOG_ERROR_READING_FD_ADDRESS, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
} }
} }
@@ -88,8 +88,7 @@ static __always_inline void send_chunk_part(struct pt_regs *ctx, __u8* buffer, _
} }
if (err != 0) { if (err != 0) {
char msg[] = "Error reading from ssl buffer %ld - %ld"; log_error(ctx, LOG_ERROR_READING_FROM_SSL_BUFFER, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
@@ -101,8 +100,9 @@ static __always_inline void send_chunk(struct pt_regs *ctx, __u8* buffer, __u64
// //
// https://lwn.net/Articles/794934/ // https://lwn.net/Articles/794934/
// //
// If we want to compile in kernel older than 5.3, we should add "#pragma unroll" to this loop // However we want to run in kernel older than 5.3, hence we use "#pragma unroll" anyway
// //
#pragma unroll
for (int i = 0; i < MAX_CHUNKS_PER_OPERATION; i++) { for (int i = 0; i < MAX_CHUNKS_PER_OPERATION; i++) {
if (chunk->len <= (CHUNK_SIZE * i)) { if (chunk->len <= (CHUNK_SIZE * i)) {
break; break;
@@ -120,8 +120,7 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
} }
if (countBytes > (CHUNK_SIZE * MAX_CHUNKS_PER_OPERATION)) { if (countBytes > (CHUNK_SIZE * MAX_CHUNKS_PER_OPERATION)) {
char msg[] = "Buffer too big %d (id: %ld)"; log_error(ctx, LOG_ERROR_BUFFER_TOO_BIG, id, countBytes, 0l);
bpf_trace_printk(msg, sizeof(msg), countBytes, id);
return; return;
} }
@@ -134,8 +133,7 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
chunk = bpf_map_lookup_elem(&heap, &zero); chunk = bpf_map_lookup_elem(&heap, &zero);
if (!chunk) { if (!chunk) {
char msg[] = "Unable to allocate chunk (id: %ld)"; log_error(ctx, LOG_ERROR_ALLOCATING_CHUNK, id, 0l, 0l);
bpf_trace_printk(msg, sizeof(msg), id);
return; return;
} }
@@ -145,11 +143,11 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
chunk->len = countBytes; chunk->len = countBytes;
chunk->fd = info->fd; chunk->fd = info->fd;
add_address_to_chunk(chunk, id, chunk->fd); add_address_to_chunk(ctx, chunk, id, chunk->fd);
send_chunk(ctx, info->buffer, id, chunk); send_chunk(ctx, info->buffer, id, chunk);
} }
static __always_inline void ssl_uprobe(void* ssl, void* buffer, int num, struct bpf_map_def* map_fd, size_t *count_ptr) { static __always_inline void ssl_uprobe(struct pt_regs *ctx, void* ssl, void* buffer, int num, struct bpf_map_def* map_fd, size_t *count_ptr) {
__u64 id = bpf_get_current_pid_tgid(); __u64 id = bpf_get_current_pid_tgid();
if (!should_tap(id >> 32)) { if (!should_tap(id >> 32)) {
@@ -166,8 +164,7 @@ static __always_inline void ssl_uprobe(void* ssl, void* buffer, int num, struct
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr); long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading old ssl context (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SSL_UPROBE_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
} }
if ((bpf_ktime_get_ns() - info.created_at_nano) > SSL_INFO_MAX_TTL_NANO) { if ((bpf_ktime_get_ns() - info.created_at_nano) > SSL_INFO_MAX_TTL_NANO) {
@@ -184,8 +181,7 @@ static __always_inline void ssl_uprobe(void* ssl, void* buffer, int num, struct
long err = bpf_map_update_elem(map_fd, &id, &info, BPF_ANY); long err = bpf_map_update_elem(map_fd, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting ssl context (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_SSL_CONTEXT, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
} }
} }
@@ -199,8 +195,7 @@ static __always_inline void ssl_uretprobe(struct pt_regs *ctx, struct bpf_map_de
struct ssl_info *infoPtr = bpf_map_lookup_elem(map_fd, &id); struct ssl_info *infoPtr = bpf_map_lookup_elem(map_fd, &id);
if (infoPtr == NULL) { if (infoPtr == NULL) {
char msg[] = "Error getting ssl context info (id: %ld)"; log_error(ctx, LOG_ERROR_GETTING_SSL_CONTEXT, id, 0l, 0l);
bpf_trace_printk(msg, sizeof(msg), id);
return; return;
} }
@@ -220,14 +215,12 @@ static __always_inline void ssl_uretprobe(struct pt_regs *ctx, struct bpf_map_de
// bpf_map_delete_elem(map_fd, &id); // bpf_map_delete_elem(map_fd, &id);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading ssl context (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SSL_URETPROBE_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
if (info.fd == -1) { if (info.fd == -1) {
char msg[] = "File descriptor is missing from ssl info (id: %ld)"; log_error(ctx, LOG_ERROR_MISSING_FILE_DESCRIPTOR, id, 0l, 0l);
bpf_trace_printk(msg, sizeof(msg), id);
return; return;
} }
@@ -236,7 +229,7 @@ static __always_inline void ssl_uretprobe(struct pt_regs *ctx, struct bpf_map_de
SEC("uprobe/ssl_write") SEC("uprobe/ssl_write")
void BPF_KPROBE(ssl_write, void* ssl, void* buffer, int num) { void BPF_KPROBE(ssl_write, void* ssl, void* buffer, int num) {
ssl_uprobe(ssl, buffer, num, &ssl_write_context, 0); ssl_uprobe(ctx, ssl, buffer, num, &ssl_write_context, 0);
} }
SEC("uretprobe/ssl_write") SEC("uretprobe/ssl_write")
@@ -246,7 +239,7 @@ void BPF_KPROBE(ssl_ret_write) {
SEC("uprobe/ssl_read") SEC("uprobe/ssl_read")
void BPF_KPROBE(ssl_read, void* ssl, void* buffer, int num) { void BPF_KPROBE(ssl_read, void* ssl, void* buffer, int num) {
ssl_uprobe(ssl, buffer, num, &ssl_read_context, 0); ssl_uprobe(ctx, ssl, buffer, num, &ssl_read_context, 0);
} }
SEC("uretprobe/ssl_read") SEC("uretprobe/ssl_read")
@@ -256,7 +249,7 @@ void BPF_KPROBE(ssl_ret_read) {
SEC("uprobe/ssl_write_ex") SEC("uprobe/ssl_write_ex")
void BPF_KPROBE(ssl_write_ex, void* ssl, void* buffer, size_t num, size_t *written) { void BPF_KPROBE(ssl_write_ex, void* ssl, void* buffer, size_t num, size_t *written) {
ssl_uprobe(ssl, buffer, num, &ssl_write_context, written); ssl_uprobe(ctx, ssl, buffer, num, &ssl_write_context, written);
} }
SEC("uretprobe/ssl_write_ex") SEC("uretprobe/ssl_write_ex")
@@ -266,7 +259,7 @@ void BPF_KPROBE(ssl_ret_write_ex) {
SEC("uprobe/ssl_read_ex") SEC("uprobe/ssl_read_ex")
void BPF_KPROBE(ssl_read_ex, void* ssl, void* buffer, size_t num, size_t *readbytes) { void BPF_KPROBE(ssl_read_ex, void* ssl, void* buffer, size_t num, size_t *readbytes) {
ssl_uprobe(ssl, buffer, num, &ssl_read_context, readbytes); ssl_uprobe(ctx, ssl, buffer, num, &ssl_read_context, readbytes);
} }
SEC("uretprobe/ssl_read_ex") SEC("uretprobe/ssl_read_ex")

View File

@@ -7,6 +7,8 @@ Copyright (C) UP9 Inc.
#include "include/headers.h" #include "include/headers.h"
#include "include/util.h" #include "include/util.h"
#include "include/maps.h" #include "include/maps.h"
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h" #include "include/pids.h"
// To avoid multiple .o files // To avoid multiple .o files

116
tap/tlstapper/bpf_logger.go Normal file
View File

@@ -0,0 +1,116 @@
package tlstapper
import (
"bytes"
"encoding/binary"
"strings"
"github.com/cilium/ebpf/perf"
"github.com/go-errors/errors"
"github.com/up9inc/mizu/shared/logger"
)
const logPrefix = "[bpf] "
// The same consts defined in log.h
//
const logLevelError = 0
const logLevelInfo = 1
const logLevelDebug = 2
type logMessage struct {
Level uint32
MessageCode uint32
Arg1 uint64
Arg2 uint64
Arg3 uint64
}
type bpfLogger struct {
logReader *perf.Reader
}
func newBpfLogger() *bpfLogger {
return &bpfLogger{
logReader: nil,
}
}
func (p *bpfLogger) init(bpfObjects *tlsTapperObjects, bufferSize int) error {
var err error
p.logReader, err = perf.NewReader(bpfObjects.LogBuffer, bufferSize)
if err != nil {
return errors.Wrap(err, 0)
}
return nil
}
func (p *bpfLogger) close() error {
return p.logReader.Close()
}
func (p *bpfLogger) poll() {
logger.Log.Infof("Start polling for bpf logs")
for {
record, err := p.logReader.Read()
if err != nil {
if errors.Is(err, perf.ErrClosed) {
return
}
LogError(errors.Errorf("Error reading from bpf logger perf buffer, aboring logger! %w", err))
return
}
if record.LostSamples != 0 {
logger.Log.Infof("Log buffer is full, dropped %d logs", record.LostSamples)
continue
}
buffer := bytes.NewReader(record.RawSample)
var log logMessage
if err := binary.Read(buffer, binary.LittleEndian, &log); err != nil {
LogError(errors.Errorf("Error parsing log %v", err))
continue
}
p.log(&log)
}
}
func (p *bpfLogger) log(log *logMessage) {
if int(log.MessageCode) >= len(bpfLogMessages) {
logger.Log.Errorf("Unknown message code from bpf logger %d", log.MessageCode)
return
}
format := bpfLogMessages[log.MessageCode]
tokensCount := strings.Count(format, "%")
if tokensCount == 0 {
p.logLevel(log.Level, format)
} else if tokensCount == 1 {
p.logLevel(log.Level, format, log.Arg1)
} else if tokensCount == 2 {
p.logLevel(log.Level, format, log.Arg1, log.Arg2)
} else if tokensCount == 3 {
p.logLevel(log.Level, format, log.Arg1, log.Arg2, log.Arg3)
}
}
func (p *bpfLogger) logLevel(level uint32, format string, args ...interface{}) {
if level == logLevelError {
logger.Log.Errorf(logPrefix+format, args...)
} else if level == logLevelInfo {
logger.Log.Infof(logPrefix+format, args...)
} else if level == logLevelDebug {
logger.Log.Debugf(logPrefix+format, args...)
}
}

View File

@@ -0,0 +1,25 @@
package tlstapper
// Must be synced with logger_messages.h
//
var bpfLogMessages = []string {
/*0000*/ "[%d] Unable to read bytes count from _ex methods [err: %d]",
/*0001*/ "[%d] Unable to read ipv4 address [err: %d]",
/*0002*/ "[%d] Unable to read ssl buffer [err: %d]",
/*0003*/ "[%d] Buffer is too big [size: %d]",
/*0004*/ "[%d] Unable to allocate chunk in bpf heap",
/*0005*/ "[%d] Unable to read ssl context [err: %d] [origin: %d]",
/*0006*/ "[%d] Unable to put ssl context [err: %d]",
/*0007*/ "[%d] Unable to get ssl context",
/*0008*/ "[%d] File descriptor is missing for tls chunk",
/*0009*/ "[%d] Unable to put file descriptor [err: %d] [origin: %d]",
/*0010*/ "[%d] Unable to put accept info [err: %d]",
/*0011*/ "[%d] Unable to get accept info",
/*0012*/ "[%d] Unable to read accept info [err: %d]",
/*0013*/ "[%d] Unable to put file descriptor to address mapping [err: %d] [origin: %d]",
/*0014*/ "[%d] Unable to put connect info [err: %d]",
/*0015*/ "[%d] Unable to get connect info",
/*0016*/ "[%d] Unable to read connect info [err: %d]",
}

View File

@@ -8,6 +8,8 @@ import (
"sync" "sync"
) )
const GLOABL_TAP_PID = 0
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go tlsTapper bpf/tls_tapper.c -- -O2 -g -D__TARGET_ARCH_x86 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go tlsTapper bpf/tls_tapper.c -- -O2 -g -D__TARGET_ARCH_x86
type TlsTapper struct { type TlsTapper struct {
@@ -15,11 +17,12 @@ type TlsTapper struct {
syscallHooks syscallHooks syscallHooks syscallHooks
sslHooksStructs []sslHooks sslHooksStructs []sslHooks
poller *tlsPoller poller *tlsPoller
bpfLogger *bpfLogger
registeredPids sync.Map registeredPids sync.Map
} }
func (t *TlsTapper) Init(bufferSize int, procfs string, extension *api.Extension) error { func (t *TlsTapper) Init(chunksBufferSize int, logBufferSize int, procfs string, extension *api.Extension) error {
logger.Log.Infof("Initializing tls tapper (bufferSize: %v)", bufferSize) logger.Log.Infof("Initializing tls tapper (chunksSize: %d) (logSize: %d)", chunksBufferSize, logBufferSize)
if err := setupRLimit(); err != nil { if err := setupRLimit(); err != nil {
return err return err
@@ -37,16 +40,25 @@ func (t *TlsTapper) Init(bufferSize int, procfs string, extension *api.Extension
t.sslHooksStructs = make([]sslHooks, 0) t.sslHooksStructs = make([]sslHooks, 0)
t.bpfLogger = newBpfLogger()
if err := t.bpfLogger.init(&t.bpfObjects, logBufferSize); err != nil {
return err
}
t.poller = newTlsPoller(t, extension, procfs) t.poller = newTlsPoller(t, extension, procfs)
return t.poller.init(&t.bpfObjects, bufferSize) return t.poller.init(&t.bpfObjects, chunksBufferSize)
} }
func (t *TlsTapper) Poll(emitter api.Emitter, options *api.TrafficFilteringOptions) { func (t *TlsTapper) Poll(emitter api.Emitter, options *api.TrafficFilteringOptions) {
t.poller.poll(emitter, options) t.poller.poll(emitter, options)
} }
func (t *TlsTapper) PollForLogging() {
t.bpfLogger.poll()
}
func (t *TlsTapper) GlobalTap(sslLibrary string) error { func (t *TlsTapper) GlobalTap(sslLibrary string) error {
return t.tapPid(0, sslLibrary) return t.tapPid(GLOABL_TAP_PID, sslLibrary)
} }
func (t *TlsTapper) AddPid(procfs string, pid uint32) error { func (t *TlsTapper) AddPid(procfs string, pid uint32) error {
@@ -74,7 +86,12 @@ func (t *TlsTapper) RemovePid(pid uint32) error {
func (t *TlsTapper) ClearPids() { func (t *TlsTapper) ClearPids() {
t.registeredPids.Range(func(key, v interface{}) bool { t.registeredPids.Range(func(key, v interface{}) bool {
if err := t.RemovePid(key.(uint32)); err != nil { pid := key.(uint32)
if pid == GLOABL_TAP_PID {
return true
}
if err := t.RemovePid(pid); err != nil {
LogError(err) LogError(err)
} }
t.registeredPids.Delete(key) t.registeredPids.Delete(key)
@@ -95,6 +112,10 @@ func (t *TlsTapper) Close() []error {
errors = append(errors, sslHooks.close()...) errors = append(errors, sslHooks.close()...)
} }
if err := t.bpfLogger.close(); err != nil {
errors = append(errors, err)
}
if err := t.poller.close(); err != nil { if err := t.poller.close(); err != nil {
errors = append(errors, err) errors = append(errors, err)
} }

View File

@@ -78,6 +78,7 @@ type tlsTapperMapSpecs struct {
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"` ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"` FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
Heap *ebpf.MapSpec `ebpf:"heap"` Heap *ebpf.MapSpec `ebpf:"heap"`
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
PidsMap *ebpf.MapSpec `ebpf:"pids_map"` PidsMap *ebpf.MapSpec `ebpf:"pids_map"`
SslReadContext *ebpf.MapSpec `ebpf:"ssl_read_context"` SslReadContext *ebpf.MapSpec `ebpf:"ssl_read_context"`
SslWriteContext *ebpf.MapSpec `ebpf:"ssl_write_context"` SslWriteContext *ebpf.MapSpec `ebpf:"ssl_write_context"`
@@ -107,6 +108,7 @@ type tlsTapperMaps struct {
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"` ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"` FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
Heap *ebpf.Map `ebpf:"heap"` Heap *ebpf.Map `ebpf:"heap"`
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
PidsMap *ebpf.Map `ebpf:"pids_map"` PidsMap *ebpf.Map `ebpf:"pids_map"`
SslReadContext *ebpf.Map `ebpf:"ssl_read_context"` SslReadContext *ebpf.Map `ebpf:"ssl_read_context"`
SslWriteContext *ebpf.Map `ebpf:"ssl_write_context"` SslWriteContext *ebpf.Map `ebpf:"ssl_write_context"`
@@ -119,6 +121,7 @@ func (m *tlsTapperMaps) Close() error {
m.ConnectSyscallInfo, m.ConnectSyscallInfo,
m.FileDescriptorToIpv4, m.FileDescriptorToIpv4,
m.Heap, m.Heap,
m.LogBuffer,
m.PidsMap, m.PidsMap,
m.SslReadContext, m.SslReadContext,
m.SslWriteContext, m.SslWriteContext,

Binary file not shown.

View File

@@ -78,6 +78,7 @@ type tlsTapperMapSpecs struct {
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"` ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"` FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
Heap *ebpf.MapSpec `ebpf:"heap"` Heap *ebpf.MapSpec `ebpf:"heap"`
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
PidsMap *ebpf.MapSpec `ebpf:"pids_map"` PidsMap *ebpf.MapSpec `ebpf:"pids_map"`
SslReadContext *ebpf.MapSpec `ebpf:"ssl_read_context"` SslReadContext *ebpf.MapSpec `ebpf:"ssl_read_context"`
SslWriteContext *ebpf.MapSpec `ebpf:"ssl_write_context"` SslWriteContext *ebpf.MapSpec `ebpf:"ssl_write_context"`
@@ -107,6 +108,7 @@ type tlsTapperMaps struct {
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"` ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"` FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
Heap *ebpf.Map `ebpf:"heap"` Heap *ebpf.Map `ebpf:"heap"`
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
PidsMap *ebpf.Map `ebpf:"pids_map"` PidsMap *ebpf.Map `ebpf:"pids_map"`
SslReadContext *ebpf.Map `ebpf:"ssl_read_context"` SslReadContext *ebpf.Map `ebpf:"ssl_read_context"`
SslWriteContext *ebpf.Map `ebpf:"ssl_write_context"` SslWriteContext *ebpf.Map `ebpf:"ssl_write_context"`
@@ -119,6 +121,7 @@ func (m *tlsTapperMaps) Close() error {
m.ConnectSyscallInfo, m.ConnectSyscallInfo,
m.FileDescriptorToIpv4, m.FileDescriptorToIpv4,
m.Heap, m.Heap,
m.LogBuffer,
m.PidsMap, m.PidsMap,
m.SslReadContext, m.SslReadContext,
m.SslWriteContext, m.SslWriteContext,

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{ {
"name": "@up9/mizu-common", "name": "@up9/mizu-common",
"version": "1.0.137", "version": "1.0.145",
"description": "Made with create-react-library", "description": "Made with create-react-library",
"author": "", "author": "",
"license": "MIT", "license": "MIT",
@@ -27,7 +27,7 @@
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60", "@material-ui/lab": "^4.0.0-alpha.60",
"node-sass": "^6.0.0", "node-sass": "^6.0.0",
"react":"^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"recoil": "^0.5.2", "recoil": "^0.5.2",
"react-copy-to-clipboard": "^5.0.3", "react-copy-to-clipboard": "^5.0.3",
@@ -77,7 +77,7 @@
"rollup-plugin-postcss": "^4.0.2", "rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-sass": "^1.2.10", "rollup-plugin-sass": "^1.2.10",
"rollup-plugin-scss": "^3.0.0", "rollup-plugin-scss": "^3.0.0",
"react":"^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"typescript": "^4.2.4" "typescript": "^4.2.4"
}, },

View File

@@ -1,4 +1,4 @@
import { Box, Fade, FormControl, MenuItem, Modal, Backdrop, ListSubheader } from "@material-ui/core"; import { Box, Fade, FormControl, MenuItem, Modal, Backdrop } from "@material-ui/core";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { RedocStandalone } from "redoc"; import { RedocStandalone } from "redoc";
import closeIcon from "assets/closeIcon.svg"; import closeIcon from "assets/closeIcon.svg";
@@ -7,7 +7,8 @@ import style from './OasModal.module.sass';
import openApiLogo from 'assets/openApiLogo.png' import openApiLogo from 'assets/openApiLogo.png'
import { redocThemeOptions } from "./redocThemeOptions"; import { redocThemeOptions } from "./redocThemeOptions";
import React from "react"; import React from "react";
import { UI } from "../.."; import { Select } from "../UI/Select";
const modalStyle = { const modalStyle = {
position: 'absolute', position: 'absolute',
@@ -23,68 +24,44 @@ const modalStyle = {
color: '#000', color: '#000',
}; };
const ipAddressWithPortRegex = new RegExp('([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):([0-9]{1,5})');
const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService }) => { const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService }) => {
const [oasServices, setOasServices] = useState([] as string[]) const [oasServices, setOasServices] = useState([] as string[])
const [selectedServiceName, setSelectedServiceName] = useState(""); const [selectedServiceName, setSelectedServiceName] = useState("");
const [selectedServiceSpec, setSelectedServiceSpec] = useState(null); const [selectedServiceSpec, setSelectedServiceSpec] = useState(null);
const [resolvedServices, setResolvedServices] = useState([]);
const [unResolvedServices, setUnResolvedServices] = useState([]);
const onSelectedOASService = useCallback(async (selectedService) => { const onSelectedOASService = async (selectedService) => {
if (!!selectedService) { if (oasServices.length === 0) {
setSelectedServiceName(selectedService); setSelectedServiceSpec(null);
if (oasServices.length === 0) { setSelectedServiceName("");
return return
} }
try { else {
const data = await getOasByService(selectedService); setSelectedServiceName(selectedService ? selectedService : oasServices[0]);
setSelectedServiceSpec(data); }
try {
const data = await getOasByService(selectedService ? selectedService : oasServices[0]);
setSelectedServiceSpec(data);
} catch (e) { } catch (e) {
toast.error("Error occurred while fetching service OAS spec"); toast.error("Error occurred while fetching service OAS spec");
console.error(e); console.error(e);
} }
} };
}, [oasServices.length])
const resolvedArrayBuilder = useCallback(async (services) => {
const resServices = [];
const unResServices = [];
services.forEach(s => {
if (ipAddressWithPortRegex.test(s)) {
unResServices.push(s);
}
else {
resServices.push(s);
}
});
resServices.sort();
unResServices.sort();
if (resServices.length > 0) {
onSelectedOASService(resServices[0]);
}
else {
onSelectedOASService(unResServices[0]);
}
setResolvedServices(resServices);
setUnResolvedServices(unResServices);
}, [onSelectedOASService])
useEffect(() => { useEffect(() => {
(async () => { (async () => {
try { try {
const services = await getOasServices(); const services = await getOasServices();
resolvedArrayBuilder(services);
setOasServices(services); setOasServices(services);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
})(); })();
}, [openModal, resolvedArrayBuilder]); }, [openModal]);
useEffect(() => {
onSelectedOASService(null);
},[oasServices])
return ( return (
<Modal <Modal
@@ -102,34 +79,27 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
<Box sx={modalStyle}> <Box sx={modalStyle}>
<div className={style.boxContainer}> <div className={style.boxContainer}>
<div className={style.selectHeader}> <div className={style.selectHeader}>
<div><img src={openApiLogo} alt="openApi" className={style.openApilogo} /></div> <div><img src={openApiLogo} alt="openAPI" className={style.openApilogo} /></div>
<div className={style.title}>OpenApi </div> <div className={style.title}>OpenAPI</div>
</div> </div>
<div style={{ cursor: "pointer" }}> <div style={{ cursor: "pointer" }}>
<img src={closeIcon} alt="close" onClick={handleCloseModal} /> <img src={closeIcon} alt="close" onClick={handleCloseModal} />
</div> </div>
</div> </div>
<div className={style.selectContainer} > <div className={style.selectContainer} >
<FormControl> <FormControl>
<UI.Select <Select
labelId="service-select-label" labelId="service-select-label"
id="service-select" id="service-select"
value={selectedServiceName} value={selectedServiceName}
onChangeCb={onSelectedOASService} onChangeCb={onSelectedOASService}
> >
<ListSubheader disableSticky={true}>Resolved</ListSubheader> {oasServices.map((service) => (
{resolvedServices.map((service) => (
<MenuItem key={service} value={service}> <MenuItem key={service} value={service}>
{service} {service}
</MenuItem> </MenuItem>
))} ))}
<ListSubheader disableSticky={true}>UnResolved</ListSubheader> </Select>
{unResolvedServices.map((service) => (
<MenuItem key={service} value={service}>
{service}
</MenuItem>
))}
</UI.Select>
</FormControl> </FormControl>
</div> </div>
<div className={style.borderLine}></div> <div className={style.borderLine}></div>

View File

@@ -1,17 +1,18 @@
import React, {useEffect, useState} from "react"; import React, { useEffect, useState } from "react";
import EntryViewer from "./EntryDetailed/EntryViewer"; import EntryViewer from "./EntryDetailed/EntryViewer";
import {EntryItem} from "./EntryListItem/EntryListItem"; import { EntryItem } from "./EntryListItem/EntryListItem";
import {makeStyles} from "@material-ui/core"; import { makeStyles } from "@material-ui/core";
import Protocol from "../UI/Protocol" import Protocol from "../UI/Protocol"
import Queryable from "../UI/Queryable"; import Queryable from "../UI/Queryable";
import {toast} from "react-toastify"; import { toast } from "react-toastify";
import {RecoilState, useRecoilState, useRecoilValue} from "recoil"; import { RecoilState, useRecoilState, useRecoilValue } from "recoil";
import focusedEntryIdAtom from "../../recoil/focusedEntryId"; import focusedEntryIdAtom from "../../recoil/focusedEntryId";
import trafficViewerApi from "../../recoil/TrafficViewerApi"; import trafficViewerApi from "../../recoil/TrafficViewerApi";
import TrafficViewerApi from "./TrafficViewerApi"; import TrafficViewerApi from "./TrafficViewerApi";
import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom"; import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom";
import queryAtom from "../../recoil/query/atom"; import queryAtom from "../../recoil/query/atom";
import useWindowDimensions, { useRequestTextByWidth } from "../../hooks/WindowDimensionsHook"; import useWindowDimensions, { useRequestTextByWidth } from "../../hooks/WindowDimensionsHook";
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
const useStyles = makeStyles(() => ({ const useStyles = makeStyles(() => ({
entryTitle: { entryTitle: {
@@ -37,24 +38,24 @@ const useStyles = makeStyles(() => ({
export const formatSize = (n: number) => n > 1000 ? `${Math.round(n / 1000)}KB` : `${n} B`; export const formatSize = (n: number) => n > 1000 ? `${Math.round(n / 1000)}KB` : `${n} B`;
const minSizeDisplayRequestSize = 880; const minSizeDisplayRequestSize = 880;
const EntryTitle: React.FC<any> = ({protocol, data, elapsedTime}) => { const EntryTitle: React.FC<any> = ({ protocol, data, elapsedTime }) => {
const classes = useStyles(); const classes = useStyles();
const request = data.request; const request = data.request;
const response = data.response; const response = data.response;
const { width } = useWindowDimensions(); const { width } = useWindowDimensions();
const {requestText, responseText, elapsedTimeText} = useRequestTextByWidth(width) const { requestText, responseText, elapsedTimeText } = useRequestTextByWidth(width)
return <div className={classes.entryTitle}> return <div className={classes.entryTitle}>
<Protocol protocol={protocol} horizontal={true}/> <Protocol protocol={protocol} horizontal={true} />
{(width > minSizeDisplayRequestSize) && <div style={{right: "30px", position: "absolute", display: "flex"}}> {(width > minSizeDisplayRequestSize) && <div style={{ right: "30px", position: "absolute", display: "flex" }}>
{request && <Queryable {request && <Queryable
query={`requestSize == ${data.requestSize}`} query={`requestSize == ${data.requestSize}`}
style={{margin: "0 18px"}} style={{ margin: "0 18px" }}
displayIconOnMouseOver={true} displayIconOnMouseOver={true}
> >
<div <div
style={{opacity: 0.5}} style={{ opacity: 0.5 }}
id="entryDetailedTitleRequestSize" id="entryDetailedTitleRequestSize"
> >
{`${requestText}${formatSize(data.requestSize)}`} {`${requestText}${formatSize(data.requestSize)}`}
@@ -62,11 +63,11 @@ const EntryTitle: React.FC<any> = ({protocol, data, elapsedTime}) => {
</Queryable>} </Queryable>}
{response && <Queryable {response && <Queryable
query={`responseSize == ${data.responseSize}`} query={`responseSize == ${data.responseSize}`}
style={{margin: "0 18px"}} style={{ margin: "0 18px" }}
displayIconOnMouseOver={true} displayIconOnMouseOver={true}
> >
<div <div
style={{opacity: 0.5}} style={{ opacity: 0.5 }}
id="entryDetailedTitleResponseSize" id="entryDetailedTitleResponseSize"
> >
{`${responseText}${formatSize(data.responseSize)}`} {`${responseText}${formatSize(data.responseSize)}`}
@@ -74,11 +75,11 @@ const EntryTitle: React.FC<any> = ({protocol, data, elapsedTime}) => {
</Queryable>} </Queryable>}
{response && <Queryable {response && <Queryable
query={`elapsedTime >= ${elapsedTime}`} query={`elapsedTime >= ${elapsedTime}`}
style={{margin: "0 0 0 18px"}} style={{ margin: "0 0 0 18px" }}
displayIconOnMouseOver={true} displayIconOnMouseOver={true}
> >
<div <div
style={{opacity: 0.5}} style={{ opacity: 0.5 }}
id="entryDetailedTitleElapsedTime" id="entryDetailedTitleElapsedTime"
> >
{`${elapsedTimeText}${Math.round(elapsedTime)}ms`} {`${elapsedTimeText}${Math.round(elapsedTime)}ms`}
@@ -88,7 +89,7 @@ const EntryTitle: React.FC<any> = ({protocol, data, elapsedTime}) => {
</div>; </div>;
}; };
const EntrySummary: React.FC<any> = ({entry}) => { const EntrySummary: React.FC<any> = ({ entry }) => {
return <EntryItem return <EntryItem
key={`entry-${entry.id}`} key={`entry-${entry.id}`}
entry={entry} entry={entry}
@@ -117,14 +118,10 @@ export const EntryDetailed = () => {
} catch (error) { } catch (error) {
if (error.response?.data?.type) { if (error.response?.data?.type) {
toast[error.response.data.type](`Entry[${focusedEntryId}]: ${error.response.data.msg}`, { toast[error.response.data.type](`Entry[${focusedEntryId}]: ${error.response.data.msg}`, {
position: "bottom-right",
theme: "colored", theme: "colored",
autoClose: error.response.data.autoClose, autoClose: error.response.data.autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined, progress: undefined,
containerId: TOAST_CONTAINER_ID
}); });
} }
console.error(error); console.error(error);
@@ -139,7 +136,7 @@ export const EntryDetailed = () => {
data={entryData.data} data={entryData.data}
elapsedTime={entryData.data.elapsedTime} elapsedTime={entryData.data.elapsedTime}
/>} />}
{entryData && <EntrySummary entry={entryData.base}/>} {entryData && <EntrySummary entry={entryData.base} />}
<React.Fragment> <React.Fragment>
{entryData && <EntryViewer {entryData && <EntryViewer
representation={entryData.representation} representation={entryData.representation}

View File

@@ -4,7 +4,7 @@ import SwapHorizIcon from '@material-ui/icons/SwapHoriz';
import styles from './EntryListItem.module.sass'; import styles from './EntryListItem.module.sass';
import StatusCode, {getClassification, StatusCodeClassification} from "../../UI/StatusCode"; import StatusCode, {getClassification, StatusCodeClassification} from "../../UI/StatusCode";
import Protocol, {ProtocolInterface} from "../../UI/Protocol" import Protocol, {ProtocolInterface} from "../../UI/Protocol"
import eBPFLogo from '../../assets/ebpf.png'; import eBPFLogo from 'assets/lock.svg';
import {Summary} from "../../UI/Summary"; import {Summary} from "../../UI/Summary";
import Queryable from "../../UI/Queryable"; import Queryable from "../../UI/Queryable";
import ingoingIconSuccess from "assets/ingoing-traffic-success.svg" import ingoingIconSuccess from "assets/ingoing-traffic-success.svg"

View File

@@ -0,0 +1,3 @@
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.33333 6.36364H8.66667V6.36011H10.6667V6.36364H11C11.2778 6.36364 11.5139 6.45644 11.7083 6.64205C11.9028 6.82765 12 7.05303 12 7.31818V13.0455C12 13.3106 11.9028 13.536 11.7083 13.7216C11.5139 13.9072 11.2778 14 11 14H1C0.722222 14 0.486111 13.9072 0.291666 13.7216C0.0972223 13.536 0 13.3106 0 13.0455V7.31818C0 7.05303 0.0972223 6.82765 0.291666 6.64205C0.486111 6.45644 0.722222 6.36364 1 6.36364H1.33333V4.45455C1.33333 3.23485 1.79167 2.1875 2.70833 1.3125C3.625 0.4375 4.72222 0 6 0C7.27778 0 8.375 0.4375 9.29167 1.3125C9.92325 1.91538 10.3373 2.60007 10.5337 3.36658L8.59659 3.85085C8.48731 3.40176 8.25026 3.00309 7.88542 2.65483C7.36458 2.15767 6.73611 1.90909 6 1.90909C5.26389 1.90909 4.63542 2.15767 4.11458 2.65483C3.59375 3.15199 3.33333 3.75189 3.33333 4.45455V6.36364Z" fill="#BCCEFD"/>
</svg>

After

Width:  |  Height:  |  Size: 959 B

View File

@@ -8,8 +8,7 @@ import { EntryDetailed } from "./EntryDetailed";
import playIcon from 'assets/run.svg'; import playIcon from 'assets/run.svg';
import pauseIcon from 'assets/pause.svg'; import pauseIcon from 'assets/pause.svg';
import variables from '../../variables.module.scss'; import variables from '../../variables.module.scss';
import { toast,ToastContainer } from 'react-toastify'; import { toast, ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { RecoilRoot, RecoilState, useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; import { RecoilRoot, RecoilState, useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
import entriesAtom from "../../recoil/entries"; import entriesAtom from "../../recoil/entries";
@@ -20,6 +19,7 @@ import trafficViewerApiAtom from "../../recoil/TrafficViewerApi"
import TrafficViewerApi from "./TrafficViewerApi"; import TrafficViewerApi from "./TrafficViewerApi";
import { StatusBar } from "../UI/StatusBar"; import { StatusBar } from "../UI/StatusBar";
import tappingStatusAtom from "../../recoil/tappingStatus/atom"; import tappingStatusAtom from "../../recoil/tappingStatus/atom";
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
const useLayoutStyles = makeStyles(() => ({ const useLayoutStyles = makeStyles(() => ({
details: { details: {
@@ -47,14 +47,14 @@ interface TrafficViewerProps {
trafficViewerApiProp: TrafficViewerApi, trafficViewerApiProp: TrafficViewerApi,
actionButtons?: JSX.Element, actionButtons?: JSX.Element,
isShowStatusBar?: boolean, isShowStatusBar?: boolean,
webSocketUrl : string, webSocketUrl: string,
isCloseWebSocket : boolean, isCloseWebSocket: boolean,
isDemoBannerView : boolean isDemoBannerView: boolean
} }
export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus, trafficViewerApiProp, export const TrafficViewer: React.FC<TrafficViewerProps> = ({ setAnalyzeStatus, trafficViewerApiProp,
actionButtons,isShowStatusBar,webSocketUrl, actionButtons, isShowStatusBar, webSocketUrl,
isCloseWebSocket, isDemoBannerView}) => { isCloseWebSocket, isDemoBannerView }) => {
const classes = useLayoutStyles(); const classes = useLayoutStyles();
@@ -106,12 +106,31 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
handleQueryChange(query); handleQueryChange(query);
}, [query, handleQueryChange]); }, [query, handleQueryChange]);
useEffect(()=>{ useEffect(() => {
isCloseWebSocket && closeWebSocket() isCloseWebSocket && closeWebSocket()
},[isCloseWebSocket]) }, [isCloseWebSocket])
useEffect(() => {
reopenConnection()
}, [webSocketUrl])
const ws = useRef(null); const ws = useRef(null);
const openEmptyWebSocket = () => {
if (query) {
openWebSocket(`(${query}) and leftOff(-1)`, true);
} else {
openWebSocket(`leftOff(-1)`, true);
}
}
const closeWebSocket = () => {
if(ws?.current?.readyState === WebSocket.OPEN) {
ws.current.close();
return true;
}
}
const listEntry = useRef(null); const listEntry = useRef(null);
const openWebSocket = (query: string, resetEntries: boolean) => { const openWebSocket = (query: string, resetEntries: boolean) => {
if (resetEntries) { if (resetEntries) {
@@ -126,7 +145,7 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
sendQueryWhenWsOpen(query); sendQueryWhenWsOpen(query);
ws.current.onclose = () => { ws.current.onclose = () => {
if(window.location.pathname === "/") if (window.location.pathname === "/")
setForceRender(forceRender + 1); setForceRender(forceRender + 1);
} }
ws.current.onerror = (event) => { ws.current.onerror = (event) => {
@@ -140,25 +159,19 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
openWebSocket(`leftOff(${leftOffBottom})`, false); openWebSocket(`leftOff(${leftOffBottom})`, false);
} }
} }
} catch (e) {} } catch (e) { }
} }
const sendQueryWhenWsOpen = (query) => { const sendQueryWhenWsOpen = (query) => {
setTimeout(() => { setTimeout(() => {
if (ws?.current?.readyState === WebSocket.OPEN) { if (ws?.current?.readyState === WebSocket.OPEN) {
ws.current.send(JSON.stringify({"query": query, "enableFullEntries": false})); ws.current.send(JSON.stringify({ "query": query, "enableFullEntries": false }));
} else { } else {
sendQueryWhenWsOpen(query); sendQueryWhenWsOpen(query);
} }
}, 500) }, 500)
} }
const closeWebSocket = () => {
if(ws?.current?.readyState === WebSocket.OPEN) {
ws.current.close();
}
}
if (ws.current) { if (ws.current) {
ws.current.onmessage = (e) => { ws.current.onmessage = (e) => {
if (!e?.data) return; if (!e?.data) return;
@@ -186,14 +199,11 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
break; break;
case "toast": case "toast":
toast[message.data.type](message.data.text, { toast[message.data.type](message.data.text, {
position: "bottom-right",
theme: "colored", theme: "colored",
autoClose: message.data.autoClose, autoClose: message.data.autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true, pauseOnHover: true,
draggable: true,
progress: undefined, progress: undefined,
containerId: TOAST_CONTAINER_ID
}); });
break; break;
case "queryMetadata": case "queryMetadata":
@@ -217,13 +227,12 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
} }
useEffect(() => { useEffect(() => {
setTrafficViewerApiState({...trafficViewerApiProp, webSocket : {close : closeWebSocket}}); setTrafficViewerApiState({ ...trafficViewerApiProp, webSocket: { close: closeWebSocket } });
(async () => { (async () => {
openWebSocket("leftOff(-1)", true);
try{ try{
const tapStatusResponse = await trafficViewerApiProp.tapStatus(); const tapStatusResponse = await trafficViewerApiProp.tapStatus();
setTappingStatus(tapStatusResponse); setTappingStatus(tapStatusResponse);
if(setAnalyzeStatus) { if (setAnalyzeStatus) {
const analyzeStatusResponse = await trafficViewerApiProp.analyzeStatus(); const analyzeStatusResponse = await trafficViewerApiProp.analyzeStatus();
setAnalyzeStatus(analyzeStatusResponse); setAnalyzeStatus(analyzeStatusResponse);
} }
@@ -235,19 +244,18 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
}, []); }, []);
const toggleConnection = () => { const toggleConnection = () => {
if(ws?.current?.readyState === WebSocket.OPEN) { if(!closeWebSocket()) {
ws?.current?.close(); openEmptyWebSocket();
} else {
if (query) {
openWebSocket(`(${query}) and leftOff(-1)`, true);
} else {
openWebSocket(`leftOff(-1)`, true);
}
scrollableRef.current.jumpToBottom(); scrollableRef.current.jumpToBottom();
setIsSnappedToBottom(true); setIsSnappedToBottom(true);
} }
} }
const reopenConnection = async () => {
closeWebSocket()
openEmptyWebSocket();
}
useEffect(() => { useEffect(() => {
return () => { return () => {
ws.current.close(); ws.current.close();
@@ -349,19 +357,28 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
setAddressesWithTLS={setAddressesWithTLS} setAddressesWithTLS={setAddressesWithTLS}
userDismissedTLSWarning={userDismissedTLSWarning} userDismissedTLSWarning={userDismissedTLSWarning}
setUserDismissedTLSWarning={setUserDismissedTLSWarning} /> setUserDismissedTLSWarning={setUserDismissedTLSWarning} />
<ToastContainer/>
</div> </div>
); );
}; };
const MemoiedTrafficViewer = React.memo(TrafficViewer) const MemoiedTrafficViewer = React.memo(TrafficViewer)
const TrafficViewerContainer: React.FC<TrafficViewerProps> = ({ setAnalyzeStatus, trafficViewerApiProp, const TrafficViewerContainer: React.FC<TrafficViewerProps> = ({ setAnalyzeStatus, trafficViewerApiProp,
actionButtons, isShowStatusBar = true , actionButtons, isShowStatusBar = true,
webSocketUrl, isCloseWebSocket, isDemoBannerView}) => { webSocketUrl, isCloseWebSocket, isDemoBannerView }) => {
return <RecoilRoot> return <RecoilRoot>
<MemoiedTrafficViewer actionButtons={actionButtons} isShowStatusBar={isShowStatusBar} webSocketUrl={webSocketUrl} <MemoiedTrafficViewer actionButtons={actionButtons} isShowStatusBar={isShowStatusBar} webSocketUrl={webSocketUrl}
isCloseWebSocket={isCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp} isCloseWebSocket={isCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp}
setAnalyzeStatus={setAnalyzeStatus} isDemoBannerView={isDemoBannerView} /> setAnalyzeStatus={setAnalyzeStatus} isDemoBannerView={isDemoBannerView} />
<ToastContainer enableMultiContainer containerId={TOAST_CONTAINER_ID}
position="bottom-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover />
</RecoilRoot> </RecoilRoot>
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -6,11 +6,11 @@ export interface Props {
disabled?: boolean; disabled?: boolean;
} }
const Checkbox: React.FC<Props> = ({checked, onToggle, disabled}) => { const Checkbox: React.FC<Props> = ({checked, onToggle, disabled, ...props}) => {
return ( return (
<div> <div>
<input style={!disabled ? {cursor: "pointer"}: {}} type="checkbox" checked={checked} disabled={disabled} onChange={(event) => onToggle(event.target.checked)}/> <input style={!disabled ? {cursor: "pointer"}: {}} type="checkbox" checked={checked} disabled={disabled} onChange={(event) => onToggle(event.target.checked)} {...props}/>
</div> </div>
); );
}; };

View File

@@ -11,7 +11,7 @@ export interface InformationIconProps{
export const InformationIcon: React.FC<InformationIconProps> = ({link,style}) => { export const InformationIcon: React.FC<InformationIconProps> = ({link,style}) => {
return <React.Fragment> return <React.Fragment>
<a href={DEFUALT_LINK ? DEFUALT_LINK : link} style={style} className={styles.flex} title="documentation"> <a href={DEFUALT_LINK ? DEFUALT_LINK : link} style={style} className={styles.flex} title="documentation" target="_blank">
<img className="headerIcon" src={infoImg} alt="Info icon"/> <img className="headerIcon" src={infoImg} alt="Info icon"/>
</a> </a>
</React.Fragment> </React.Fragment>

View File

@@ -0,0 +1,39 @@
.highlighterContainer
&.fitScreen
pre
max-height: 90vh
overflow: auto
pre
code
font-size: 0.75rem
&:first-child
margin-right: 0.75rem
background: #F7F9FC
.react-syntax-highlighter-line-number
color: rgb(98, 126, 247)
&:last-child
display: block
code.hljs
white-space: pre-wrap
code.hljs:before
counter-reset: listing
.hljsMarkerLine
counter-increment: listing
.hljsMarkerLine:before
content: counter(listing) " "
display: inline-block
width: 3rem
padding-left: auto
margin-left: auto
text-align: right
opacity: .5

View File

@@ -1,49 +0,0 @@
.highlighterContainer {
&.fitScreen {
pre {
max-height: 90vh;
overflow: auto;
}
}
pre {
code {
font-size: 0.75rem;
&:first-child {
margin-right: 0.75rem;
background: #F7F9FC;
.react-syntax-highlighter-line-number {
color: rgb(98, 126, 247);
}
}
&:last-child {
display: block;
}
}
}
}
code.hljs {
white-space: pre-wrap;
}
code.hljs:before {
counter-reset: listing;
}
code.hljs .hljs-marker-line {
counter-increment: listing;
}
code.hljs .hljs-marker-line:before {
content: counter(listing) " ";
display: inline-block;
width: 3rem;
padding-left: auto;
margin-left: auto;
text-align: right;
opacity: .5;
}

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import Lowlight from 'react-lowlight' import Lowlight from 'react-lowlight'
import 'highlight.js/styles/atom-one-light.css' import 'highlight.js/styles/atom-one-light.css'
import './index.scss'; import styles from './index.module.sass';
import xml from 'highlight.js/lib/languages/xml' import xml from 'highlight.js/lib/languages/xml'
import json from 'highlight.js/lib/languages/json' import json from 'highlight.js/lib/languages/json'
@@ -37,11 +37,11 @@ export const SyntaxHighlighter: React.FC<Props> = ({
const markers = showLineNumbers ? code.split("\n").map((item, i) => { const markers = showLineNumbers ? code.split("\n").map((item, i) => {
return { return {
line: i + 1, line: i + 1,
className: 'hljs-marker-line' className: styles.hljsMarkerLine
} }
}) : []; }) : [];
return <div style={{fontSize: ".75rem"}}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>; return <div style={{fontSize: ".75rem"}} className={styles.highlighterContainer}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>;
}; };
export default SyntaxHighlighter; export default SyntaxHighlighter;

View File

@@ -1,3 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill="#627EF7" d="M12 4.707c-2.938-1.83-7.416-2.567-12-2.707v17.714c3.937.12 7.795.681 10.667 1.995.846.388 1.817.388 2.667 0 2.872-1.314 6.729-1.875 10.666-1.995v-17.714c-4.584.14-9.062.877-12 2.707zm-10 13.104v-13.704c5.157.389 7.527 1.463 9 2.334v13.168c-1.525-.546-4.716-1.505-9-1.798zm20 0c-4.283.293-7.475 1.252-9 1.799v-13.171c1.453-.861 3.83-1.942 9-2.332v13.704z"/> <path d="M19 21H6.14286C5.07143 21 4 20.32 4 18.96C4 17.6 5.07143 16.92 6.14286 16.92H19V4H6.14286C5.07143 4 4 5.02 4 6.04V18.96M16.8571 17.6V20.32V17.6Z" stroke="#627EF7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg> <rect x="8" y="7" width="7" height="2" fill="#627EF7"/>
<rect x="8" y="11" width="4" height="2" fill="#627EF7"/>
</svg>

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1 @@
export const TOAST_CONTAINER_ID = "Common";

4657
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@
"@types/jest": "^26.0.22", "@types/jest": "^26.0.22",
"@types/node": "^12.20.10", "@types/node": "^12.20.10",
"@uiw/react-textarea-code-editor": "^1.4.12", "@uiw/react-textarea-code-editor": "^1.4.12",
"@up9/mizu-common": "1.0.137", "@up9/mizu-common": "1.0.145",
"axios": "^0.25.0", "axios": "^0.25.0",
"core-js": "^3.20.2", "core-js": "^3.20.2",
"craco-babel-loader": "^1.0.3", "craco-babel-loader": "^1.0.3",

View File

@@ -44,7 +44,7 @@ const trafficViewerApi = {...api}
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton} className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
style={{ marginRight: 25, textTransform: 'unset' }} style={{ marginRight: 25, textTransform: 'unset' }}
onClick={handleOpenOasModal}> onClick={handleOpenOasModal}>
OpenApi Specs OpenAPI Specs
</Button>} </Button>}
{window["isServiceMapEnabled"] && <Button {window["isServiceMapEnabled"] && <Button
startIcon={<img src={serviceMap} className="custom" alt="service-map" style={{marginRight:"8%"}}></img>} startIcon={<img src={serviceMap} className="custom" alt="service-map" style={{marginRight:"8%"}}></img>}

View File

@@ -11,6 +11,7 @@ import ServiceMapOptions from './ServiceMapOptions'
import { useCommonStyles } from "../../helpers/commonStyle"; import { useCommonStyles } from "../../helpers/commonStyle";
import refresh from "../assets/refresh.svg"; import refresh from "../assets/refresh.svg";
import close from "../assets/close.svg"; import close from "../assets/close.svg";
import { TOAST_CONTAINER_ID } from "../../consts";
interface GraphData { interface GraphData {
nodes: Node[]; nodes: Node[];
@@ -140,7 +141,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
setGraphData(newGraphData) setGraphData(newGraphData)
} catch (ex) { } catch (ex) {
toast.error("An error occurred while loading Mizu Service Map, see console for mode details"); toast.error("An error occurred while loading Mizu Service Map, see console for mode details", { containerId: TOAST_CONTAINER_ID });
console.error(ex); console.error(ex);
} finally { } finally {
setIsLoading(false) setIsLoading(false)
@@ -176,20 +177,20 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} /> <img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
</div>} </div>}
{!isLoading && <div style={{ height: "100%", width: "100%" }}> {!isLoading && <div style={{ height: "100%", width: "100%" }}>
<div style={{ display: "flex", justifyContent: "space-between" }}> <div style={{ display: "flex", justifyContent: "space-between" }}>
<div> <div>
<Button <Button
startIcon={<img src={refresh} className="custom" alt="refresh" style={{ marginRight:"8%"}}></img>} startIcon={<img src={refresh} className="custom" alt="refresh" style={{ marginRight: "8%" }}></img>}
size="medium" size="medium"
variant="contained" variant="contained"
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton} className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
onClick={refreshServiceMap} onClick={refreshServiceMap}
> >
Refresh Refresh
</Button> </Button>
</div>
<img src={close} alt="close" onClick={() => onClose()} style={{ cursor: "pointer" }}></img>
</div> </div>
<img src={close} alt="close" onClick={() => onClose()} style={{cursor:"pointer"}}></img>
</div>
<Graph <Graph
graph={graphData} graph={graphData}
options={ServiceMapOptions} options={ServiceMapOptions}

View File

@@ -1 +1,2 @@
export const adminUsername = "admin"; export const adminUsername = "admin";
export const TOAST_CONTAINER_ID = "Community";

View File

@@ -1,14 +1,15 @@
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import './index.sass'; import './index.sass';
import {ToastContainer} from "react-toastify"; import {ToastContainer} from "react-toastify";
import 'react-toastify/dist/ReactToastify.css'; import 'react-toastify/dist/ReactToastify.min.css';
import {RecoilRoot} from "recoil"; import {RecoilRoot} from "recoil";
import App from './App'; import App from './App';
import { TOAST_CONTAINER_ID } from './consts';
ReactDOM.render( <> ReactDOM.render( <>
<RecoilRoot> <RecoilRoot>
<App/> <App/>
<ToastContainer <ToastContainer enableMultiContainer containerId={TOAST_CONTAINER_ID}
position="bottom-right" position="bottom-right"
autoClose={5000} autoClose={5000}
hideProgressBar={false} hideProgressBar={false}