Compare commits

..

13 Commits

Author SHA1 Message Date
M. Mert Yıldıran
e52ba1f05d Add AF_PACKET support (#1052)
* Add `AF_PACKET` support

* Update `.gitignore`

* Support both `libpcap` and `AF_PACKET` at the same time

* Fix linter errors

* Fix a bug that introduced while fixing a linter error

* Revert the changes related to `MaxBufferedPages` prefixed consts

* #run_acceptance_tests

* #run_acceptance_tests

* Revert channel buffer size #run_acceptance_tests

* Revert "Revert channel buffer size #run_acceptance_tests"

This reverts commit e62c3844cd.

* Increase `cy.wait` from `500` to `1000` #run_acceptance_tests

* Fix the `pcapHandle` handle

* Revert "Increase `cy.wait` from `500` to `1000` #run_acceptance_tests"

This reverts commit 938c550e72.

* #run_acceptance_tests

* Handle the merge conflicts

* Add `AF_XDP` support

* Implement `Close()` of `AF_XDP` and fix linter errors

* Fix `NewIPProtoProgram` function and internet protocol number

* Pipe the packet stream from every network interface using `*pcapgo.NgReader` and `*pcapgo.NgWriter`

Implement `SetDecoder` and `SetBPF` methods.

* Fix `NewNgReader` call

* Implement `Stats` method

* Rebroadcast to the XDP socket

* Add `-packet-capture` flag and make `AF_PACKET`, `AF_XDP` optional

* #run_acceptance_tests

* Fix `newAfXdpHandle` method

* #run_acceptance_tests

* Update tap/xdp/ipproto.c

Co-authored-by: Nimrod Gilboa Markevich <59927337+nimrod-up9@users.noreply.github.com>

* Update tap/xdp/ipproto.c

Co-authored-by: Nimrod Gilboa Markevich <59927337+nimrod-up9@users.noreply.github.com>

* Update tap/xdp/ipproto.c

Co-authored-by: Nimrod Gilboa Markevich <59927337+nimrod-up9@users.noreply.github.com>

* Fix several issues

* Update tap/xdp/ipproto.c

Co-authored-by: Nimrod Gilboa Markevich <59927337+nimrod-up9@users.noreply.github.com>

* Fix `ipproto.c`

* Remove `AF_XDP`

* Comment on frameSize

Co-authored-by: Nimrod Gilboa Markevich <59927337+nimrod-up9@users.noreply.github.com>
2022-08-08 13:48:19 +03:00
gadotroee
378270ee3d Change tests to use github repo (#1211)
remove goole related stuff
2022-07-26 15:23:37 +03:00
Nimrod Gilboa Markevich
692c500b0f Improve Go TLS address availability (#1207)
Fetch source and destination addresses with bpf from tcp kprobes, similar to how it is done for openssl lib.
Chunk contains both source address and destination address.
FD is no longer used to obtain addresses.
2022-07-19 14:31:27 +03:00
gadotroee
5525214d0a Fix memory of acceptanceTests minikube cluster (#1209) 2022-07-19 12:55:44 +03:00
RoyIsland
efd414a2ed Removed telemetry (#1208) 2022-07-19 12:29:48 +03:00
AmitUp9
b3e79ff244 Grooming Traffic Stats Modal - change font and time picker position (#1206)
* font change and time picker position update

* add font-family to variables scss
2022-07-17 17:19:44 +03:00
AmitUp9
d4b9fea5a7 fix elastic time picker ui css (#1204) 2022-07-14 11:11:05 +03:00
leon-up9
d11770681b full height (#1202)
Co-authored-by: Leon <>
2022-07-13 18:37:22 +03:00
gadotroee
e9719cba3a Add time range to stats (#1199) 2022-07-13 17:21:18 +03:00
leon-up9
15f7b889e2 height change (#1201)
Co-authored-by: Leon <>
2022-07-13 13:37:08 +03:00
RoyUP9
d98ac0e8f7 Removed redundant IgnoredUserAgents field (#1198) 2022-07-12 20:41:42 +03:00
gadotroee
a3c236ff0a Fix colors map initialization (#1200) 2022-07-12 20:05:21 +03:00
gadotroee
4b280ecd6d Hide Response tab if there is no response (#1197) 2022-07-12 18:38:39 +03:00
77 changed files with 60779 additions and 11276 deletions

View File

@@ -52,15 +52,3 @@ jobs:
- name: Test
run: make acceptance-test
- name: Slack notification on failure
uses: ravsamhq/notify-slack-action@v1
if: always()
with:
status: ${{ job.status }}
notification_title: 'Mizu {workflow} has {status_message}'
message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit <{commit_url}|{commit_sha}> by ${{ github.event.head_commit.author.name }} <${{ github.event.head_commit.author.email }}> ```${{ github.event.head_commit.message }}```'
footer: 'Linked Repo <{repo_url}|{repo}>'
notify_when: 'failure'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

View File

@@ -85,94 +85,6 @@ jobs:
GIT_BRANCH=${{ steps.version_parameters.outputs.branch }}
COMMIT_HASH=${{ github.sha }}
gcp-registry:
name: Push Docker image to GCR
runs-on: ubuntu-latest
strategy:
max-parallel: 2
fail-fast: false
matrix:
target:
- amd64
- arm64v8
steps:
- name: Check out the repo
uses: actions/checkout@v2
- id: 'auth'
uses: 'google-github-actions/auth@v0'
with:
credentials_json: '${{ secrets.GCR_JSON_KEY }}'
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@v0'
- name: Determine versioning strategy
uses: haya14busa/action-cond@v1
id: condval
with:
cond: ${{ github.ref == 'refs/heads/main' }}
if_true: "stable"
if_false: "dev"
- name: Auto Increment Ver Action
uses: docker://igorgov/auto-inc-ver:v2.0.0
id: versioning
with:
mode: ${{ steps.condval.outputs.value }}
suffix: 'dev'
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Get version parameters
shell: bash
run: |
echo "##[set-output name=build_timestamp;]$(echo $(date +%s))"
echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
id: version_parameters
- name: Get base image name
shell: bash
run: echo "##[set-output name=image;]$(echo gcr.io/up9-docker-hub/mizu/${GITHUB_REF#refs/heads/})"
id: base_image_step
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: |
${{ steps.base_image_step.outputs.image }}
tags: |
type=raw,${{ steps.versioning.outputs.version }}
type=raw,value=latest,enable=${{ steps.condval.outputs.value == 'stable' }}
type=raw,value=dev-latest,enable=${{ steps.condval.outputs.value == 'dev' }}
flavor: |
latest=auto
prefix=
suffix=-${{ matrix.target }},onlatest=true
- name: Login to GCR
uses: docker/login-action@v1
with:
registry: gcr.io
username: _json_key
password: ${{ secrets.GCR_JSON_KEY }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
TARGETARCH=${{ matrix.target }}
VER=${{ steps.versioning.outputs.version }}
BUILD_TIMESTAMP=${{ steps.version_parameters.outputs.build_timestamp }}
GIT_BRANCH=${{ steps.version_parameters.outputs.branch }}
COMMIT_HASH=${{ github.sha }}
docker-manifest:
name: Create and Push a Docker Manifest
runs-on: ubuntu-latest
@@ -232,7 +144,7 @@ jobs:
cli:
name: Build the CLI and publish
runs-on: ubuntu-latest
needs: [docker-manifest, gcp-registry]
needs: [docker-manifest]
steps:
- name: Set up Go 1.17
uses: actions/setup-go@v2
@@ -290,15 +202,3 @@ jobs:
tag: ${{ steps.versioning.outputs.version }}
prerelease: ${{ github.ref != 'refs/heads/main' }}
bodyFile: 'cli/bin/README.md'
- name: Slack notification on failure
uses: ravsamhq/notify-slack-action@v1
if: always()
with:
status: ${{ job.status }}
notification_title: 'Mizu enterprise {workflow} has {status_message}'
message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit <{commit_url}|{commit_sha}> by ${{ github.event.head_commit.author.name }} <${{ github.event.head_commit.author.email }}> ```${{ github.event.head_commit.message }}```'
footer: 'Linked Repo <{repo_url}|{repo}>'
notify_when: 'failure'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

1
.gitignore vendored
View File

@@ -53,6 +53,7 @@ tap/extensions/*/expect
**/node_modules/**
**/dist/**
*.editorconfig
ui/up9-mizu-common-0.0.0.tgz
# Ignore *.log files
*.log

View File

@@ -104,8 +104,7 @@ ARG BUILD_TIMESTAMP
ARG VER=0.0
WORKDIR /app/tap/tlstapper
RUN rm tlstapper_bpf*
RUN rm *_bpfel_*
RUN GOARCH=${BUILDARCH} go generate tls_tapper.go
WORKDIR /app/agent-build

View File

@@ -18,7 +18,6 @@ require (
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-logr/logr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/google/gofuzz v1.2.0 // indirect
@@ -29,7 +28,6 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/up9inc/mizu/logger v0.0.0 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220207234003-57398862261d // indirect

View File

@@ -206,7 +206,6 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=

View File

@@ -27,7 +27,7 @@ else
fi
echo "Starting minikube..."
minikube start --cpus 2 --memory 6946
minikube start --cpus 2 --memory 6000
echo "Creating mizu tests namespaces"
kubectl create namespace mizu-tests --dry-run=client -o yaml | kubectl apply -f -

View File

@@ -215,12 +215,11 @@ func DeleteKubeFile(kubeContext string, namespace string, filename string) error
func getDefaultCommandArgs() []string {
agentImageValue := os.Getenv("MIZU_CI_IMAGE")
setFlag := "--set"
telemetry := "telemetry=false"
agentImage := fmt.Sprintf("agent-image=%s", agentImageValue)
imagePullPolicy := "image-pull-policy=IfNotPresent"
headless := "headless=true"
return []string{setFlag, telemetry, setFlag, agentImage, setFlag, imagePullPolicy, setFlag, headless}
return []string{setFlag, agentImage, setFlag, imagePullPolicy, setFlag, headless}
}
func GetDefaultTapCommandArgs() []string {

View File

@@ -48,7 +48,6 @@ require (
github.com/Masterminds/semver v1.5.0 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/beevik/etree v1.1.0 // indirect
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b // indirect
github.com/cilium/ebpf v0.9.0 // indirect

View File

@@ -101,7 +101,6 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=

View File

@@ -1,7 +1,10 @@
package controllers
import (
"fmt"
"net/http"
"strconv"
"time"
core "k8s.io/api/core/v1"
@@ -80,7 +83,24 @@ func GetGeneralStats(c *gin.Context) {
}
func GetTrafficStats(c *gin.Context) {
c.JSON(http.StatusOK, providers.GetTrafficStats())
startTime, endTime, err := getStartEndTime(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, providers.GetTrafficStats(startTime, endTime))
}
func getStartEndTime(c *gin.Context) (time.Time, time.Time, error) {
startTimeValue, err := strconv.Atoi(c.Query("startTimeMs"))
if err != nil {
return time.UnixMilli(0), time.UnixMilli(0), fmt.Errorf("invalid start time: %v", err)
}
endTimeValue, err := strconv.Atoi(c.Query("endTimeMs"))
if err != nil {
return time.UnixMilli(0), time.UnixMilli(0), fmt.Errorf("invalid end time: %v", err)
}
return time.UnixMilli(int64(startTimeValue)), time.UnixMilli(int64(endTimeValue)), nil
}
func GetCurrentResolvingInformation(c *gin.Context) {

View File

@@ -5,7 +5,6 @@ import (
"encoding/hex"
"fmt"
"reflect"
"strings"
"sync"
"time"
@@ -82,13 +81,12 @@ func GetGeneralStats() *GeneralStats {
func InitProtocolToColor(protocolMap map[string]*api.Protocol) {
for item, value := range protocolMap {
splitted := strings.SplitN(item, "/", 3)
protocolToColor[splitted[len(splitted)-1]] = value.BackgroundColor
protocolToColor[api.GetProtocolSummary(item).Abbreviation] = value.BackgroundColor
}
}
func GetTrafficStats() *TrafficStatsResponse {
bucketsStatsCopy := getBucketStatsCopy()
func GetTrafficStats(startTime time.Time, endTime time.Time) *TrafficStatsResponse {
bucketsStatsCopy := getFilteredBucketStatsCopy(startTime, endTime)
return &TrafficStatsResponse{
Protocols: getAvailableProtocols(bucketsStatsCopy),
@@ -264,7 +262,7 @@ func convertAccumulativeStatsDictToArray(methodsPerProtocolAggregated map[string
return protocolsData
}
func getBucketStatsCopy() BucketStats {
func getFilteredBucketStatsCopy(startTime time.Time, endTime time.Time) BucketStats {
bucketStatsCopy := BucketStats{}
bucketStatsLocker.Lock()
if err := copier.Copy(&bucketStatsCopy, bucketsStats); err != nil {
@@ -272,7 +270,18 @@ func getBucketStatsCopy() BucketStats {
return nil
}
bucketStatsLocker.Unlock()
return bucketStatsCopy
filteredBucketStatsCopy := BucketStats{}
interval := InternalBucketThreshold
for _, bucket := range bucketStatsCopy {
if (bucket.BucketTime.After(startTime.Add(-1*interval/2).Round(interval)) && bucket.BucketTime.Before(endTime.Add(-1*interval/2).Round(interval))) ||
bucket.BucketTime.Equal(startTime.Add(-1*interval/2).Round(interval)) ||
bucket.BucketTime.Equal(endTime.Add(-1*interval/2).Round(interval)) {
filteredBucketStatsCopy = append(filteredBucketStatsCopy, bucket)
}
}
return filteredBucketStatsCopy
}
func getAggregatedResultTiming(stats BucketStats, interval time.Duration) map[time.Time]map[string]map[string]*AccumulativeStatsCounter {

View File

@@ -4,9 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
"github.com/up9inc/mizu/cli/utils"
@@ -93,45 +91,3 @@ func (provider *Provider) ReportTappedPods(pods []core.Pod) error {
}
}
}
func (provider *Provider) GetGeneralStats() (map[string]interface{}, error) {
generalStatsUrl := fmt.Sprintf("%s/status/general", provider.url)
response, requestErr := utils.Get(generalStatsUrl, provider.client)
if requestErr != nil {
return nil, fmt.Errorf("failed to get general stats for telemetry, err: %w", requestErr)
}
defer response.Body.Close()
data, readErr := ioutil.ReadAll(response.Body)
if readErr != nil {
return nil, fmt.Errorf("failed to read general stats for telemetry, err: %v", readErr)
}
var generalStats map[string]interface{}
if parseErr := json.Unmarshal(data, &generalStats); parseErr != nil {
return nil, fmt.Errorf("failed to parse general stats for telemetry, err: %v", parseErr)
}
return generalStats, nil
}
func (provider *Provider) GetVersion() (string, error) {
versionUrl, _ := url.Parse(fmt.Sprintf("%s/metadata/version", provider.url))
req := &http.Request{
Method: http.MethodGet,
URL: versionUrl,
}
statusResp, err := utils.Do(req, provider.client)
if err != nil {
return "", err
}
defer statusResp.Body.Close()
versionResponse := &shared.VersionResponse{}
if err := json.NewDecoder(statusResp.Body).Decode(&versionResponse); err != nil {
return "", err
}
return versionResponse.Ver, nil
}

View File

@@ -4,7 +4,6 @@ import (
"github.com/creasty/defaults"
"github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/logger"
)
@@ -12,7 +11,6 @@ var checkCmd = &cobra.Command{
Use: "check",
Short: "Check the Mizu installation for potential problems",
RunE: func(cmd *cobra.Command, args []string) error {
go telemetry.ReportRun("check", nil)
runMizuCheck()
return nil
},

View File

@@ -2,14 +2,12 @@ package cmd
import (
"github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/telemetry"
)
var cleanCmd = &cobra.Command{
Use: "clean",
Short: "Removes all mizu resources",
RunE: func(cmd *cobra.Command, args []string) error {
go telemetry.ReportRun("clean", nil)
performCleanCommand()
return nil
},

View File

@@ -7,7 +7,6 @@ import (
"github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/logger"
)
@@ -16,8 +15,6 @@ var configCmd = &cobra.Command{
Use: "config",
Short: "Generate config with default values",
RunE: func(cmd *cobra.Command, args []string) error {
go telemetry.ReportRun("config", config.Config.Config)
configWithDefaults, err := config.GetConfigWithDefaults()
if err != nil {
logger.Log.Errorf("Failed generating config with defaults, err: %v", err)

View File

@@ -4,7 +4,6 @@ import (
"github.com/creasty/defaults"
"github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/logger"
)
@@ -12,7 +11,6 @@ var installCmd = &cobra.Command{
Use: "install",
Short: "Installs mizu components",
RunE: func(cmd *cobra.Command, args []string) error {
go telemetry.ReportRun("install", nil)
runMizuInstall()
return nil
},

View File

@@ -9,7 +9,6 @@ import (
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/errormessage"
"github.com/up9inc/mizu/cli/mizu/fsUtils"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/logger"
)
@@ -17,8 +16,6 @@ var logsCmd = &cobra.Command{
Use: "logs",
Short: "Create a zip file with logs for Github issue or troubleshoot",
RunE: func(cmd *cobra.Command, args []string) error {
go telemetry.ReportRun("logs", config.Config.Logs)
kubernetesProvider, err := getKubernetesProviderForCli()
if err != nil {
return nil

View File

@@ -9,7 +9,6 @@ import (
"time"
"github.com/up9inc/mizu/cli/resources"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/cli/utils"
core "k8s.io/api/core/v1"
@@ -109,8 +108,6 @@ func RunMizuTap() {
}
func finishTapExecution(kubernetesProvider *kubernetes.Provider) {
telemetry.ReportTapTelemetry(apiProvider, config.Config.Tap, state.startTime)
finishMizuExecution(kubernetesProvider, config.Config.IsNsRestrictedMode(), config.Config.MizuResourcesNamespace)
}
@@ -126,7 +123,6 @@ func getTapMizuAgentConfig() *shared.MizuAgentConfig {
AgentDatabasePath: shared.DataDirPath,
ServiceMap: config.Config.ServiceMap,
OAS: config.Config.OAS,
Telemetry: config.Config.Telemetry,
}
return &mizuAgentConfig
@@ -151,17 +147,18 @@ func printTappedPodsPreview(ctx context.Context, kubernetesProvider *kubernetes.
}
}
func startTapperSyncer(ctx context.Context, cancel context.CancelFunc, provider *kubernetes.Provider, targetNamespaces []string, mizuApiFilteringOptions api.TrafficFilteringOptions, startTime time.Time) error {
func startTapperSyncer(ctx context.Context, cancel context.CancelFunc, provider *kubernetes.Provider, targetNamespaces []string, startTime time.Time) error {
tapperSyncer, err := kubernetes.CreateAndStartMizuTapperSyncer(ctx, provider, kubernetes.TapperSyncerConfig{
TargetNamespaces: targetNamespaces,
PodFilterRegex: *config.Config.Tap.PodRegex(),
MizuResourcesNamespace: config.Config.MizuResourcesNamespace,
AgentImage: config.Config.AgentImage,
TapperResources: config.Config.Tap.TapperResources,
ImagePullPolicy: config.Config.ImagePullPolicy(),
LogLevel: config.Config.LogLevel(),
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
MizuApiFilteringOptions: mizuApiFilteringOptions,
TargetNamespaces: targetNamespaces,
PodFilterRegex: *config.Config.Tap.PodRegex(),
MizuResourcesNamespace: config.Config.MizuResourcesNamespace,
AgentImage: config.Config.AgentImage,
TapperResources: config.Config.Tap.TapperResources,
ImagePullPolicy: config.Config.ImagePullPolicy(),
LogLevel: config.Config.LogLevel(),
MizuApiFilteringOptions: api.TrafficFilteringOptions{
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
},
MizuServiceAccountExists: state.mizuServiceAccountExists,
ServiceMesh: config.Config.Tap.ServiceMesh,
Tls: config.Config.Tap.Tls,
@@ -229,12 +226,6 @@ func getErrorDisplayTextForK8sTapManagerError(err kubernetes.K8sTapManagerError)
}
}
func getMizuApiFilteringOptions() (*api.TrafficFilteringOptions, error) {
return &api.TrafficFilteringOptions{
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
}, nil
}
func watchApiServerPod(ctx context.Context, kubernetesProvider *kubernetes.Provider, cancel context.CancelFunc) {
podExactRegex := regexp.MustCompile(fmt.Sprintf("^%s$", kubernetes.ApiServerPodName))
podWatchHelper := kubernetes.NewPodWatchHelper(kubernetesProvider, podExactRegex)
@@ -352,8 +343,7 @@ func watchApiServerEvents(ctx context.Context, kubernetesProvider *kubernetes.Pr
func postApiServerStarted(ctx context.Context, kubernetesProvider *kubernetes.Provider, cancel context.CancelFunc) {
startProxyReportErrorIfAny(kubernetesProvider, ctx, cancel, config.Config.Tap.GuiPort)
options, _ := getMizuApiFilteringOptions()
if err := startTapperSyncer(ctx, cancel, kubernetesProvider, state.targetNamespaces, *options, state.startTime); err != nil {
if err := startTapperSyncer(ctx, cancel, kubernetesProvider, state.targetNamespaces, state.startTime); err != nil {
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error starting mizu tapper syncer: %v", errormessage.FormatError(err)))
cancel()
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/logger"
"github.com/creasty/defaults"
@@ -18,8 +17,6 @@ var versionCmd = &cobra.Command{
Use: "version",
Short: "Print version info",
RunE: func(cmd *cobra.Command, args []string) error {
go telemetry.ReportRun("version", config.Config.Version)
if config.Config.Version.DebugInfo {
timeStampInt, _ := strconv.ParseInt(mizu.BuildTimestamp, 10, 0)
logger.Log.Infof("Version: %s \nBranch: %s (%s)", mizu.Ver, mizu.Branch, mizu.GitCommitHash)

View File

@@ -3,9 +3,7 @@ package cmd
import (
"github.com/creasty/defaults"
"github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/logger"
)
@@ -13,7 +11,6 @@ var viewCmd = &cobra.Command{
Use: "view",
Short: "Open GUI in browser",
RunE: func(cmd *cobra.Command, args []string) error {
go telemetry.ReportRun("view", config.Config.View)
runMizuView()
return nil
},

View File

@@ -31,7 +31,6 @@ type ConfigStruct struct {
AgentImage string `yaml:"agent-image,omitempty" readonly:""`
ImagePullPolicyStr string `yaml:"image-pull-policy" default:"Always"`
MizuResourcesNamespace string `yaml:"mizu-resources-namespace" default:"mizu"`
Telemetry bool `yaml:"telemetry" default:"true"`
DumpLogs bool `yaml:"dump-logs" default:"false"`
KubeConfigPathStr string `yaml:"kube-config-path"`
KubeContext string `yaml:"kube-context"`

View File

@@ -51,6 +51,7 @@ type TapConfig struct {
TapperResources shared.Resources `yaml:"tapper-resources"`
ServiceMesh bool `yaml:"service-mesh" default:"false"`
Tls bool `yaml:"tls" default:"false"`
PacketCapture string `yaml:"packet-capture" default:"libpcap"`
Profiler bool `yaml:"profiler" default:"false"`
MaxLiveStreams int `yaml:"max-live-streams" default:"500"`
}

View File

@@ -4,7 +4,6 @@ go 1.17
require (
github.com/creasty/defaults v1.5.2
github.com/denisbrodbeck/machineid v1.0.1
github.com/google/go-github/v37 v37.0.0
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/spf13/cobra v1.3.0

View File

@@ -145,8 +145,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=

View File

@@ -14,8 +14,6 @@ var (
Platform = ""
)
const DEVENVVAR = "MIZU_DISABLE_TELEMTRY"
func GetMizuFolderPath() string {
home, homeDirErr := os.UserHomeDir()
if homeDirErr != nil {

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"io/ioutil"
"net/http"
"os"
"runtime"
"strings"
"time"
@@ -18,10 +17,6 @@ import (
)
func CheckNewerVersion(versionChan chan string) {
if _, present := os.LookupEnv(mizu.DEVENVVAR); present {
versionChan <- ""
return
}
logger.Log.Debugf("Checking for newer version...")
start := time.Now()
client := github.NewClient(nil)

View File

@@ -1,97 +0,0 @@
package telemetry
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
"github.com/denisbrodbeck/machineid"
"github.com/up9inc/mizu/cli/apiserver"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/mizu"
"github.com/up9inc/mizu/logger"
)
const telemetryUrl = "https://us-east4-up9-prod.cloudfunctions.net/mizu-telemetry"
func ReportRun(cmd string, args interface{}) {
if !shouldRunTelemetry() {
logger.Log.Debug("not reporting telemetry")
return
}
argsBytes, _ := json.Marshal(args)
argsMap := map[string]interface{}{
"cmd": cmd,
"args": string(argsBytes),
}
if err := sendTelemetry(argsMap); err != nil {
logger.Log.Debug(err)
return
}
logger.Log.Debugf("successfully reported telemetry for cmd %v", cmd)
}
func ReportTapTelemetry(apiProvider *apiserver.Provider, args interface{}, startTime time.Time) {
if !shouldRunTelemetry() {
logger.Log.Debug("not reporting telemetry")
return
}
generalStats, err := apiProvider.GetGeneralStats()
if err != nil {
logger.Log.Debugf("[ERROR] failed to get general stats from api server %v", err)
return
}
argsBytes, _ := json.Marshal(args)
argsMap := map[string]interface{}{
"cmd": "tap",
"args": string(argsBytes),
"executionTimeInSeconds": int(time.Since(startTime).Seconds()),
"apiCallsCount": generalStats["EntriesCount"],
"trafficVolumeInGB": generalStats["EntriesVolumeInGB"],
}
if err := sendTelemetry(argsMap); err != nil {
logger.Log.Debug(err)
return
}
logger.Log.Debug("successfully reported telemetry of tap command")
}
func shouldRunTelemetry() bool {
if _, present := os.LookupEnv(mizu.DEVENVVAR); present {
return false
}
if !config.Config.Telemetry {
return false
}
return mizu.Branch == "main" || mizu.Branch == "develop"
}
func sendTelemetry(argsMap map[string]interface{}) error {
argsMap["component"] = "mizu_cli"
argsMap["buildTimestamp"] = mizu.BuildTimestamp
argsMap["branch"] = mizu.Branch
argsMap["version"] = mizu.Ver
argsMap["platform"] = mizu.Platform
if machineId, err := machineid.ProtectedID("mizu"); err == nil {
argsMap["machineId"] = machineId
}
jsonValue, _ := json.Marshal(argsMap)
if resp, err := http.Post(telemetryUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil {
return fmt.Errorf("ERROR: failed sending telemetry, err: %v, response %v", err, resp)
}
return nil
}

View File

@@ -43,7 +43,6 @@ type TapperSyncerConfig struct {
TapperResources shared.Resources
ImagePullPolicy core.PullPolicy
LogLevel logging.Level
IgnoredUserAgents []string
MizuApiFilteringOptions api.TrafficFilteringOptions
MizuServiceAccountExists bool
ServiceMesh bool

View File

@@ -43,7 +43,6 @@ type MizuAgentConfig struct {
AgentDatabasePath string `json:"agentDatabasePath"`
ServiceMap bool `json:"serviceMap"`
OAS OASConfig `json:"oas"`
Telemetry bool `json:"telemetry"`
}
type WebSocketMessageMetadata struct {

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"net"
"strings"
"sync"
"time"
@@ -25,6 +26,15 @@ func (protocol *ProtocolSummary) ToString() string {
return fmt.Sprintf("%s?%s?%s", protocol.Name, protocol.Version, protocol.Abbreviation)
}
func GetProtocolSummary(inputString string) *ProtocolSummary {
splitted := strings.SplitN(inputString, "?", 3)
return &ProtocolSummary{
Name: splitted[0],
Version: splitted[1],
Abbreviation: splitted[2],
}
}
type Protocol struct {
ProtocolSummary
LongName string `json:"longName"`

View File

@@ -8,9 +8,7 @@ test-update: test-pull-bin
@MIZU_TEST=1 TEST_UPDATE=1 go test -v ./... -coverpkg=./... -coverprofile=coverage.out -covermode=atomic
test-pull-bin:
@mkdir -p bin
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp gs://static.up9.io/mizu/test-pcap/bin/amqp/\*.bin bin
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || ../get-folder-of-tests.sh bin/amqp bin
test-pull-expect:
@mkdir -p expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect16/amqp/\* expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || ../get-folder-of-tests.sh expect/amqp expect

View File

@@ -0,0 +1,24 @@
mizu_test_files_repo=https://github.com/up9inc/mizu-tests-data
mizu_test_files_tmp_folder="mizu-test-files-tmp"
requested_folder=$1
destination_folder_name=$2
echo "Going to download folder (${requested_folder}) from repo (${mizu_test_files_repo}) and save it to local folder (${destination_folder_name})"
echo "Cloning repo to tmp folder (${mizu_test_files_tmp_folder})"
git clone ${mizu_test_files_repo} ${mizu_test_files_tmp_folder} --no-checkout --depth 1 --filter=blob:none --sparse
cd ${mizu_test_files_tmp_folder}
echo "Adding sparse checkout folder"
git sparse-checkout add ${requested_folder}
echo "Checkout"
git checkout
echo "Moving folder to the destination location"
mv ${requested_folder} ../${destination_folder_name}
cd ..
echo "Removing the tmp folder"
rm -rf ${mizu_test_files_tmp_folder}

View File

@@ -8,9 +8,7 @@ test-update: test-pull-bin
@MIZU_TEST=1 TEST_UPDATE=1 go test -v ./... -coverpkg=./... -coverprofile=coverage.out -covermode=atomic
test-pull-bin:
@mkdir -p bin
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp gs://static.up9.io/mizu/test-pcap/bin/http/\*.bin bin
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || ../get-folder-of-tests.sh bin/http bin
test-pull-expect:
@mkdir -p expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect16/http/\* expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || ../get-folder-of-tests.sh expect/http expect

View File

@@ -8,9 +8,7 @@ test-update: test-pull-bin
@MIZU_TEST=1 TEST_UPDATE=1 go test -v ./... -coverpkg=./... -coverprofile=coverage.out -covermode=atomic
test-pull-bin:
@mkdir -p bin
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp gs://static.up9.io/mizu/test-pcap/bin/kafka/\*.bin bin
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || ../get-folder-of-tests.sh bin/kafka bin
test-pull-expect:
@mkdir -p expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect15/kafka/\* expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || ../get-folder-of-tests.sh expect/kafka expect

View File

@@ -8,9 +8,7 @@ test-update: test-pull-bin
@MIZU_TEST=1 TEST_UPDATE=1 go test -v ./... -coverpkg=./... -coverprofile=coverage.out -covermode=atomic
test-pull-bin:
@mkdir -p bin
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp gs://static.up9.io/mizu/test-pcap/bin/redis/\*.bin bin
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || ../get-folder-of-tests.sh bin/redis bin
test-pull-expect:
@mkdir -p expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect15/redis/\* expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || ../get-folder-of-tests.sh expect/redis expect

View File

@@ -16,6 +16,7 @@ require (
github.com/up9inc/mizu/tap/api v0.0.0
github.com/up9inc/mizu/tap/dbgctl v0.0.0
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
k8s.io/api v0.23.3
)
@@ -33,7 +34,6 @@ require (
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sys v0.0.0-20220207234003-57398862261d // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect

View File

@@ -51,11 +51,13 @@ var maxLiveStreams = flag.Int("max-live-streams", 500, "Maximum live streams to
var iface = flag.String("i", "en0", "Interface to read packets from")
var fname = flag.String("r", "", "Filename to read from, overrides -i")
var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet")
var tstype = flag.String("timestamp_type", "", "Type of timestamps to use")
var targetSizeMb = flag.Int("target-size-mb", 8, "AF_PACKET target block size (MB)")
var tstype = flag.String("timestamp-type", "", "Type of timestamps to use")
var promisc = flag.Bool("promisc", true, "Set promiscuous mode")
var staleTimeoutSeconds = flag.Int("staletimout", 120, "Max time in seconds to keep connections which don't transmit data")
var servicemesh = flag.Bool("servicemesh", false, "Record decrypted traffic if the cluster is configured with a service mesh and with mtls")
var tls = flag.Bool("tls", false, "Enable TLS tapper")
var packetCapture = flag.String("packet-capture", "libpcap", "Packet capture backend. Possible values: libpcap, af_packet")
var memprofile = flag.String("memprofile", "", "Write memory profile")
@@ -210,16 +212,17 @@ func initializePacketSources() error {
}
behaviour := source.TcpPacketSourceBehaviour{
SnapLength: *snaplen,
Promisc: *promisc,
Tstype: *tstype,
DecoderName: *decoder,
Lazy: *lazy,
BpfFilter: bpffilter,
SnapLength: *snaplen,
TargetSizeMb: *targetSizeMb,
Promisc: *promisc,
Tstype: *tstype,
DecoderName: *decoder,
Lazy: *lazy,
BpfFilter: bpffilter,
}
var err error
packetSourceManager, err = source.NewPacketSourceManager(*procfs, *fname, *iface, *servicemesh, tapTargets, behaviour, !*nodefrag, mainPacketInputChan)
packetSourceManager, err = source.NewPacketSourceManager(*procfs, *fname, *iface, *servicemesh, tapTargets, behaviour, !*nodefrag, *packetCapture, mainPacketInputChan)
return err
}

View File

@@ -0,0 +1,152 @@
package source
import (
"fmt"
"os"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/afpacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"golang.org/x/net/bpf"
)
type afPacketHandle struct {
source gopacket.ZeroCopyPacketDataSource
capture *afpacket.TPacket
decoder gopacket.Decoder
decodeOptions gopacket.DecodeOptions
}
func (h *afPacketHandle) NextPacket() (packet gopacket.Packet, err error) {
var data []byte
var ci gopacket.CaptureInfo
data, ci, err = h.source.ZeroCopyReadPacketData()
if err != nil {
return
}
packet = gopacket.NewPacket(data, h.decoder, h.decodeOptions)
m := packet.Metadata()
m.CaptureInfo = ci
m.Truncated = m.Truncated || ci.CaptureLength < ci.Length
return
}
func (h *afPacketHandle) SetDecoder(decoder gopacket.Decoder, lazy bool, noCopy bool) {
h.decoder = decoder
h.decodeOptions = gopacket.DecodeOptions{Lazy: lazy, NoCopy: noCopy}
}
func (h *afPacketHandle) SetBPF(expr string) (err error) {
var pcapBPF []pcap.BPFInstruction
pcapBPF, err = pcap.CompileBPFFilter(layers.LinkTypeEthernet, 65535, expr)
if err != nil {
return
}
bpfIns := []bpf.RawInstruction{}
for _, ins := range pcapBPF {
bpfIns2 := bpf.RawInstruction{
Op: ins.Code,
Jt: ins.Jt,
Jf: ins.Jf,
K: ins.K,
}
bpfIns = append(bpfIns, bpfIns2)
}
err = h.capture.SetBPF(bpfIns)
return
}
func (h *afPacketHandle) LinkType() layers.LinkType {
return layers.LinkTypeEthernet
}
func (h *afPacketHandle) Stats() (packetsReceived uint, packetsDropped uint, err error) {
var stats afpacket.SocketStatsV3
_, stats, err = h.capture.SocketStats()
packetsReceived = stats.Packets()
packetsDropped = stats.Drops()
return
}
func (h *afPacketHandle) Close() (err error) {
h.capture.Close()
return
}
func newAfpacketHandle(device string, targetSizeMb int, snaplen int) (handle Handle, err error) {
snaplen -= 1
if snaplen < 0 {
snaplen = 0
}
szFrame, szBlock, numBlocks, err := afpacketComputeSize(targetSizeMb, snaplen, os.Getpagesize())
if err != nil {
return
}
var capture *afpacket.TPacket
capture, err = newAfpacket(device, szFrame, szBlock, numBlocks, false, pcap.BlockForever)
if err != nil {
return
}
handle = &afPacketHandle{
capture: capture,
source: gopacket.ZeroCopyPacketDataSource(capture),
}
return
}
func newAfpacket(device string, snaplen int, block_size int, num_blocks int,
useVLAN bool, timeout time.Duration) (*afpacket.TPacket, error) {
var h *afpacket.TPacket
var err error
if device == "any" {
h, err = afpacket.NewTPacket(
afpacket.OptFrameSize(snaplen),
afpacket.OptBlockSize(block_size),
afpacket.OptNumBlocks(num_blocks),
afpacket.OptAddVLANHeader(useVLAN),
afpacket.OptPollTimeout(timeout),
afpacket.SocketRaw,
afpacket.TPacketVersion3)
} else {
h, err = afpacket.NewTPacket(
afpacket.OptInterface(device),
afpacket.OptFrameSize(snaplen),
afpacket.OptBlockSize(block_size),
afpacket.OptNumBlocks(num_blocks),
afpacket.OptAddVLANHeader(useVLAN),
afpacket.OptPollTimeout(timeout),
afpacket.SocketRaw,
afpacket.TPacketVersion3)
}
return h, err
}
// afpacketComputeSize computes the block_size and the num_blocks in such a way that the
// allocated mmap buffer is close to but smaller than target_size_mb.
// The restriction is that the block_size must be divisible by both the
// frame size and page size.
func afpacketComputeSize(targetSizeMb int, snaplen int, pageSize int) (
frameSize int, blockSize int, numBlocks int, err error) {
// frameSize calculation was taken from gopacket's afpacket.go
if snaplen < pageSize {
frameSize = pageSize / (pageSize / snaplen)
} else {
frameSize = (snaplen/pageSize + 1) * pageSize
}
// 128 is the default from the gopacket library so just use that
blockSize = frameSize * 128
numBlocks = (targetSizeMb * 1024 * 1024) / blockSize
if numBlocks == 0 {
return 0, 0, 0, fmt.Errorf("Interface buffersize is too small")
}
return frameSize, blockSize, numBlocks, nil
}

97
tap/source/handle_pcap.go Normal file
View File

@@ -0,0 +1,97 @@
package source
import (
"fmt"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
type pcapHandle struct {
source *gopacket.PacketSource
capture *pcap.Handle
}
func (h *pcapHandle) NextPacket() (packet gopacket.Packet, err error) {
return h.source.NextPacket()
}
func (h *pcapHandle) SetDecoder(decoder gopacket.Decoder, lazy bool, noCopy bool) {
h.source = gopacket.NewPacketSource(h.capture, decoder)
h.source.Lazy = lazy
h.source.NoCopy = noCopy
}
func (h *pcapHandle) SetBPF(expr string) (err error) {
return h.capture.SetBPFFilter(expr)
}
func (h *pcapHandle) LinkType() layers.LinkType {
return h.capture.LinkType()
}
func (h *pcapHandle) Stats() (packetsReceived uint, packetsDropped uint, err error) {
var stats *pcap.Stats
stats, err = h.capture.Stats()
packetsReceived = uint(stats.PacketsReceived)
packetsDropped = uint(stats.PacketsDropped)
return
}
func (h *pcapHandle) Close() (err error) {
h.capture.Close()
return
}
func newPcapHandle(filename string, device string, snaplen int, promisc bool, tstype string) (handle Handle, err error) {
var capture *pcap.Handle
if filename != "" {
if capture, err = pcap.OpenOffline(filename); err != nil {
err = fmt.Errorf("PCAP OpenOffline error: %v", err)
return
}
} else {
// This is a little complicated because we want to allow all possible options
// for creating the packet capture handle... instead of all this you can
// just call pcap.OpenLive if you want a simple handle.
var inactive *pcap.InactiveHandle
inactive, err = pcap.NewInactiveHandle(device)
if err != nil {
err = fmt.Errorf("could not create: %v", err)
return
}
defer inactive.CleanUp()
if err = inactive.SetSnapLen(snaplen); err != nil {
err = fmt.Errorf("could not set snap length: %v", err)
return
} else if err = inactive.SetPromisc(promisc); err != nil {
err = fmt.Errorf("could not set promisc mode: %v", err)
return
} else if err = inactive.SetTimeout(time.Second); err != nil {
err = fmt.Errorf("could not set timeout: %v", err)
return
}
if tstype != "" {
var t pcap.TimestampSource
if t, err = pcap.TimestampSourceFromString(tstype); err != nil {
err = fmt.Errorf("supported timestamp types: %v", inactive.SupportedTimestamps())
return
} else if err = inactive.SetTimestampSource(t); err != nil {
err = fmt.Errorf("supported timestamp types: %v", inactive.SupportedTimestamps())
return
}
}
if capture, err = inactive.Activate(); err != nil {
err = fmt.Errorf("PCAP Activate error: %v", err)
return
}
}
handle = &pcapHandle{
capture: capture,
}
return
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/vishvananda/netns"
)
func newNetnsPacketSource(procfs string, pid string, interfaceName string,
func newNetnsPacketSource(procfs string, pid string, interfaceName string, packetCapture string,
behaviour TcpPacketSourceBehaviour, origin api.Capture) (*tcpPacketSource, error) {
nsh, err := netns.GetFromPath(fmt.Sprintf("%s/%s/ns/net", procfs, pid))
@@ -18,7 +18,7 @@ func newNetnsPacketSource(procfs string, pid string, interfaceName string,
return nil, err
}
src, err := newPacketSourceFromNetnsHandle(pid, nsh, interfaceName, behaviour, origin)
src, err := newPacketSourceFromNetnsHandle(pid, nsh, interfaceName, packetCapture, behaviour, origin)
if err != nil {
logger.Log.Errorf("Error starting netns packet source for %s - %w", pid, err)
@@ -28,7 +28,7 @@ func newNetnsPacketSource(procfs string, pid string, interfaceName string,
return src, nil
}
func newPacketSourceFromNetnsHandle(pid string, nsh netns.NsHandle, interfaceName string,
func newPacketSourceFromNetnsHandle(pid string, nsh netns.NsHandle, interfaceName string, packetCapture string,
behaviour TcpPacketSourceBehaviour, origin api.Capture) (*tcpPacketSource, error) {
done := make(chan *tcpPacketSource)
@@ -58,7 +58,7 @@ func newPacketSourceFromNetnsHandle(pid string, nsh netns.NsHandle, interfaceNam
}
name := fmt.Sprintf("netns-%s-%s", pid, interfaceName)
src, err := newTcpPacketSource(name, "", interfaceName, behaviour, origin)
src, err := newTcpPacketSource(name, "", interfaceName, packetCapture, behaviour, origin)
if err != nil {
logger.Log.Errorf("Error listening to PID %s - %w", pid, err)

View File

@@ -16,6 +16,7 @@ type PacketSourceManagerConfig struct {
mtls bool
procfs string
interfaceName string
packetCapture string
behaviour TcpPacketSourceBehaviour
}
@@ -25,8 +26,9 @@ type PacketSourceManager struct {
}
func NewPacketSourceManager(procfs string, filename string, interfaceName string,
mtls bool, pods []v1.Pod, behaviour TcpPacketSourceBehaviour, ipdefrag bool, packets chan<- TcpPacketInfo) (*PacketSourceManager, error) {
hostSource, err := newHostPacketSource(filename, interfaceName, behaviour)
mtls bool, pods []v1.Pod, behaviour TcpPacketSourceBehaviour, ipdefrag bool,
packetCapture string, packets chan<- TcpPacketInfo) (*PacketSourceManager, error) {
hostSource, err := newHostPacketSource(filename, interfaceName, packetCapture, behaviour)
if err != nil {
return nil, err
}
@@ -41,6 +43,7 @@ func NewPacketSourceManager(procfs string, filename string, interfaceName string
mtls: mtls,
procfs: procfs,
interfaceName: interfaceName,
packetCapture: packetCapture,
behaviour: behaviour,
}
@@ -48,7 +51,7 @@ func NewPacketSourceManager(procfs string, filename string, interfaceName string
return sourceManager, nil
}
func newHostPacketSource(filename string, interfaceName string,
func newHostPacketSource(filename string, interfaceName string, packetCapture string,
behaviour TcpPacketSourceBehaviour) (*tcpPacketSource, error) {
var name string
if filename == "" {
@@ -57,7 +60,7 @@ func newHostPacketSource(filename string, interfaceName string,
name = fmt.Sprintf("file-%s", filename)
}
source, err := newTcpPacketSource(name, filename, interfaceName, behaviour, api.Pcap)
source, err := newTcpPacketSource(name, filename, interfaceName, packetCapture, behaviour, api.Pcap)
if err != nil {
return nil, err
}
@@ -67,14 +70,14 @@ func newHostPacketSource(filename string, interfaceName string,
func (m *PacketSourceManager) UpdatePods(pods []v1.Pod, ipdefrag bool, packets chan<- TcpPacketInfo) {
if m.config.mtls {
m.updateMtlsPods(m.config.procfs, pods, m.config.interfaceName, m.config.behaviour, ipdefrag, packets)
m.updateMtlsPods(m.config.procfs, pods, m.config.interfaceName, m.config.packetCapture, m.config.behaviour, ipdefrag, packets)
}
m.setBPFFilter(pods)
}
func (m *PacketSourceManager) updateMtlsPods(procfs string, pods []v1.Pod,
interfaceName string, behaviour TcpPacketSourceBehaviour, ipdefrag bool, packets chan<- TcpPacketInfo) {
interfaceName string, packetCapture string, behaviour TcpPacketSourceBehaviour, ipdefrag bool, packets chan<- TcpPacketInfo) {
relevantPids := m.getRelevantPids(procfs, pods)
logger.Log.Infof("Updating mtls pods (new: %v) (current: %v)", relevantPids, m.sources)
@@ -88,7 +91,7 @@ func (m *PacketSourceManager) updateMtlsPods(procfs string, pods []v1.Pod,
for pid, origin := range relevantPids {
if _, ok := m.sources[pid]; !ok {
source, err := newNetnsPacketSource(procfs, pid, interfaceName, behaviour, origin)
source, err := newNetnsPacketSource(procfs, pid, interfaceName, packetCapture, behaviour, origin)
if err == nil {
go source.readPackets(ipdefrag, packets)
@@ -165,12 +168,12 @@ func (m *PacketSourceManager) Stats() string {
result := ""
for _, source := range m.sources {
stats, err := source.Stats()
packetsReceived, packetsDropped, err := source.Stats()
if err != nil {
result = result + fmt.Sprintf("[%s: err:%s]", source.String(), err)
} else {
result = result + fmt.Sprintf("[%s: rec: %d dropped: %d]", source.String(), stats.PacketsReceived, stats.PacketsDropped)
result = result + fmt.Sprintf("[%s: rec: %d dropped: %d]", source.String(), packetsReceived, packetsDropped)
}
}

View File

@@ -3,21 +3,27 @@ package source
import (
"fmt"
"io"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/ip4defrag"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/up9inc/mizu/logger"
"github.com/up9inc/mizu/tap/api"
"github.com/up9inc/mizu/tap/dbgctl"
"github.com/up9inc/mizu/tap/diagnose"
)
type Handle interface {
NextPacket() (packet gopacket.Packet, err error)
SetDecoder(decoder gopacket.Decoder, lazy bool, noCopy bool)
SetBPF(expr string) (err error)
LinkType() layers.LinkType
Stats() (packetsReceived uint, packetsDropped uint, err error)
Close() (err error)
}
type tcpPacketSource struct {
source *gopacket.PacketSource
handle *pcap.Handle
Handle Handle
defragger *ip4defrag.IPv4Defragmenter
Behaviour *TcpPacketSourceBehaviour
name string
@@ -25,12 +31,13 @@ type tcpPacketSource struct {
}
type TcpPacketSourceBehaviour struct {
SnapLength int
Promisc bool
Tstype string
DecoderName string
Lazy bool
BpfFilter string
SnapLength int
TargetSizeMb int
Promisc bool
Tstype string
DecoderName string
Lazy bool
BpfFilter string
}
type TcpPacketInfo struct {
@@ -38,7 +45,7 @@ type TcpPacketInfo struct {
Source *tcpPacketSource
}
func newTcpPacketSource(name, filename string, interfaceName string,
func newTcpPacketSource(name, filename string, interfaceName string, packetCapture string,
behaviour TcpPacketSourceBehaviour, origin api.Capture) (*tcpPacketSource, error) {
var err error
@@ -49,56 +56,48 @@ func newTcpPacketSource(name, filename string, interfaceName string,
Origin: origin,
}
if filename != "" {
if result.handle, err = pcap.OpenOffline(filename); err != nil {
return result, fmt.Errorf("PCAP OpenOffline error: %v", err)
}
} else {
// This is a little complicated because we want to allow all possible options
// for creating the packet capture handle... instead of all this you can
// just call pcap.OpenLive if you want a simple handle.
inactive, err := pcap.NewInactiveHandle(interfaceName)
switch packetCapture {
case "af_packet":
result.Handle, err = newAfpacketHandle(
interfaceName,
behaviour.TargetSizeMb,
behaviour.SnapLength,
)
if err != nil {
return result, fmt.Errorf("could not create: %v", err)
return nil, err
}
defer inactive.CleanUp()
if err = inactive.SetSnapLen(behaviour.SnapLength); err != nil {
return result, fmt.Errorf("could not set snap length: %v", err)
} else if err = inactive.SetPromisc(behaviour.Promisc); err != nil {
return result, fmt.Errorf("could not set promisc mode: %v", err)
} else if err = inactive.SetTimeout(time.Second); err != nil {
return result, fmt.Errorf("could not set timeout: %v", err)
}
if behaviour.Tstype != "" {
if t, err := pcap.TimestampSourceFromString(behaviour.Tstype); err != nil {
return result, fmt.Errorf("supported timestamp types: %v", inactive.SupportedTimestamps())
} else if err := inactive.SetTimestampSource(t); err != nil {
return result, fmt.Errorf("supported timestamp types: %v", inactive.SupportedTimestamps())
}
}
if result.handle, err = inactive.Activate(); err != nil {
return result, fmt.Errorf("PCAP Activate error: %v", err)
logger.Log.Infof("Using AF_PACKET socket as the capture source")
default:
result.Handle, err = newPcapHandle(
filename,
interfaceName,
behaviour.SnapLength,
behaviour.Promisc,
behaviour.Tstype,
)
if err != nil {
return nil, err
}
logger.Log.Infof("Using libpcap as the capture source")
}
var decoder gopacket.Decoder
var ok bool
if behaviour.DecoderName == "" {
behaviour.DecoderName = result.Handle.LinkType().String()
}
if decoder, ok = gopacket.DecodersByLayerName[behaviour.DecoderName]; !ok {
return nil, fmt.Errorf("no decoder named %v", behaviour.DecoderName)
}
result.Handle.SetDecoder(decoder, behaviour.Lazy, true)
if behaviour.BpfFilter != "" {
logger.Log.Infof("Using BPF filter %q", behaviour.BpfFilter)
if err = result.handle.SetBPFFilter(behaviour.BpfFilter); err != nil {
if err = result.setBPFFilter(behaviour.BpfFilter); err != nil {
return nil, fmt.Errorf("BPF filter error: %v", err)
}
}
var dec gopacket.Decoder
var ok bool
if behaviour.DecoderName == "" {
behaviour.DecoderName = result.handle.LinkType().String()
}
if dec, ok = gopacket.DecodersByLayerName[behaviour.DecoderName]; !ok {
return nil, fmt.Errorf("no decoder named %v", behaviour.DecoderName)
}
result.source = gopacket.NewPacketSource(result.handle, dec)
result.source.Lazy = behaviour.Lazy
result.source.NoCopy = true
return result, nil
}
@@ -107,17 +106,17 @@ func (source *tcpPacketSource) String() string {
}
func (source *tcpPacketSource) setBPFFilter(expr string) (err error) {
return source.handle.SetBPFFilter(expr)
return source.Handle.SetBPF(expr)
}
func (source *tcpPacketSource) close() {
if source.handle != nil {
source.handle.Close()
if source.Handle != nil {
source.Handle.Close()
}
}
func (source *tcpPacketSource) Stats() (stat *pcap.Stats, err error) {
return source.handle.Stats()
func (source *tcpPacketSource) Stats() (packetsReceived uint, packetsDropped uint, err error) {
return source.Handle.Stats()
}
func (source *tcpPacketSource) readPackets(ipdefrag bool, packets chan<- TcpPacketInfo) {
@@ -127,7 +126,7 @@ func (source *tcpPacketSource) readPackets(ipdefrag bool, packets chan<- TcpPack
logger.Log.Infof("Start reading packets from %v", source.name)
for {
packet, err := source.source.NextPacket()
packet, err := source.Handle.NextPacket()
if err == io.EOF {
logger.Log.Infof("Got EOF while reading packets from %v", source.name)

View File

@@ -16,34 +16,16 @@ static __always_inline int add_address_to_chunk(struct pt_regs *ctx, struct tls_
__u32 pid = id >> 32;
__u64 key = (__u64) pid << 32 | fd;
struct fd_info *fdinfo = bpf_map_lookup_elem(&file_descriptor_to_ipv4, &key);
conn_flags *flags = bpf_map_lookup_elem(&connection_context, &key);
if (fdinfo == NULL) {
// Happens when we don't catch the connect / accept (if the connection is created before tapping is started)
if (flags == NULL) {
return 0;
}
int err;
chunk->flags |= (*flags & FLAGS_IS_CLIENT_BIT);
switch (info->address_info.mode) {
case ADDRESS_INFO_MODE_UNDEFINED:
chunk->address_info.mode = ADDRESS_INFO_MODE_SINGLE;
err = bpf_probe_read(&chunk->address_info.sport, sizeof(chunk->address_info.sport), &fdinfo->ipv4_addr[2]);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_FD_ADDRESS, id, err, 0l);
return 0;
}
err = bpf_probe_read(&chunk->address_info.saddr, sizeof(chunk->address_info.saddr), &fdinfo->ipv4_addr[4]);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_FD_ADDRESS, id, err, 0l);
return 0;
}
break;
default:
bpf_probe_read(&chunk->address_info, sizeof(chunk->address_info), &info->address_info);
}
chunk->flags |= (fdinfo->flags & FLAGS_IS_CLIENT_BIT);
bpf_probe_read(&chunk->address_info, sizeof(chunk->address_info), &info->address_info);
return 1;
}

View File

@@ -14,7 +14,6 @@ Copyright (C) UP9 Inc.
#define IPV4_ADDR_LEN (16)
struct accept_info {
__u64* sockaddr;
__u32* addrlen;
};
@@ -39,7 +38,6 @@ void sys_enter_accept4(struct sys_enter_accept4_ctx *ctx) {
struct accept_info info = {};
info.sockaddr = ctx->sockaddr;
info.addrlen = ctx->addrlen;
long err = bpf_map_update_elem(&accept_syscall_context, &id, &info, BPF_ANY);
@@ -94,26 +92,21 @@ void sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
return;
}
struct fd_info fdinfo = {
.flags = 0
};
bpf_probe_read(fdinfo.ipv4_addr, sizeof(fdinfo.ipv4_addr), info.sockaddr);
conn_flags flags = 0;
__u32 pid = id >> 32;
__u32 fd = (__u32) ctx->ret;
__u64 key = (__u64) pid << 32 | fd;
err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY);
err = bpf_map_update_elem(&connection_context, &key, &flags, BPF_ANY);
if (err != 0) {
log_error(ctx, LOG_ERROR_PUTTING_FD_MAPPING, id, err, ORIGIN_SYS_EXIT_ACCEPT4_CODE);
log_error(ctx, LOG_ERROR_PUTTING_CONNECTION_CONTEXT, id, err, ORIGIN_SYS_EXIT_ACCEPT4_CODE);
}
}
struct connect_info {
__u64 fd;
__u64* sockaddr;
__u32 addrlen;
};
@@ -138,7 +131,6 @@ void sys_enter_connect(struct sys_enter_connect_ctx *ctx) {
struct connect_info info = {};
info.sockaddr = ctx->sockaddr;
info.addrlen = ctx->addrlen;
info.fd = ctx->fd;
@@ -193,19 +185,15 @@ void sys_exit_connect(struct sys_exit_connect_ctx *ctx) {
return;
}
struct fd_info fdinfo = {
.flags = FLAGS_IS_CLIENT_BIT
};
bpf_probe_read(fdinfo.ipv4_addr, sizeof(fdinfo.ipv4_addr), info.sockaddr);
conn_flags flags = FLAGS_IS_CLIENT_BIT;
__u32 pid = id >> 32;
__u32 fd = (__u32) info.fd;
__u64 key = (__u64) pid << 32 | fd;
err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY);
err = bpf_map_update_elem(&connection_context, &key, &flags, BPF_ANY);
if (err != 0) {
log_error(ctx, LOG_ERROR_PUTTING_FD_MAPPING, id, err, ORIGIN_SYS_EXIT_CONNECT_CODE);
log_error(ctx, LOG_ERROR_PUTTING_CONNECTION_CONTEXT, id, err, ORIGIN_SYS_EXIT_CONNECT_CODE);
}
}

View File

@@ -10,8 +10,9 @@ Copyright (C) UP9 Inc.
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h"
#include "include/common.h"
struct sys_enter_read_ctx {
struct sys_enter_read_write_ctx {
__u64 __unused_syscall_header;
__u32 __unused_syscall_nr;
@@ -20,8 +21,46 @@ struct sys_enter_read_ctx {
__u64 count;
};
struct sys_exit_read_write_ctx {
__u64 __unused_syscall_header;
__u32 __unused_syscall_nr;
__u64 ret;
};
static __always_inline void fd_tracepoints_handle_openssl(struct sys_enter_read_write_ctx *ctx, __u64 id, struct ssl_info *infoPtr, struct bpf_map_def *map_fd, __u64 origin_code) {
struct ssl_info info;
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, origin_code);
return;
}
info.fd = ctx->fd;
err = bpf_map_update_elem(map_fd, &id, &info, BPF_ANY);
if (err != 0) {
log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, origin_code);
return;
}
}
static __always_inline void fd_tracepoints_handle_go(struct sys_enter_read_write_ctx *ctx, __u64 id, struct bpf_map_def *map_fd, __u64 origin_code) {
__u32 fd = ctx->fd;
long err = bpf_map_update_elem(map_fd, &id, &fd, BPF_ANY);
if (err != 0) {
log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, origin_code);
return;
}
}
SEC("tracepoint/syscalls/sys_enter_read")
void sys_enter_read(struct sys_enter_read_ctx *ctx) {
void sys_enter_read(struct sys_enter_read_write_ctx *ctx) {
__u64 id = bpf_get_current_pid_tgid();
if (!should_tap(id >> 32)) {
@@ -30,38 +69,15 @@ void sys_enter_read(struct sys_enter_read_ctx *ctx) {
struct ssl_info *infoPtr = bpf_map_lookup_elem(&openssl_read_context, &id);
if (infoPtr == NULL) {
return;
}
struct ssl_info info;
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SYS_ENTER_READ_CODE);
return;
}
info.fd = ctx->fd;
err = bpf_map_update_elem(&openssl_read_context, &id, &info, BPF_ANY);
if (err != 0) {
log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, ORIGIN_SYS_ENTER_READ_CODE);
if (infoPtr != NULL) {
fd_tracepoints_handle_openssl(ctx, id, infoPtr, &openssl_read_context, ORIGIN_SYS_ENTER_READ_CODE);
}
fd_tracepoints_handle_go(ctx, id, &go_kernel_read_context, ORIGIN_SYS_ENTER_READ_CODE);
}
struct sys_enter_write_ctx {
__u64 __unused_syscall_header;
__u32 __unused_syscall_nr;
__u64 fd;
__u64* buf;
__u64 count;
};
SEC("tracepoint/syscalls/sys_enter_write")
void sys_enter_write(struct sys_enter_write_ctx *ctx) {
void sys_enter_write(struct sys_enter_read_write_ctx *ctx) {
__u64 id = bpf_get_current_pid_tgid();
if (!should_tap(id >> 32)) {
@@ -70,23 +86,25 @@ void sys_enter_write(struct sys_enter_write_ctx *ctx) {
struct ssl_info *infoPtr = bpf_map_lookup_elem(&openssl_write_context, &id);
if (infoPtr == NULL) {
return;
}
struct ssl_info info;
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SYS_ENTER_WRITE_CODE);
return;
}
info.fd = ctx->fd;
err = bpf_map_update_elem(&openssl_write_context, &id, &info, BPF_ANY);
if (err != 0) {
log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, ORIGIN_SYS_ENTER_WRITE_CODE);
if (infoPtr != NULL) {
fd_tracepoints_handle_openssl(ctx, id, infoPtr, &openssl_write_context, ORIGIN_SYS_ENTER_WRITE_CODE);
}
fd_tracepoints_handle_go(ctx, id, &go_kernel_write_context, ORIGIN_SYS_ENTER_WRITE_CODE);
}
SEC("tracepoint/syscalls/sys_exit_read")
void sys_exit_read(struct sys_exit_read_write_ctx *ctx) {
__u64 id = bpf_get_current_pid_tgid();
// Delete from go map. The value is not used after exiting this syscall.
// Keep value in openssl map.
bpf_map_delete_elem(&go_kernel_read_context, &id);
}
SEC("tracepoint/syscalls/sys_exit_write")
void sys_exit_write(struct sys_exit_read_write_ctx *ctx) {
__u64 id = bpf_get_current_pid_tgid();
// Delete from go map. The value is not used after exiting this syscall.
// Keep value in openssl map.
bpf_map_delete_elem(&go_kernel_write_context, &id);
}

View File

@@ -220,7 +220,7 @@ static __always_inline void go_crypto_tls_uprobe(struct pt_regs *ctx, struct bpf
return;
}
static __always_inline void go_crypto_tls_ex_uprobe(struct pt_regs *ctx, struct bpf_map_def* go_context, __u32 flags, enum ABI abi) {
static __always_inline void go_crypto_tls_ex_uprobe(struct pt_regs *ctx, struct bpf_map_def* go_context, struct bpf_map_def* go_user_kernel_context, __u32 flags, enum ABI abi) {
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u64 pid = pid_tgid >> 32;
if (!should_tap(pid)) {
@@ -285,6 +285,21 @@ static __always_inline void go_crypto_tls_ex_uprobe(struct pt_regs *ctx, struct
}
}
__u64 key = (__u64) pid << 32 | info_ptr->fd;
struct address_info *address_info = bpf_map_lookup_elem(go_user_kernel_context, &key);
// Ideally we would delete the entry from the map after reading it,
// but sometimes the uprobe is called twice in a row without the tcp kprobes in between to fill in
// the entry again. Keeping it in the map and rely on LRU logic.
if (address_info == NULL) {
log_error(ctx, LOG_ERROR_GETTING_GO_USER_KERNEL_CONTEXT, pid_tgid, info_ptr->fd, err);
return;
}
info.address_info.daddr = address_info->daddr;
info.address_info.dport = address_info->dport;
info.address_info.saddr = address_info->saddr;
info.address_info.sport = address_info->sport;
output_ssl_chunk(ctx, &info, info.buffer_len, pid_tgid, flags);
return;
@@ -298,7 +313,7 @@ int BPF_KPROBE(go_crypto_tls_abi0_write) {
SEC("uprobe/go_crypto_tls_abi0_write_ex")
int BPF_KPROBE(go_crypto_tls_abi0_write_ex) {
go_crypto_tls_ex_uprobe(ctx, &go_write_context, 0, ABI0);
go_crypto_tls_ex_uprobe(ctx, &go_write_context, &go_user_kernel_write_context, 0, ABI0);
return 1;
}
@@ -310,7 +325,7 @@ int BPF_KPROBE(go_crypto_tls_abi0_read) {
SEC("uprobe/go_crypto_tls_abi0_read_ex")
int BPF_KPROBE(go_crypto_tls_abi0_read_ex) {
go_crypto_tls_ex_uprobe(ctx, &go_read_context, FLAGS_IS_READ_BIT, ABI0);
go_crypto_tls_ex_uprobe(ctx, &go_read_context, &go_user_kernel_read_context, FLAGS_IS_READ_BIT, ABI0);
return 1;
}
@@ -322,7 +337,7 @@ int BPF_KPROBE(go_crypto_tls_abi_internal_write) {
SEC("uprobe/go_crypto_tls_abi_internal_write_ex")
int BPF_KPROBE(go_crypto_tls_abi_internal_write_ex) {
go_crypto_tls_ex_uprobe(ctx, &go_write_context, 0, ABIInternal);
go_crypto_tls_ex_uprobe(ctx, &go_write_context, &go_user_kernel_write_context, 0, ABIInternal);
return 1;
}
@@ -334,6 +349,6 @@ int BPF_KPROBE(go_crypto_tls_abi_internal_read) {
SEC("uprobe/go_crypto_tls_abi_internal_read_ex")
int BPF_KPROBE(go_crypto_tls_abi_internal_read_ex) {
go_crypto_tls_ex_uprobe(ctx, &go_read_context, FLAGS_IS_READ_BIT, ABIInternal);
go_crypto_tls_ex_uprobe(ctx, &go_read_context, &go_user_kernel_read_context, FLAGS_IS_READ_BIT, ABIInternal);
return 1;
}

View File

@@ -10,27 +10,28 @@ Copyright (C) UP9 Inc.
// 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)
#define LOG_ERROR_READING_SOCKET_FAMILY (17)
#define LOG_ERROR_READING_SOCKET_DADDR (18)
#define LOG_ERROR_READING_SOCKET_SADDR (19)
#define LOG_ERROR_READING_SOCKET_DPORT (20)
#define LOG_ERROR_READING_SOCKET_SPORT (21)
#define LOG_ERROR_READING_FROM_SSL_BUFFER (1)
#define LOG_ERROR_BUFFER_TOO_BIG (2)
#define LOG_ERROR_ALLOCATING_CHUNK (3)
#define LOG_ERROR_READING_SSL_CONTEXT (4)
#define LOG_ERROR_PUTTING_SSL_CONTEXT (5)
#define LOG_ERROR_GETTING_SSL_CONTEXT (6)
#define LOG_ERROR_MISSING_FILE_DESCRIPTOR (7)
#define LOG_ERROR_PUTTING_FILE_DESCRIPTOR (8)
#define LOG_ERROR_PUTTING_ACCEPT_INFO (9)
#define LOG_ERROR_GETTING_ACCEPT_INFO (10)
#define LOG_ERROR_READING_ACCEPT_INFO (11)
#define LOG_ERROR_PUTTING_CONNECTION_CONTEXT (12)
#define LOG_ERROR_PUTTING_CONNECT_INFO (13)
#define LOG_ERROR_GETTING_CONNECT_INFO (14)
#define LOG_ERROR_READING_CONNECT_INFO (15)
#define LOG_ERROR_READING_SOCKET_FAMILY (16)
#define LOG_ERROR_READING_SOCKET_DADDR (17)
#define LOG_ERROR_READING_SOCKET_SADDR (18)
#define LOG_ERROR_READING_SOCKET_DPORT (19)
#define LOG_ERROR_READING_SOCKET_SPORT (20)
#define LOG_ERROR_PUTTING_GO_USER_KERNEL_CONTEXT (21)
#define LOG_ERROR_GETTING_GO_USER_KERNEL_CONTEXT (22)
// 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

View File

@@ -25,14 +25,7 @@ Copyright (C) UP9 Inc.
// Be careful when editing, alignment and padding should be exactly the same in go/c.
//
typedef enum {
ADDRESS_INFO_MODE_UNDEFINED,
ADDRESS_INFO_MODE_SINGLE,
ADDRESS_INFO_MODE_PAIR,
} address_info_mode;
struct address_info {
address_info_mode mode;
__be32 saddr;
__be32 daddr;
__be16 sport;
@@ -64,10 +57,7 @@ struct ssl_info {
size_t *count_ptr;
};
struct fd_info {
__u8 ipv4_addr[16]; // struct sockaddr (linux-src/include/linux/socket.h)
__u8 flags;
};
typedef __u8 conn_flags;
struct goid_offsets {
__u64 g_addr_offset;
@@ -105,7 +95,7 @@ struct {
// Generic
BPF_HASH(pids_map, __u32, __u32);
BPF_LRU_HASH(file_descriptor_to_ipv4, __u64, struct fd_info);
BPF_LRU_HASH(connection_context, __u64, conn_flags);
BPF_PERF_OUTPUT(chunks_buffer);
BPF_PERF_OUTPUT(log_buffer);
@@ -117,5 +107,9 @@ BPF_LRU_HASH(openssl_read_context, __u64, struct ssl_info);
BPF_HASH(goid_offsets_map, __u32, struct goid_offsets);
BPF_LRU_HASH(go_write_context, __u64, struct ssl_info);
BPF_LRU_HASH(go_read_context, __u64, struct ssl_info);
BPF_LRU_HASH(go_kernel_write_context, __u64, __u32);
BPF_LRU_HASH(go_kernel_read_context, __u64, __u32);
BPF_LRU_HASH(go_user_kernel_write_context, __u64, struct address_info);
BPF_LRU_HASH(go_user_kernel_read_context, __u64, struct address_info);
#endif /* __MAPS__ */

View File

@@ -5,32 +5,19 @@
#include "include/pids.h"
#include "include/common.h"
static __always_inline void tcp_kprobe(struct pt_regs *ctx, struct bpf_map_def *map_fd, _Bool is_send) {
static __always_inline int tcp_kprobes_get_address_pair_from_ctx(struct pt_regs *ctx, __u64 id, struct address_info *address_info_ptr) {
long err;
__u64 id = bpf_get_current_pid_tgid();
__u32 pid = id >> 32;
if (!should_tap(id >> 32)) {
return;
}
struct ssl_info *info_ptr = bpf_map_lookup_elem(map_fd, &id);
// Happens when the connection is not tls
if (info_ptr == NULL) {
return;
}
struct sock *sk = (struct sock *) PT_REGS_PARM1(ctx);
short unsigned int family;
err = bpf_probe_read(&family, sizeof(family), (void *)&sk->__sk_common.skc_family);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_SOCKET_FAMILY, id, err, 0l);
return;
return -1;
}
if (family != AF_INET) {
return;
return -1;
}
// daddr, saddr and dport are in network byte order (big endian)
@@ -43,37 +30,89 @@ static __always_inline void tcp_kprobe(struct pt_regs *ctx, struct bpf_map_def *
err = bpf_probe_read(&saddr, sizeof(saddr), (void *)&sk->__sk_common.skc_rcv_saddr);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_SOCKET_SADDR, id, err, 0l);
return;
return -1;
}
err = bpf_probe_read(&daddr, sizeof(daddr), (void *)&sk->__sk_common.skc_daddr);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_SOCKET_DADDR, id, err, 0l);
return;
return -1;
}
err = bpf_probe_read(&dport, sizeof(dport), (void *)&sk->__sk_common.skc_dport);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_SOCKET_DPORT, id, err, 0l);
return;
return -1;
}
err = bpf_probe_read(&sport, sizeof(sport), (void *)&sk->__sk_common.skc_num);
if (err != 0) {
log_error(ctx, LOG_ERROR_READING_SOCKET_SPORT, id, err, 0l);
return -1;
}
address_info_ptr->daddr = daddr;
address_info_ptr->saddr = saddr;
address_info_ptr->dport = dport;
address_info_ptr->sport = bpf_htons(sport);
return 0;
}
static __always_inline void tcp_kprobes_forward_go(struct pt_regs *ctx, __u64 id, __u32 fd, struct address_info address_info, struct bpf_map_def *map_fd_go_user_kernel) {
__u32 pid = id >> 32;
__u64 key = (__u64) pid << 32 | fd;
long err = bpf_map_update_elem(map_fd_go_user_kernel, &key, &address_info, BPF_ANY);
if (err != 0) {
log_error(ctx, LOG_ERROR_PUTTING_GO_USER_KERNEL_CONTEXT, id, fd, err);
return;
}
}
static void __always_inline tcp_kprobes_forward_openssl(struct ssl_info *info_ptr, struct address_info address_info) {
info_ptr->address_info.daddr = address_info.daddr;
info_ptr->address_info.saddr = address_info.saddr;
info_ptr->address_info.dport = address_info.dport;
info_ptr->address_info.sport = address_info.sport;
}
static __always_inline void tcp_kprobe(struct pt_regs *ctx, struct bpf_map_def *map_fd_openssl, struct bpf_map_def *map_fd_go_kernel, struct bpf_map_def *map_fd_go_user_kernel) {
long err;
__u64 id = bpf_get_current_pid_tgid();
if (!should_tap(id >> 32)) {
return;
}
info_ptr->address_info.mode = ADDRESS_INFO_MODE_PAIR;
info_ptr->address_info.daddr = daddr;
info_ptr->address_info.saddr = saddr;
info_ptr->address_info.dport = dport;
info_ptr->address_info.sport = bpf_htons(sport);
struct address_info address_info;
if (0 != tcp_kprobes_get_address_pair_from_ctx(ctx, id, &address_info)) {
return;
}
struct ssl_info *info_ptr = bpf_map_lookup_elem(map_fd_openssl, &id);
__u32 *fd_ptr;
if (info_ptr == NULL) {
fd_ptr = bpf_map_lookup_elem(map_fd_go_kernel, &id);
// Connection is used by a Go program
if (fd_ptr == NULL) {
// Connection was not created by a Go program or by openssl lib
return;
}
tcp_kprobes_forward_go(ctx, id, *fd_ptr, address_info, map_fd_go_user_kernel);
} else {
// Connection is used by openssl lib
tcp_kprobes_forward_openssl(info_ptr, address_info);
}
}
SEC("kprobe/tcp_sendmsg")
void BPF_KPROBE(tcp_sendmsg) {
tcp_kprobe(ctx, &openssl_write_context, true);
__u64 id = bpf_get_current_pid_tgid();
tcp_kprobe(ctx, &openssl_write_context, &go_kernel_write_context, &go_user_kernel_write_context);
}
SEC("kprobe/tcp_recvmsg")
void BPF_KPROBE(tcp_recvmsg) {
tcp_kprobe(ctx, &openssl_read_context, false);
__u64 id = bpf_get_current_pid_tgid();
tcp_kprobe(ctx, &openssl_read_context, &go_kernel_read_context, &go_user_kernel_read_context);
}

View File

@@ -4,25 +4,26 @@ package tlstapper
//
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]",
/*0017*/ "[%d] Unable to read socket family [err: %d]",
/*0018*/ "[%d] Unable to read socket daddr [err: %d]",
/*0019*/ "[%d] Unable to read socket saddr [err: %d]",
/*0001*/ "[%d] Unable to read ssl buffer [err: %d] [origin: %d]",
/*0002*/ "[%d] Buffer is too big [size: %d]",
/*0003*/ "[%d] Unable to allocate chunk in bpf heap",
/*0004*/ "[%d] Unable to read ssl context [err: %d] [origin: %d]",
/*0005*/ "[%d] Unable to put ssl context [err: %d]",
/*0006*/ "[%d] Unable to get ssl context",
/*0007*/ "[%d] File descriptor is missing for tls chunk",
/*0008*/ "[%d] Unable to put file descriptor [err: %d] [origin: %d]",
/*0009*/ "[%d] Unable to put accept info [err: %d]",
/*0010*/ "[%d] Unable to get accept info",
/*0011*/ "[%d] Unable to read accept info [err: %d]",
/*0012*/ "[%d] Unable to put connection info to connection context [err: %d] [origin: %d]",
/*0013*/ "[%d] Unable to put connect info [err: %d]",
/*0014*/ "[%d] Unable to get connect info",
/*0015*/ "[%d] Unable to read connect info [err: %d]",
/*0016*/ "[%d] Unable to read socket family [err: %d]",
/*0017*/ "[%d] Unable to read socket daddr [err: %d]",
/*0018*/ "[%d] Unable to read socket saddr [err: %d]",
/*0019*/ "[%d] Unable to read socket dport [err: %d]",
/*0021*/ "[%d] Unable to read socket sport [err: %d]",
/*0020*/ "[%d] Unable to read socket sport [err: %d]",
/*0021*/ "[%d] Unable to put go user-kernel context [fd: %d] [err: %d]",
/*0022*/ "[%d] Unable to get go user-kernel context [fd: %d]]",
}

View File

@@ -4,17 +4,17 @@ import (
"encoding/binary"
"net"
"unsafe"
"github.com/up9inc/mizu/tap/api"
)
const FlagsIsClientBit uint32 = 1 << 0
const FlagsIsReadBit uint32 = 1 << 1
const (
addressInfoModeUndefined = iota
addressInfoModeSingle
addressInfoModePair
)
type addressPair struct {
srcIp net.IP
srcPort uint16
dstIp net.IP
dstPort uint16
}
func (c *tlsTapperTlsChunk) getSrcAddress() (net.IP, uint16) {
ip := intToIP(c.AddressInfo.Saddr)
@@ -54,36 +54,18 @@ func (c *tlsTapperTlsChunk) isRequest() bool {
return (c.isClient() && c.isWrite()) || (c.isServer() && c.isRead())
}
func (c *tlsTapperTlsChunk) getAddressPair() (addressPair, bool) {
func (c *tlsTapperTlsChunk) getAddressPair() addressPair {
var (
srcIp, dstIp net.IP
srcPort, dstPort uint16
full bool
)
switch c.AddressInfo.Mode {
case addressInfoModeSingle:
if c.isRequest() {
srcIp, srcPort = api.UnknownIp, api.UnknownPort
dstIp, dstPort = c.getSrcAddress()
} else {
srcIp, srcPort = c.getSrcAddress()
dstIp, dstPort = api.UnknownIp, api.UnknownPort
}
full = false
case addressInfoModePair:
if c.isRequest() {
srcIp, srcPort = c.getSrcAddress()
dstIp, dstPort = c.getDstAddress()
} else {
srcIp, srcPort = c.getDstAddress()
dstIp, dstPort = c.getSrcAddress()
}
full = true
case addressInfoModeUndefined:
srcIp, srcPort = api.UnknownIp, api.UnknownPort
dstIp, dstPort = api.UnknownIp, api.UnknownPort
full = false
if c.isRequest() {
srcIp, srcPort = c.getSrcAddress()
dstIp, dstPort = c.getDstAddress()
} else {
srcIp, srcPort = c.getDstAddress()
dstIp, dstPort = c.getSrcAddress()
}
return addressPair{
@@ -91,7 +73,7 @@ func (c *tlsTapperTlsChunk) getAddressPair() (addressPair, bool) {
srcPort: srcPort,
dstIp: dstIp,
dstPort: dstPort,
}, full
}
}
// intToIP converts IPv4 number to net.IP

View File

@@ -1,122 +0,0 @@
package tlstapper
import (
"fmt"
"io/ioutil"
"net"
"os"
"regexp"
"strconv"
"strings"
"github.com/go-errors/errors"
)
var socketInodeRegex = regexp.MustCompile(`socket:\[(\d+)\]`)
const (
SRC_ADDRESS_FILED_INDEX = 1
DST_ADDRESS_FILED_INDEX = 2
INODE_FILED_INDEX = 9
)
type addressPair struct {
srcIp net.IP
srcPort uint16
dstIp net.IP
dstPort uint16
}
// This file helps to extract Ip and Port out of a Socket file descriptor.
//
// The equivalent bash commands are:
//
// > ls -l /proc/<pid>/fd/<fd>
// Output something like "socket:[1234]" for sockets - 1234 is the inode of the socket
// > cat /proc/<pid>/net/tcp | grep <inode>
// Output a line per ipv4 socket, the 9th field is the inode of the socket
// The 1st and 2nd fields are the source and dest ip and ports in a Hex format
// 0100007F:50 is 127.0.0.1:80
func getAddressBySockfd(procfs string, pid uint32, fd uint32) (addressPair, error) {
inode, err := getSocketInode(procfs, pid, fd)
if err != nil {
return addressPair{}, err
}
tcppath := fmt.Sprintf("%s/%d/net/tcp", procfs, pid)
tcp, err := ioutil.ReadFile(tcppath)
if err != nil {
return addressPair{}, errors.Wrap(err, 0)
}
for _, line := range strings.Split(string(tcp), "\n") {
parts := strings.Fields(line)
if len(parts) < 10 {
continue
}
if inode == parts[INODE_FILED_INDEX] {
srcIp, srcPort, srcErr := parseHexAddress(parts[SRC_ADDRESS_FILED_INDEX])
if srcErr != nil {
return addressPair{}, srcErr
}
dstIp, dstPort, dstErr := parseHexAddress(parts[DST_ADDRESS_FILED_INDEX])
if dstErr != nil {
return addressPair{}, dstErr
}
return addressPair{
srcIp: srcIp,
srcPort: srcPort,
dstIp: dstIp,
dstPort: dstPort,
}, nil
}
}
return addressPair{}, errors.Errorf("address not found [pid: %d] [sockfd: %d] [inode: %s]", pid, fd, inode)
}
func getSocketInode(procfs string, pid uint32, fd uint32) (string, error) {
fdlinkPath := fmt.Sprintf("%s/%d/fd/%d", procfs, pid, fd)
fdlink, err := os.Readlink(fdlinkPath)
if err != nil {
return "", errors.Wrap(err, 0)
}
tokens := socketInodeRegex.FindStringSubmatch(fdlink)
if tokens == nil || len(tokens) < 1 {
return "", errors.Errorf("socket inode not found [pid: %d] [sockfd: %d] [link: %s]", pid, fd, fdlink)
}
return tokens[1], nil
}
// Format looks like 0100007F:50 for 127.0.0.1:80
//
func parseHexAddress(addr string) (net.IP, uint16, error) {
addrParts := strings.Split(addr, ":")
port, err := strconv.ParseUint(addrParts[1], 16, 16)
if err != nil {
return nil, 0, errors.Wrap(err, 0)
}
ip, err := strconv.ParseUint(addrParts[0], 16, 32)
if err != nil {
return nil, 0, errors.Wrap(err, 0)
}
return net.IP{uint8(ip), uint8(ip >> 8), uint8(ip >> 16), uint8(ip >> 24)}, uint16(port), nil
}

View File

@@ -14,8 +14,6 @@ type sslHooks struct {
sslWriteExRetProbe link.Link
sslReadExProbe link.Link
sslReadExRetProbe link.Link
tcpSendmsg link.Link
tcpRecvmsg link.Link
}
func (s *sslHooks) installUprobes(bpfObjects *tlsTapperObjects, sslLibraryPath string) error {
@@ -105,16 +103,6 @@ func (s *sslHooks) installSslHooks(bpfObjects *tlsTapperObjects, sslLibrary *lin
}
}
s.tcpSendmsg, err = link.Kprobe("tcp_sendmsg", bpfObjects.TcpSendmsg, nil)
if err != nil {
return errors.Wrap(err, 0)
}
s.tcpRecvmsg, err = link.Kprobe("tcp_recvmsg", bpfObjects.TcpRecvmsg, nil)
if err != nil {
return errors.Wrap(err, 0)
}
return nil
}
@@ -161,17 +149,5 @@ func (s *sslHooks) close() []error {
}
}
if s.tcpSendmsg != nil {
if err := s.tcpSendmsg.Close(); err != nil {
returnValue = append(returnValue, err)
}
}
if s.tcpRecvmsg != nil {
if err := s.tcpRecvmsg.Close(); err != nil {
returnValue = append(returnValue, err)
}
}
return returnValue
}

View File

@@ -8,6 +8,8 @@ import (
type syscallHooks struct {
sysEnterRead link.Link
sysEnterWrite link.Link
sysExitRead link.Link
sysExitWrite link.Link
sysEnterAccept4 link.Link
sysExitAccept4 link.Link
sysEnterConnect link.Link
@@ -29,6 +31,18 @@ func (s *syscallHooks) installSyscallHooks(bpfObjects *tlsTapperObjects) error {
return errors.Wrap(err, 0)
}
s.sysExitRead, err = link.Tracepoint("syscalls", "sys_exit_read", bpfObjects.SysExitRead, nil)
if err != nil {
return errors.Wrap(err, 0)
}
s.sysExitWrite, err = link.Tracepoint("syscalls", "sys_exit_write", bpfObjects.SysExitWrite, nil)
if err != nil {
return errors.Wrap(err, 0)
}
s.sysEnterAccept4, err = link.Tracepoint("syscalls", "sys_enter_accept4", bpfObjects.SysEnterAccept4, nil)
if err != nil {
@@ -67,6 +81,14 @@ func (s *syscallHooks) close() []error {
returnValue = append(returnValue, err)
}
if err := s.sysExitRead.Close(); err != nil {
returnValue = append(returnValue, err)
}
if err := s.sysExitWrite.Close(); err != nil {
returnValue = append(returnValue, err)
}
if err := s.sysEnterAccept4.Close(); err != nil {
returnValue = append(returnValue, err)
}

View File

@@ -0,0 +1,45 @@
package tlstapper
import (
"github.com/cilium/ebpf/link"
"github.com/go-errors/errors"
)
type tcpKprobeHooks struct {
tcpSendmsg link.Link
tcpRecvmsg link.Link
}
func (s *tcpKprobeHooks) installTcpKprobeHooks(bpfObjects *tlsTapperObjects) error {
var err error
s.tcpSendmsg, err = link.Kprobe("tcp_sendmsg", bpfObjects.TcpSendmsg, nil)
if err != nil {
return errors.Wrap(err, 0)
}
s.tcpRecvmsg, err = link.Kprobe("tcp_recvmsg", bpfObjects.TcpRecvmsg, nil)
if err != nil {
return errors.Wrap(err, 0)
}
return nil
}
func (s *tcpKprobeHooks) close() []error {
returnValue := make([]error, 0)
if s.tcpSendmsg != nil {
if err := s.tcpSendmsg.Close(); err != nil {
returnValue = append(returnValue, err)
}
}
if s.tcpRecvmsg != nil {
if err := s.tcpRecvmsg.Close(); err != nil {
returnValue = append(returnValue, err)
}
}
return returnValue
}

View File

@@ -134,10 +134,7 @@ func (p *tlsPoller) pollChunksPerfBuffer(chunks chan<- *tlsTapperTlsChunk) {
func (p *tlsPoller) handleTlsChunk(chunk *tlsTapperTlsChunk, extension *api.Extension, emitter api.Emitter,
options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) error {
address, err := p.getAddressPair(chunk)
if err != nil {
return err
}
address := chunk.getAddressPair()
key := buildTlsKey(address)
reader, exists := p.readers[key]
@@ -156,22 +153,6 @@ func (p *tlsPoller) handleTlsChunk(chunk *tlsTapperTlsChunk, extension *api.Exte
return nil
}
func (p *tlsPoller) getAddressPair(chunk *tlsTapperTlsChunk) (addressPair, error) {
addrPairFromChunk, full := chunk.getAddressPair()
if full {
return addrPairFromChunk, nil
}
addrPairFromSockfd, err := p.getSockfdAddressPair(chunk)
if err == nil {
return addrPairFromSockfd, nil
} else {
logger.Log.Error("failed to get address from sock fd:", err)
}
return addrPairFromChunk, err
}
func (p *tlsPoller) startNewTlsReader(chunk *tlsTapperTlsChunk, address *addressPair, key string,
emitter api.Emitter, extension *api.Extension, options *api.TrafficFilteringOptions,
streamsMap api.TcpStreamMap) *tlsReader {
@@ -227,41 +208,6 @@ func (p *tlsPoller) closeReader(key string, r *tlsReader) {
p.closedReaders <- key
}
func (p *tlsPoller) getSockfdAddressPair(chunk *tlsTapperTlsChunk) (addressPair, error) {
address, err := getAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd)
fdCacheKey := fmt.Sprintf("%d:%d", chunk.Pid, chunk.Fd)
if err == nil {
if !chunk.isRequest() {
switchedAddress := addressPair{
srcIp: address.dstIp,
srcPort: address.dstPort,
dstIp: address.srcIp,
dstPort: address.srcPort,
}
p.fdCache.Add(fdCacheKey, switchedAddress)
return switchedAddress, nil
} else {
p.fdCache.Add(fdCacheKey, address)
return address, nil
}
}
fromCacheIfc, ok := p.fdCache.Get(fdCacheKey)
if !ok {
return addressPair{}, err
}
fromCache, ok := fromCacheIfc.(addressPair)
if !ok {
return address, errors.Errorf("Unable to cast %T to addressPair", fromCacheIfc)
}
return fromCache, nil
}
func buildTlsKey(address addressPair) string {
return fmt.Sprintf("%s:%d>%s:%d", address.srcIp, address.srcPort, address.dstIp, address.dstPort)
}

View File

@@ -22,6 +22,7 @@ const GlobalTapPid = 0
type TlsTapper struct {
bpfObjects tlsTapperObjects
syscallHooks syscallHooks
tcpKprobeHooks tcpKprobeHooks
sslHooksStructs []sslHooks
goHooksStructs []goHooks
poller *tlsPoller
@@ -63,6 +64,11 @@ func (t *TlsTapper) Init(chunksBufferSize int, logBufferSize int, procfs string,
return err
}
t.tcpKprobeHooks = tcpKprobeHooks{}
if err := t.tcpKprobeHooks.installTcpKprobeHooks(&t.bpfObjects); err != nil {
return err
}
t.sslHooksStructs = make([]sslHooks, 0)
t.bpfLogger = newBpfLogger()
@@ -152,6 +158,8 @@ func (t *TlsTapper) Close() []error {
returnValue = append(returnValue, t.syscallHooks.close()...)
returnValue = append(returnValue, t.tcpKprobeHooks.close()...)
for _, sslHooks := range t.sslHooksStructs {
returnValue = append(returnValue, sslHooks.close()...)
}

View File

@@ -27,7 +27,6 @@ type tlsTapper46TlsChunk struct {
Fd uint32
Flags uint32
AddressInfo struct {
Mode int32
Saddr uint32
Daddr uint32
Sport uint16
@@ -99,6 +98,8 @@ type tlsTapper46ProgramSpecs struct {
SysEnterWrite *ebpf.ProgramSpec `ebpf:"sys_enter_write"`
SysExitAccept4 *ebpf.ProgramSpec `ebpf:"sys_exit_accept4"`
SysExitConnect *ebpf.ProgramSpec `ebpf:"sys_exit_connect"`
SysExitRead *ebpf.ProgramSpec `ebpf:"sys_exit_read"`
SysExitWrite *ebpf.ProgramSpec `ebpf:"sys_exit_write"`
TcpRecvmsg *ebpf.ProgramSpec `ebpf:"tcp_recvmsg"`
TcpSendmsg *ebpf.ProgramSpec `ebpf:"tcp_sendmsg"`
}
@@ -107,18 +108,22 @@ type tlsTapper46ProgramSpecs struct {
//
// It can be passed ebpf.CollectionSpec.Assign.
type tlsTapper46MapSpecs struct {
AcceptSyscallContext *ebpf.MapSpec `ebpf:"accept_syscall_context"`
ChunksBuffer *ebpf.MapSpec `ebpf:"chunks_buffer"`
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
GoReadContext *ebpf.MapSpec `ebpf:"go_read_context"`
GoWriteContext *ebpf.MapSpec `ebpf:"go_write_context"`
GoidOffsetsMap *ebpf.MapSpec `ebpf:"goid_offsets_map"`
Heap *ebpf.MapSpec `ebpf:"heap"`
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
OpensslReadContext *ebpf.MapSpec `ebpf:"openssl_read_context"`
OpensslWriteContext *ebpf.MapSpec `ebpf:"openssl_write_context"`
PidsMap *ebpf.MapSpec `ebpf:"pids_map"`
AcceptSyscallContext *ebpf.MapSpec `ebpf:"accept_syscall_context"`
ChunksBuffer *ebpf.MapSpec `ebpf:"chunks_buffer"`
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
ConnectionContext *ebpf.MapSpec `ebpf:"connection_context"`
GoKernelReadContext *ebpf.MapSpec `ebpf:"go_kernel_read_context"`
GoKernelWriteContext *ebpf.MapSpec `ebpf:"go_kernel_write_context"`
GoReadContext *ebpf.MapSpec `ebpf:"go_read_context"`
GoUserKernelReadContext *ebpf.MapSpec `ebpf:"go_user_kernel_read_context"`
GoUserKernelWriteContext *ebpf.MapSpec `ebpf:"go_user_kernel_write_context"`
GoWriteContext *ebpf.MapSpec `ebpf:"go_write_context"`
GoidOffsetsMap *ebpf.MapSpec `ebpf:"goid_offsets_map"`
Heap *ebpf.MapSpec `ebpf:"heap"`
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
OpensslReadContext *ebpf.MapSpec `ebpf:"openssl_read_context"`
OpensslWriteContext *ebpf.MapSpec `ebpf:"openssl_write_context"`
PidsMap *ebpf.MapSpec `ebpf:"pids_map"`
}
// tlsTapper46Objects contains all objects after they have been loaded into the kernel.
@@ -140,18 +145,22 @@ func (o *tlsTapper46Objects) Close() error {
//
// It can be passed to loadTlsTapper46Objects or ebpf.CollectionSpec.LoadAndAssign.
type tlsTapper46Maps struct {
AcceptSyscallContext *ebpf.Map `ebpf:"accept_syscall_context"`
ChunksBuffer *ebpf.Map `ebpf:"chunks_buffer"`
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
GoReadContext *ebpf.Map `ebpf:"go_read_context"`
GoWriteContext *ebpf.Map `ebpf:"go_write_context"`
GoidOffsetsMap *ebpf.Map `ebpf:"goid_offsets_map"`
Heap *ebpf.Map `ebpf:"heap"`
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
OpensslReadContext *ebpf.Map `ebpf:"openssl_read_context"`
OpensslWriteContext *ebpf.Map `ebpf:"openssl_write_context"`
PidsMap *ebpf.Map `ebpf:"pids_map"`
AcceptSyscallContext *ebpf.Map `ebpf:"accept_syscall_context"`
ChunksBuffer *ebpf.Map `ebpf:"chunks_buffer"`
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
ConnectionContext *ebpf.Map `ebpf:"connection_context"`
GoKernelReadContext *ebpf.Map `ebpf:"go_kernel_read_context"`
GoKernelWriteContext *ebpf.Map `ebpf:"go_kernel_write_context"`
GoReadContext *ebpf.Map `ebpf:"go_read_context"`
GoUserKernelReadContext *ebpf.Map `ebpf:"go_user_kernel_read_context"`
GoUserKernelWriteContext *ebpf.Map `ebpf:"go_user_kernel_write_context"`
GoWriteContext *ebpf.Map `ebpf:"go_write_context"`
GoidOffsetsMap *ebpf.Map `ebpf:"goid_offsets_map"`
Heap *ebpf.Map `ebpf:"heap"`
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
OpensslReadContext *ebpf.Map `ebpf:"openssl_read_context"`
OpensslWriteContext *ebpf.Map `ebpf:"openssl_write_context"`
PidsMap *ebpf.Map `ebpf:"pids_map"`
}
func (m *tlsTapper46Maps) Close() error {
@@ -159,8 +168,12 @@ func (m *tlsTapper46Maps) Close() error {
m.AcceptSyscallContext,
m.ChunksBuffer,
m.ConnectSyscallInfo,
m.FileDescriptorToIpv4,
m.ConnectionContext,
m.GoKernelReadContext,
m.GoKernelWriteContext,
m.GoReadContext,
m.GoUserKernelReadContext,
m.GoUserKernelWriteContext,
m.GoWriteContext,
m.GoidOffsetsMap,
m.Heap,
@@ -197,6 +210,8 @@ type tlsTapper46Programs struct {
SysEnterWrite *ebpf.Program `ebpf:"sys_enter_write"`
SysExitAccept4 *ebpf.Program `ebpf:"sys_exit_accept4"`
SysExitConnect *ebpf.Program `ebpf:"sys_exit_connect"`
SysExitRead *ebpf.Program `ebpf:"sys_exit_read"`
SysExitWrite *ebpf.Program `ebpf:"sys_exit_write"`
TcpRecvmsg *ebpf.Program `ebpf:"tcp_recvmsg"`
TcpSendmsg *ebpf.Program `ebpf:"tcp_sendmsg"`
}
@@ -225,6 +240,8 @@ func (p *tlsTapper46Programs) Close() error {
p.SysEnterWrite,
p.SysExitAccept4,
p.SysExitConnect,
p.SysExitRead,
p.SysExitWrite,
p.TcpRecvmsg,
p.TcpSendmsg,
)

View File

@@ -27,7 +27,6 @@ type tlsTapperTlsChunk struct {
Fd uint32
Flags uint32
AddressInfo struct {
Mode int32
Saddr uint32
Daddr uint32
Sport uint16
@@ -99,6 +98,8 @@ type tlsTapperProgramSpecs struct {
SysEnterWrite *ebpf.ProgramSpec `ebpf:"sys_enter_write"`
SysExitAccept4 *ebpf.ProgramSpec `ebpf:"sys_exit_accept4"`
SysExitConnect *ebpf.ProgramSpec `ebpf:"sys_exit_connect"`
SysExitRead *ebpf.ProgramSpec `ebpf:"sys_exit_read"`
SysExitWrite *ebpf.ProgramSpec `ebpf:"sys_exit_write"`
TcpRecvmsg *ebpf.ProgramSpec `ebpf:"tcp_recvmsg"`
TcpSendmsg *ebpf.ProgramSpec `ebpf:"tcp_sendmsg"`
}
@@ -107,18 +108,22 @@ type tlsTapperProgramSpecs struct {
//
// It can be passed ebpf.CollectionSpec.Assign.
type tlsTapperMapSpecs struct {
AcceptSyscallContext *ebpf.MapSpec `ebpf:"accept_syscall_context"`
ChunksBuffer *ebpf.MapSpec `ebpf:"chunks_buffer"`
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
GoReadContext *ebpf.MapSpec `ebpf:"go_read_context"`
GoWriteContext *ebpf.MapSpec `ebpf:"go_write_context"`
GoidOffsetsMap *ebpf.MapSpec `ebpf:"goid_offsets_map"`
Heap *ebpf.MapSpec `ebpf:"heap"`
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
OpensslReadContext *ebpf.MapSpec `ebpf:"openssl_read_context"`
OpensslWriteContext *ebpf.MapSpec `ebpf:"openssl_write_context"`
PidsMap *ebpf.MapSpec `ebpf:"pids_map"`
AcceptSyscallContext *ebpf.MapSpec `ebpf:"accept_syscall_context"`
ChunksBuffer *ebpf.MapSpec `ebpf:"chunks_buffer"`
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
ConnectionContext *ebpf.MapSpec `ebpf:"connection_context"`
GoKernelReadContext *ebpf.MapSpec `ebpf:"go_kernel_read_context"`
GoKernelWriteContext *ebpf.MapSpec `ebpf:"go_kernel_write_context"`
GoReadContext *ebpf.MapSpec `ebpf:"go_read_context"`
GoUserKernelReadContext *ebpf.MapSpec `ebpf:"go_user_kernel_read_context"`
GoUserKernelWriteContext *ebpf.MapSpec `ebpf:"go_user_kernel_write_context"`
GoWriteContext *ebpf.MapSpec `ebpf:"go_write_context"`
GoidOffsetsMap *ebpf.MapSpec `ebpf:"goid_offsets_map"`
Heap *ebpf.MapSpec `ebpf:"heap"`
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
OpensslReadContext *ebpf.MapSpec `ebpf:"openssl_read_context"`
OpensslWriteContext *ebpf.MapSpec `ebpf:"openssl_write_context"`
PidsMap *ebpf.MapSpec `ebpf:"pids_map"`
}
// tlsTapperObjects contains all objects after they have been loaded into the kernel.
@@ -140,18 +145,22 @@ func (o *tlsTapperObjects) Close() error {
//
// It can be passed to loadTlsTapperObjects or ebpf.CollectionSpec.LoadAndAssign.
type tlsTapperMaps struct {
AcceptSyscallContext *ebpf.Map `ebpf:"accept_syscall_context"`
ChunksBuffer *ebpf.Map `ebpf:"chunks_buffer"`
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
GoReadContext *ebpf.Map `ebpf:"go_read_context"`
GoWriteContext *ebpf.Map `ebpf:"go_write_context"`
GoidOffsetsMap *ebpf.Map `ebpf:"goid_offsets_map"`
Heap *ebpf.Map `ebpf:"heap"`
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
OpensslReadContext *ebpf.Map `ebpf:"openssl_read_context"`
OpensslWriteContext *ebpf.Map `ebpf:"openssl_write_context"`
PidsMap *ebpf.Map `ebpf:"pids_map"`
AcceptSyscallContext *ebpf.Map `ebpf:"accept_syscall_context"`
ChunksBuffer *ebpf.Map `ebpf:"chunks_buffer"`
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
ConnectionContext *ebpf.Map `ebpf:"connection_context"`
GoKernelReadContext *ebpf.Map `ebpf:"go_kernel_read_context"`
GoKernelWriteContext *ebpf.Map `ebpf:"go_kernel_write_context"`
GoReadContext *ebpf.Map `ebpf:"go_read_context"`
GoUserKernelReadContext *ebpf.Map `ebpf:"go_user_kernel_read_context"`
GoUserKernelWriteContext *ebpf.Map `ebpf:"go_user_kernel_write_context"`
GoWriteContext *ebpf.Map `ebpf:"go_write_context"`
GoidOffsetsMap *ebpf.Map `ebpf:"goid_offsets_map"`
Heap *ebpf.Map `ebpf:"heap"`
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
OpensslReadContext *ebpf.Map `ebpf:"openssl_read_context"`
OpensslWriteContext *ebpf.Map `ebpf:"openssl_write_context"`
PidsMap *ebpf.Map `ebpf:"pids_map"`
}
func (m *tlsTapperMaps) Close() error {
@@ -159,8 +168,12 @@ func (m *tlsTapperMaps) Close() error {
m.AcceptSyscallContext,
m.ChunksBuffer,
m.ConnectSyscallInfo,
m.FileDescriptorToIpv4,
m.ConnectionContext,
m.GoKernelReadContext,
m.GoKernelWriteContext,
m.GoReadContext,
m.GoUserKernelReadContext,
m.GoUserKernelWriteContext,
m.GoWriteContext,
m.GoidOffsetsMap,
m.Heap,
@@ -197,6 +210,8 @@ type tlsTapperPrograms struct {
SysEnterWrite *ebpf.Program `ebpf:"sys_enter_write"`
SysExitAccept4 *ebpf.Program `ebpf:"sys_exit_accept4"`
SysExitConnect *ebpf.Program `ebpf:"sys_exit_connect"`
SysExitRead *ebpf.Program `ebpf:"sys_exit_read"`
SysExitWrite *ebpf.Program `ebpf:"sys_exit_write"`
TcpRecvmsg *ebpf.Program `ebpf:"tcp_recvmsg"`
TcpSendmsg *ebpf.Program `ebpf:"tcp_sendmsg"`
}
@@ -225,6 +240,8 @@ func (p *tlsTapperPrograms) Close() error {
p.SysEnterWrite,
p.SysExitAccept4,
p.SysExitConnect,
p.SysExitRead,
p.SysExitWrite,
p.TcpRecvmsg,
p.TcpSendmsg,
)

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,7 @@
},
"dependencies": {
"@craco/craco": "^6.4.3",
"@elastic/eui": "^60.2.0",
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.8.2",
@@ -85,7 +86,7 @@
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-sass": "^1.2.12",
"rollup-plugin-scss": "^3.0.0",
"typescript": "^4.7.2"
"typescript": "^4.5.3"
},
"eslintConfig": {
"extends": [

View File

@@ -32,7 +32,7 @@ export const AutoRepresentation: React.FC<any> = ({ representation, color, opene
badge: null
}]
if (response) {
if (response && response.length > 0) {
arr.push({
tab: 'Response',
badge: null
@@ -71,7 +71,7 @@ export const AutoRepresentation: React.FC<any> = ({ representation, color, opene
{getOpenedTabIndex() === TabsEnum.Request && <React.Fragment>
<SectionsRepresentation data={request} color={color} requestRepresentation={request} />
</React.Fragment>}
{response && getOpenedTabIndex() === TabsEnum.Response && <React.Fragment>
{response && response.length > 0 && getOpenedTabIndex() === TabsEnum.Response && <React.Fragment>
<SectionsRepresentation data={response} color={color} />
</React.Fragment>}
</div>}

View File

@@ -28,10 +28,6 @@ const SectionsRepresentation: React.FC<any> = ({ data, color }) => {
}
}
if (sections.length === 0) {
sections.push(<div>This request or response has no data.</div>);
}
return <React.Fragment>{sections}</React.Fragment>;
}

View File

@@ -97,4 +97,7 @@ $modalMargin-from-edge : 35px
overflow: hidden
.servicesFilterList
height: calc(100% - 30px - 52px)
height: calc(100% - 30px)
.protocolsFilterList
height: 100%

View File

@@ -228,7 +228,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
<div className={styles.filterWrapper}>
<div className={styles.card}>
<SelectList items={getProtocolsForFilter} checkBoxWidth="5%" tableName={"PROTOCOLS"} multiSelect={true}
checkedValues={checkedProtocols} setCheckedValues={onProtocolsChange} tableClassName={styles.filters}
checkedValues={checkedProtocols} setCheckedValues={onProtocolsChange} tableClassName={styles.filters + ` ${styles.protocolsFilterList}`}
inputSearchClass={styles.servicesFilterSearch} isFilterable={false} />
</div>
<div className={styles.servicesFilterWrapper + ` ${styles.card}`}>

View File

@@ -0,0 +1,96 @@
import React, { useState, Fragment } from 'react';
import { EuiProvider } from '@elastic/eui';
import {
EuiSuperDatePicker,
EuiSpacer,
} from '@elastic/eui';
import dateMath from '@elastic/datemath';
import '@elastic/eui/dist/eui_theme_light.css';
interface TimeRangePickerProps {
refreshStats: (startTime, endTime) => void;
}
export const TimeRangePicker: React.FC<TimeRangePickerProps> = ({ refreshStats }) => {
const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [start, setStart] = useState('now-30m');
const [end, setEnd] = useState('now');
const [isPaused, setIsPaused] = useState(true);
const [refreshInterval, setRefreshInterval] = useState();
const dateConvertor = (inputStart, inputEnd) => {
const startMoment = dateMath.parse(inputStart);
if (!startMoment || !startMoment.isValid()) {
console.error("Unable to parse start string");
}
const endMoment = dateMath.parse(inputEnd, { roundUp: true });
if (!endMoment || !endMoment.isValid()) {
console.error("Unable to parse end string");
}
return { startMoment: startMoment.format("x"), endMoment: endMoment.format("x") }
}
const onTimeChange = ({ start, end }) => {
const recentlyUsedRange = recentlyUsedRanges.filter(recentlyUsedRange => {
const isDuplicate =
recentlyUsedRange.start === start && recentlyUsedRange.end === end;
return !isDuplicate;
});
recentlyUsedRange.unshift({ start, end });
setStart(start);
setEnd(end);
setRecentlyUsedRanges(
recentlyUsedRange.length > 10
? recentlyUsedRange.slice(0, 9)
: recentlyUsedRange
);
const { startMoment, endMoment } = dateConvertor(start, end)
refreshStats(startMoment, endMoment)
setIsLoading(true);
startLoading();
};
const onRefresh = ({ start, end, refreshInterval }) => {
return new Promise(resolve => {
setTimeout(resolve, 100);
}).then(() => {
const { startMoment, endMoment } = dateConvertor(start, end)
refreshStats(startMoment, endMoment)
});
};
const startLoading = () => {
setTimeout(stopLoading, 1000);
};
const stopLoading = () => {
setIsLoading(false);
};
const onRefreshChange = ({ isPaused, refreshInterval }) => {
setIsPaused(isPaused);
setRefreshInterval(refreshInterval);
};
return (
<EuiProvider>
<Fragment>
<EuiSpacer />
<EuiSuperDatePicker
width='auto'
isLoading={isLoading}
start={start}
end={end}
onTimeChange={onTimeChange}
onRefresh={onRefresh}
isPaused={isPaused}
refreshInterval={refreshInterval}
onRefreshChange={onRefreshChange}
recentlyUsedRanges={recentlyUsedRanges}
/>
<EuiSpacer />
</Fragment>
</EuiProvider>
);
};

View File

@@ -1,3 +1,5 @@
@import "../../../../variables.module"
.barChartContainer
width: 100%
display: flex
@@ -6,3 +8,4 @@
.axisText
font-size: 12px
opacity: 0.9
font-family: $text-font-family

View File

@@ -0,0 +1,5 @@
@import "../../../../variables.module"
.entryName
margin-left: 5px
font-family: $text-font-family

View File

@@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from "react";
import { Cell, Legend, Pie, PieChart, Tooltip } from "recharts";
import { Utils } from "../../../../helpers/Utils";
import { ALL_PROTOCOLS ,StatsMode as PieChartMode } from "../consts"
import styles from "./TrafficPieChart.module.sass";
const RADIAN = Math.PI / 180;
const renderCustomizedLabel = ({
@@ -76,7 +77,7 @@ export const TrafficPieChart: React.FC<TrafficPieChartProps> = ({ pieChartMode,
if (selectedProtocol === ALL_PROTOCOLS) {
legend = data.map(protocol => <div style={{ marginBottom: 5, display: "flex" }}>
<div style={{ height: 15, width: 30, background: protocol?.color }} />
<span style={{ marginLeft: 5 }}>
<span className={styles.entryName}>
{protocol.name}
</span>
</div>)
@@ -84,7 +85,7 @@ export const TrafficPieChart: React.FC<TrafficPieChartProps> = ({ pieChartMode,
legend = data.find(protocol => protocol.name === selectedProtocol)?.methods.map((method) => <div
style={{ marginBottom: 5, display: "flex" }}>
<div style={{ height: 15, width: 30, background: method.color}} />
<span style={{ marginLeft: 5 }}>
<span className={styles.entryName}>
{method.name}
</span>
</div>)

View File

@@ -1,9 +1,11 @@
@import "../../../variables.module"
.headlineContainer
display: flex
.title
color: #494677
font-family: Source Sans Pro,Lucida Grande,Tahoma,sans-serif
font-family: $text-font-family
font-size: 28px
font-weight: 600
@@ -13,16 +15,21 @@
top: 20px
.mainContainer
padding: 30px
text-align: center
.selectContainer
display: flex
justify-content: space-evenly
align-items: center
margin-bottom: 4%
.selectTitle
font-family: $text-font-family
margin-right: 15px
.select
border: none
border-bottom: 1px black solid
outline: none
width: 100px
font-family: $text-font-family

View File

@@ -1,13 +1,12 @@
import React, { useCallback, useEffect, useState } from "react";
import { Backdrop, Box, Button, debounce, Fade, Modal } from "@mui/material";
import { Backdrop, Box, debounce, Fade, Modal } from "@mui/material";
import styles from "./TrafficStatsModal.module.sass";
import closeIcon from "assets/close.svg";
import { TrafficPieChart } from "./TrafficPieChart/TrafficPieChart";
import { TimelineBarChart } from "./TimelineBarChart/TimelineBarChart";
import refreshIcon from "assets/refresh.svg";
import { useCommonStyles } from "../../../helpers/commonStyle";
import { LoadingWrapper } from "../../UI/withLoading/withLoading";
import { ALL_PROTOCOLS, StatsMode } from "./consts";
import { TimeRangePicker } from "./TimelineBarChart/TimeRangePicker/TimeTangePicker";
const modalStyle = {
position: 'absolute',
@@ -15,7 +14,7 @@ const modalStyle = {
left: '50%',
transform: 'translate(-50%, 0%)',
width: '60vw',
height: '82vh',
height: '90vh',
bgcolor: 'background.paper',
borderRadius: '5px',
boxShadow: 24,
@@ -26,11 +25,10 @@ const modalStyle = {
interface TrafficStatsModalProps {
isOpen: boolean;
onClose: () => void;
getTrafficStatsDataApi: () => Promise<any>
getTrafficStatsDataApi: (start?, end?) => Promise<any>
}
export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, onClose, getTrafficStatsDataApi }) => {
const modes = Object.keys(StatsMode).filter(x => !(parseInt(x) >= 0));
const [statsMode, setStatsMode] = useState(modes[0]);
const [selectedProtocol, setSelectedProtocol] = useState(ALL_PROTOCOLS);
@@ -38,14 +36,13 @@ export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, on
const [timelineStatsData, setTimelineStatsData] = useState(null);
const [protocols, setProtocols] = useState([])
const [isLoading, setIsLoading] = useState(false);
const commonClasses = useCommonStyles();
const getTrafficStats = useCallback(async () => {
const getTrafficStats = useCallback(async (startTime, endTime) => {
if (isOpen && getTrafficStatsDataApi) {
(async () => {
try {
setIsLoading(true);
const statsData = await getTrafficStatsDataApi();
const statsData = await getTrafficStatsDataApi(startTime, endTime);
setPieStatsData(statsData.pie);
setTimelineStatsData(statsData.timeline);
setProtocols(statsData.protocols)
@@ -59,11 +56,13 @@ export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, on
}, [isOpen, getTrafficStatsDataApi, setPieStatsData, setTimelineStatsData])
useEffect(() => {
getTrafficStats();
const now = new Date().getTime();
const halfAnHourAgo = now - (30 * 60 * 1000);
getTrafficStats(halfAnHourAgo, now);
}, [getTrafficStats])
const refreshStats = debounce(() => {
getTrafficStats();
const refreshStats = debounce((newStartTime, newEndTime) => {
getTrafficStats(newStartTime, newEndTime);
}, 500);
return (
@@ -82,30 +81,24 @@ export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, on
</div>
<div className={styles.headlineContainer}>
<div className={styles.title}>Traffic Statistics</div>
<Button style={{ marginLeft: "2%", textTransform: 'unset' }}
startIcon={<img src={refreshIcon} className="custom" alt="refresh"></img>}
size="medium"
variant="contained"
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
onClick={refreshStats}
>
Refresh
</Button>
</div>
<div className={styles.mainContainer}>
<div className={styles.selectContainer}>
<div>
<span style={{ marginRight: 15 }}>Breakdown By</span>
<span className={styles.selectTitle}>Breakdown By</span>
<select className={styles.select} value={statsMode} onChange={(e) => setStatsMode(e.target.value)}>
{modes.map(mode => <option key={mode} value={mode}>{mode}</option>)}
</select>
</div>
<div>
<span style={{ marginRight: 15 }}>Protocol</span>
<span className={styles.selectTitle}>Protocol</span>
<select className={styles.select} value={selectedProtocol} onChange={(e) => setSelectedProtocol(e.target.value)}>
{protocols.map(protocol => <option key={protocol} value={protocol}>{protocol}</option>)}
</select>
</div>
<div>
<TimeRangePicker refreshStats={refreshStats} />
</div>
</div>
<div>
<LoadingWrapper isLoading={isLoading} loaderMargin={20} loaderHeight={50}>

View File

@@ -14,6 +14,8 @@ $content-section-color: #f8f9fc;
$blue-gray: #494677;
$light-gray: #8F9BB2;
$text-font-family: Source Sans Pro, Lucida Grande, Tahoma, sans-serif;
:export {
mainBackgroundColor: $main-background-color;
headerBackgroundColor: $header-background-color;
@@ -26,4 +28,5 @@ $light-gray: #8F9BB2;
blueGray: $blue-gray;
lightGray: $light-gray;
contentSectionColor: $content-section-color;
textFontFamily: $text-font-family
}

67736
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,8 @@
"private": true,
"dependencies": {
"@craco/craco": "^6.4.3",
"@elastic/datemath": "^5.0.3",
"@elastic/eui": "^60.2.0",
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.8.2",
@@ -23,7 +25,6 @@
"mobx": "^6.6.0",
"moment": "^2.29.3",
"node-fetch": "^3.2.4",
"sass": "^1.52.3",
"numeral": "^2.0.6",
"react": "^17.0.2",
"react-copy-to-clipboard": "^5.1.0",
@@ -35,8 +36,9 @@
"react-syntax-highlighter": "^15.5.0",
"react-toastify": "^8.2.0",
"redoc": "^2.0.0-rc.71",
"sass": "^1.52.3",
"styled-components": "^5.3.5",
"typescript": "^4.7.2",
"typescript": "^4.5.3",
"web-vitals": "^2.1.4",
"xml-formatter": "^2.6.1"
},

View File

@@ -8,7 +8,7 @@ import oasModalOpenAtom from './recoil/oasModalOpen/atom';
import trafficStatsModalOpenAtom from "./recoil/trafficStatsModalOpen";
import { OasModal } from '@up9/mizu-common';
import Api from './helpers/api';
import {ThemeProvider, StyledEngineProvider, createTheme} from '@mui/material';
import { ThemeProvider, StyledEngineProvider, createTheme } from '@mui/material';
import { TrafficStatsModal } from '@up9/mizu-common';
const api = Api.getInstance()
@@ -36,7 +36,7 @@ const App = () => {
openModal={oasModalOpen}
handleCloseModal={() => setOasModalOpen(false)}
/>}
<TrafficStatsModal isOpen={trafficStatsModalOpen} onClose={() => setTrafficStatsModalOpen(false)} getTrafficStatsDataApi={api.getTrafficStats}/>
<TrafficStatsModal isOpen={trafficStatsModalOpen} onClose={() => setTrafficStatsModalOpen(false)} getTrafficStatsDataApi={api.getTrafficStats} />
</div>
</ThemeProvider>
</StyledEngineProvider>

View File

@@ -116,8 +116,8 @@ export default class Api {
});
}
getTrafficStats = async () => {
const response = await client.get("/status/trafficStats");
getTrafficStats = async (startTimeMs, endTimeMs) => {
const response = await client.get("/status/trafficStats", {params: {startTimeMs, endTimeMs}});
return response.data;
}
}