From 13ab0dad86456b8bb87d2d662e37dc69f261651a Mon Sep 17 00:00:00 2001 From: Topspin Date: Sat, 6 Apr 2024 13:27:51 +0000 Subject: [PATCH 1/5] implemented clervers except for http2s --- clervers/http1/README.md | 9 + clervers/http1/client/client.sh | 1 + clervers/http1/go.mod | 33 +++ clervers/http1/go.sum | 78 ++++++ clervers/http1/server/main.go | 72 ++++++ clervers/http1s/README.md | 15 ++ clervers/http1s/client/client.py | 3 + clervers/http1s/server/certs/cert.pem | 21 ++ clervers/http1s/server/certs/key.pem | 28 +++ clervers/http1s/server/https_server.py | 66 +++++ clervers/http2/README.md | 9 + clervers/http2/client/main.go | 65 +++++ clervers/http2/go.mod | 19 ++ clervers/http2/go.sum | 116 +++++++++ clervers/http2/proto/greet.proto | 39 +++ clervers/http2/proto/greetpb/greet.pb.go | 230 ++++++++++++++++++ clervers/http2/proto/greetpb/greet_grpc.pb.go | 101 ++++++++ clervers/http2/server/main.go | 57 +++++ 18 files changed, 962 insertions(+) create mode 100644 clervers/http1/README.md create mode 100644 clervers/http1/client/client.sh create mode 100644 clervers/http1/go.mod create mode 100644 clervers/http1/go.sum create mode 100644 clervers/http1/server/main.go create mode 100644 clervers/http1s/README.md create mode 100644 clervers/http1s/client/client.py create mode 100644 clervers/http1s/server/certs/cert.pem create mode 100644 clervers/http1s/server/certs/key.pem create mode 100644 clervers/http1s/server/https_server.py create mode 100644 clervers/http2/README.md create mode 100644 clervers/http2/client/main.go create mode 100644 clervers/http2/go.mod create mode 100644 clervers/http2/go.sum create mode 100644 clervers/http2/proto/greet.proto create mode 100644 clervers/http2/proto/greetpb/greet.pb.go create mode 100644 clervers/http2/proto/greetpb/greet_grpc.pb.go create mode 100644 clervers/http2/server/main.go diff --git a/clervers/http1/README.md b/clervers/http1/README.md new file mode 100644 index 0000000..265de81 --- /dev/null +++ b/clervers/http1/README.md @@ -0,0 +1,9 @@ +## Run server +```bash +sudo go run server/main.go +``` + +## Running test client +```bash +bash ./client/client.sh +``` \ No newline at end of file diff --git a/clervers/http1/client/client.sh b/clervers/http1/client/client.sh new file mode 100644 index 0000000..651acbd --- /dev/null +++ b/clervers/http1/client/client.sh @@ -0,0 +1 @@ +curl -X POST -v localhost:8080/customResponse -d '{"size": 10}' \ No newline at end of file diff --git a/clervers/http1/go.mod b/clervers/http1/go.mod new file mode 100644 index 0000000..509063d --- /dev/null +++ b/clervers/http1/go.mod @@ -0,0 +1,33 @@ +module http1_clerver_modules + +go 1.22 + +replace http1_clerver_modules => ./ + +require ( + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.9.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/clervers/http1/go.sum b/clervers/http1/go.sum new file mode 100644 index 0000000..e968ca4 --- /dev/null +++ b/clervers/http1/go.sum @@ -0,0 +1,78 @@ +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/clervers/http1/server/main.go b/clervers/http1/server/main.go new file mode 100644 index 0000000..e580414 --- /dev/null +++ b/clervers/http1/server/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "log" + "math/rand" + "net/http" + "os" + "time" + + "github.com/gin-gonic/gin" +) + +const ( + defaultPort = "8080" + maxPayloadSize = 10 * 1024 * 1024 // 10 MB +) + +var ( + // source is a static, global rand object. + source *rand.Rand + letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~!@#$" +) + +func randStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[source.Intn(len(letterBytes))] + } + return string(b) +} + +func init() { + source = rand.New(rand.NewSource(time.Now().UnixNano())) +} + +// customResponse holds the requested size for the response payload. +type customResponse struct { + Size int `json:"size"` +} + +func postCustomResponse(context *gin.Context) { + var customResp customResponse + if err := context.BindJSON(&customResp); err != nil { + _ = context.AbortWithError(http.StatusBadRequest, err) + return + } + + if customResp.Size > maxPayloadSize { + _ = context.AbortWithError(http.StatusBadRequest, fmt.Errorf("requested size %d is bigger than max allowed %d", customResp, maxPayloadSize)) + return + } + + context.JSON(http.StatusOK, map[string]string{"answer": randStringBytes(customResp.Size)}) +} + +func main() { + engine := gin.New() + + engine.Use(gin.Recovery()) + engine.POST("/customResponse", postCustomResponse) + + port := os.Getenv("PORT") + if port == "" { + port = defaultPort + } + + fmt.Printf("listening on 0.0.0.0:%s\n", port) + if err := engine.Run(fmt.Sprintf("0.0.0.0:%s", port)); err != nil { + log.Fatal(err) + } +} diff --git a/clervers/http1s/README.md b/clervers/http1s/README.md new file mode 100644 index 0000000..b7cafe8 --- /dev/null +++ b/clervers/http1s/README.md @@ -0,0 +1,15 @@ +## Generate certs +```bash +cd main_src/server/certs +openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem +``` + +## Run server +```bash +python3 ./server/https_server.py 43421 +``` + +## Run client +```bash +python3 ./client/client.py +``` diff --git a/clervers/http1s/client/client.py b/clervers/http1s/client/client.py new file mode 100644 index 0000000..55ea7f5 --- /dev/null +++ b/clervers/http1s/client/client.py @@ -0,0 +1,3 @@ +import requests +body = {"Hello World": "I'm testing my HTTPS sniffer", "Results=": "Good"} +resp = requests.post("https://127.0.0.1:43421/example", json=body, verify=False) \ No newline at end of file diff --git a/clervers/http1s/server/certs/cert.pem b/clervers/http1s/server/certs/cert.pem new file mode 100644 index 0000000..fde9cec --- /dev/null +++ b/clervers/http1s/server/certs/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIULjhok7bMSNcfH6Iyf4y0OPclIDcwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAyMjgxMjA4MTlaFw0yMzAy +MjgxMjA4MTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCqXkApH3U29xdOhwrWUDODFyY3M+xfzAgoYD/ZixpM +Sr4y1P+B9x4I9yI1KIUgrwcypn3Igpz0f/YltG+amX3TTtmkJuDcA54QDiQEEUXj +ZlhKxt2TnhDXGoI2yQx1weKzjfbZhug8TpnFrpu446imOqiA09BTOhiC8Fe+ajQx +GmPndBk09QtEG1gDCo4/0ckLdNYjBExAzbRy/LjuokDE7KlJ5ocRLcI9VdGiJ9iP ++opDuKuTJynwoK6hkCK0hQFZSXXerg8kkWE7RnV8U5ZqTQ6xvThwCslCoGI5vpVK +3iGSOjzHDPyRSXJhTDadFcJD+afURcnahR5Rg5l/UJzRAgMBAAGjUzBRMB0GA1Ud +DgQWBBRMZePjcX4WEpDdvJgYJR7fjSsr1TAfBgNVHSMEGDAWgBRMZePjcX4WEpDd +vJgYJR7fjSsr1TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCJ +IKSyEMtmLILLAK6PQ0EQIwUYmVCpU/FlesMg9vew8WEfxKEzK0m1qrleBfh46Qtm +TUHcEFovVtrMEJ8j4ythQtOcaQHHp7rW8QSexpUrjCZdP64Oo6BD1jI5Cj+w7h0h +HSsNo7igWNXtOri+DGKX+A9u8OfvDPddfc/EGawB1Knt3Um/hh8EM66KUFXco1LE +oSS7hVWnQC7YEuGH9N+fCApdkzn1UvHE9nmgn9jSCcf3Q+A7t7UYnW5x22hX7FMx +cGcq82PhjLFfg8L1pprG4irvNjs6MJUWRdNiN0CyariWSvpvAxtEBtIeXnhqj33b +nWcsrhu+uVbkholAbrGy +-----END CERTIFICATE----- diff --git a/clervers/http1s/server/certs/key.pem b/clervers/http1s/server/certs/key.pem new file mode 100644 index 0000000..e46ba09 --- /dev/null +++ b/clervers/http1s/server/certs/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCqXkApH3U29xdO +hwrWUDODFyY3M+xfzAgoYD/ZixpMSr4y1P+B9x4I9yI1KIUgrwcypn3Igpz0f/Yl +tG+amX3TTtmkJuDcA54QDiQEEUXjZlhKxt2TnhDXGoI2yQx1weKzjfbZhug8TpnF +rpu446imOqiA09BTOhiC8Fe+ajQxGmPndBk09QtEG1gDCo4/0ckLdNYjBExAzbRy +/LjuokDE7KlJ5ocRLcI9VdGiJ9iP+opDuKuTJynwoK6hkCK0hQFZSXXerg8kkWE7 +RnV8U5ZqTQ6xvThwCslCoGI5vpVK3iGSOjzHDPyRSXJhTDadFcJD+afURcnahR5R +g5l/UJzRAgMBAAECggEBAIPbrKbnTQ48kZJanH2g1y2XpiFFk6XVQV8Wl3Rk3/a6 +RpkLF7JcM6fWtmgUM21Haje+ek3NIXNu8nDeRR6Pu73nIjWVTaWbOyPL/f4T7FaC +6MT/Q5Ez3m7NeoiydG1ToYU81Bgp/OZoI0XHzYh80xPNBIuo5Gz14vC82fxrMHfr +ZINH5A0E2QYJ1gyMlhbp6hLigY6NptfAELoVvoctjoYAgozEjEAAQjOG1s4Cocdk +zkq1uJFTVhKT+IED9/HKzPWlBNCdPvDAbehkcsEJb7A4DeKF+1eVNL354yUtVlr0 +0yf1OeTxtdwp7eFf0j/AxdJ0XXtmql+kj2PTS0MJmAECgYEA18ZSemR3Pnu7I83Q +GvmXMWQWs7B5gn8AUPZh+Yqhvi5yIqzm+za9eEXDb+M0hp5wmWtKs5/7qx89Dt+0 +3g1BFJyRyduvkJGV6AnSJkOimcApM/9oVEKBgBtbTES+eCvKiFUzK+o9LGuYxN1t +Ag4F11oKm1ZSfZRpQeOAGak8HoECgYEAyiDwAF5k2OVpeZEybBwuoD/3a6EBAzcg +M+hgTM77tj8MhfNh7YKl8A9/4LWhp8NOIAcEvzNxtmqUxYJP6Pm4Q85d9CpuZOev +QUnKDXOHT2B9fSRihNoZ6VmV4gXa5POdok8ZW2VlbtdgaOY8Gkf5i9y0YKRjQx30 +f8osgYZ79lECgYEAx6KjDsBDH1llBLRYNYz1WOosouXFFnqgdUfvz+x2xzm7ud9U ++dpkpJGPR8bkAyU2Mov1GooLVADcAhXyBnwm2YYe0K9kGRLJqlzjxSlQmIYU2RoW +kdbScA9fggocW5zQjyCc7qWTIbUPLB1dzMfimaOiKqRMQAn+9Moi7BsLF4ECgYEA +iQ1eTpGyhZLAOsqlysItJ0FYLWVE/34gWrHkog/ygrcrcaP+MYJVq3mG9sGRM+Rs +k7DOmipynwKTj3x1XH3+YBOMhyNCKS8jGPzEgOxlwf+l9vV1y6jqytuQkmnv6y/4 +IXbKtbsB1O86ksYR2KwW26uxrpmoKHkmMIPQO58JbJECgYBQ0eDn/253h6VLkTBn +3djuA1Ghey1Q7zSySViXYYK9y+SR1kT44o6LBpKVss0KL0SudYiVdfv+pLNXyk4v +5kPtXyIM1Dpcb3mMh1um47R3UOmM+R3f0ekUsBxkqkBP2LQeVkjCmDXY7m46fthT +Fh5u/vW4J72AJIVcFCsfmn/jwA== +-----END PRIVATE KEY----- diff --git a/clervers/http1s/server/https_server.py b/clervers/http1s/server/https_server.py new file mode 100644 index 0000000..1ae7bb9 --- /dev/null +++ b/clervers/http1s/server/https_server.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +import os.path +from http.server import HTTPServer, BaseHTTPRequestHandler + +import ssl +import contextlib +import threading +import json +import sys +import signal + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +CERTS_DIR = os.path.join(CURRENT_DIR, "certs") +KEY_PEM = os.path.join(CERTS_DIR, "key.pem") +CERT_PEM = os.path.join(CERTS_DIR, "cert.pem") + + +class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): + + def do_POST(self): + content_len = int(self.headers.get('content-length')) + post_body = self.rfile.read(content_len) + try: + json.loads(post_body) + except Exception: + self.send_response(400) + self.end_headers() + self.wfile.write(json.dumps({ + "error": "request body is not json", + "original body": post_body + }).encode()) + return + + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.send_header('Content-length', str(content_len)) + + self.end_headers() + self.wfile.write(post_body) + + +@contextlib.contextmanager +def tls_server(host, port): + httpd = HTTPServer((host, port), SimpleHTTPRequestHandler) + httpd.socket = ssl.wrap_socket(httpd.socket, keyfile=KEY_PEM, certfile=CERT_PEM, server_side=True) + thread = threading.Thread(target=httpd.serve_forever, daemon=True) + thread.start() + yield + httpd.server_close() + thread.join(timeout=5) + + +def signal_handler(sig, frame): + print('You pressed Ctrl+C!') + sys.exit(0) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"{sys.argv[0]} ") + sys.exit(1) + with tls_server("0.0.0.0", int(sys.argv[1])): + print(f"TLS server (PID {os.getpid()}) is listening on 0.0.0.0:{sys.argv[1]}") + signal.signal(signal.SIGINT, signal_handler) + print('Press Ctrl+C') + signal.pause() diff --git a/clervers/http2/README.md b/clervers/http2/README.md new file mode 100644 index 0000000..5b54391 --- /dev/null +++ b/clervers/http2/README.md @@ -0,0 +1,9 @@ +## Run server +```bash +sudo go build -o /tmp/grpc_server server/main.go && /tmp/grpc_server +``` + +## Run client +```bash +sudo go build -o ./tmp/grpc_client client/main.go && ./tmp/grpc_client --count 1 +``` \ No newline at end of file diff --git a/clervers/http2/client/main.go b/clervers/http2/client/main.go new file mode 100644 index 0000000..3d25fdb --- /dev/null +++ b/clervers/http2/client/main.go @@ -0,0 +1,65 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package main + +import ( + "context" + "flag" + "log" + "time" + + "google.golang.org/grpc" + + pb "pixielabs.ai/pixie/demos/http2-tracing/proto/greetpb" +) + +func mustCreateGrpcClientConn(address string) *grpc.ClientConn { + conn, err := grpc.Dial(address, grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + return conn +} + +func connectAndGreet(address, name string, count, sleep_millis int) { + conn := mustCreateGrpcClientConn(address) + defer conn.Close() + + c := pb.NewGreeterClient(conn) + + for i := 0; i < count; i++ { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) + defer cancel() + r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) + if err != nil { + log.Fatalf("could not greet: %v", err) + } + log.Printf("Greeting: %s", r.Message) + time.Sleep(time.Duration(sleep_millis) * time.Millisecond) + } +} + +func main() { + address := flag.String("address", "localhost:50051", "Server end point.") + name := flag.String("name", "world", "The name to greet.") + count := flag.Int("count", 1, "The number of RPC calls to make.") + sleep_millis := flag.Int("sleep-millis", 500, "The number of milliseconds to sleep between RPC calls.") + flag.Parse() + connectAndGreet(*address, *name, *count, *sleep_millis) +} diff --git a/clervers/http2/go.mod b/clervers/http2/go.mod new file mode 100644 index 0000000..ab00741 --- /dev/null +++ b/clervers/http2/go.mod @@ -0,0 +1,19 @@ +module pixielabs.ai/pixie/demos/http2-tracing + +go 1.22 + +replace pixielabs.ai/pixie/demos/http2-tracing => ./ + +require ( + google.golang.org/grpc v1.43.0 + google.golang.org/protobuf v1.25.0 +) + +require ( + github.com/golang/protobuf v1.4.3 // indirect + // github.com/iovisor/gobpf v0.2.0 // indirect + golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + golang.org/x/text v0.3.0 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect +) diff --git a/clervers/http2/go.sum b/clervers/http2/go.sum new file mode 100644 index 0000000..da538da --- /dev/null +++ b/clervers/http2/go.sum @@ -0,0 +1,116 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/clervers/http2/proto/greet.proto b/clervers/http2/proto/greet.proto new file mode 100644 index 0000000..f9b6869 --- /dev/null +++ b/clervers/http2/proto/greet.proto @@ -0,0 +1,39 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +syntax = "proto3"; + +package greet; + +option go_package = "proto/greetpb;greetpb"; + +// The greeting service. Used to demo HTTP2 tracer with gRPC application. +service Greeter { + // Sends a greeting to the provided name. + rpc SayHello(HelloRequest) returns (HelloReply); +} + +// The request message containing the name to greet. +message HelloRequest { + string name = 1; +} + +// The response message containing the greeting to a name. +message HelloReply { + string message = 1; +} diff --git a/clervers/http2/proto/greetpb/greet.pb.go b/clervers/http2/proto/greetpb/greet.pb.go new file mode 100644 index 0000000..c100dfc --- /dev/null +++ b/clervers/http2/proto/greetpb/greet.pb.go @@ -0,0 +1,230 @@ +// +// Copyright 2018- The Pixie Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.6.1 +// source: proto/greet.proto + +package greetpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// The request message containing the name to greet. +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_greet_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_greet_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_proto_greet_proto_rawDescGZIP(), []int{0} +} + +func (x *HelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// The response message containing the greeting to a name. +type HelloReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *HelloReply) Reset() { + *x = HelloReply{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_greet_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloReply) ProtoMessage() {} + +func (x *HelloReply) ProtoReflect() protoreflect.Message { + mi := &file_proto_greet_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead. +func (*HelloReply) Descriptor() ([]byte, []int) { + return file_proto_greet_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloReply) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_proto_greet_proto protoreflect.FileDescriptor + +var file_proto_greet_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x67, 0x72, 0x65, 0x65, 0x74, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, + 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, + 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x3d, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, + 0x72, 0x12, 0x32, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x13, 0x2e, + 0x67, 0x72, 0x65, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x67, 0x72, 0x65, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x17, 0x5a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, + 0x72, 0x65, 0x65, 0x74, 0x70, 0x62, 0x3b, 0x67, 0x72, 0x65, 0x65, 0x74, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_greet_proto_rawDescOnce sync.Once + file_proto_greet_proto_rawDescData = file_proto_greet_proto_rawDesc +) + +func file_proto_greet_proto_rawDescGZIP() []byte { + file_proto_greet_proto_rawDescOnce.Do(func() { + file_proto_greet_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_greet_proto_rawDescData) + }) + return file_proto_greet_proto_rawDescData +} + +var file_proto_greet_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_greet_proto_goTypes = []interface{}{ + (*HelloRequest)(nil), // 0: greet.HelloRequest + (*HelloReply)(nil), // 1: greet.HelloReply +} +var file_proto_greet_proto_depIdxs = []int32{ + 0, // 0: greet.Greeter.SayHello:input_type -> greet.HelloRequest + 1, // 1: greet.Greeter.SayHello:output_type -> greet.HelloReply + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_proto_greet_proto_init() } +func file_proto_greet_proto_init() { + if File_proto_greet_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_greet_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_greet_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_greet_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_greet_proto_goTypes, + DependencyIndexes: file_proto_greet_proto_depIdxs, + MessageInfos: file_proto_greet_proto_msgTypes, + }.Build() + File_proto_greet_proto = out.File + file_proto_greet_proto_rawDesc = nil + file_proto_greet_proto_goTypes = nil + file_proto_greet_proto_depIdxs = nil +} diff --git a/clervers/http2/proto/greetpb/greet_grpc.pb.go b/clervers/http2/proto/greetpb/greet_grpc.pb.go new file mode 100644 index 0000000..976c9e8 --- /dev/null +++ b/clervers/http2/proto/greetpb/greet_grpc.pb.go @@ -0,0 +1,101 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package greetpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// GreeterClient is the client API for Greeter service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GreeterClient interface { + // Sends a greeting to the provided name. + SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) +} + +type greeterClient struct { + cc grpc.ClientConnInterface +} + +func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { + return &greeterClient{cc} +} + +func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { + out := new(HelloReply) + err := c.cc.Invoke(ctx, "/greet.Greeter/SayHello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GreeterServer is the server API for Greeter service. +// All implementations should embed UnimplementedGreeterServer +// for forward compatibility +type GreeterServer interface { + // Sends a greeting to the provided name. + SayHello(context.Context, *HelloRequest) (*HelloReply, error) +} + +// UnimplementedGreeterServer should be embedded to have forward compatible implementations. +type UnimplementedGreeterServer struct { +} + +func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} + +// UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GreeterServer will +// result in compilation errors. +type UnsafeGreeterServer interface { + mustEmbedUnimplementedGreeterServer() +} + +func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) { + s.RegisterService(&Greeter_ServiceDesc, srv) +} + +func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/greet.Greeter/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Greeter_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "greet.Greeter", + HandlerType: (*GreeterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _Greeter_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/greet.proto", +} diff --git a/clervers/http2/server/main.go b/clervers/http2/server/main.go new file mode 100644 index 0000000..62eac9a --- /dev/null +++ b/clervers/http2/server/main.go @@ -0,0 +1,57 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package main + +import ( + "context" + "flag" + "log" + "net" + "strconv" + + "google.golang.org/grpc" + + pb "pixielabs.ai/pixie/demos/http2-tracing/proto/greetpb" +) + +type server struct{} + +func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { + return &pb.HelloReply{Message: "Hello " + in.Name}, nil +} + +func main() { + port := flag.Int("port", 50051, "The port to listen.") + flag.Parse() + + log.Printf("Starting http server on port: %d", *port) + lis, err := net.Listen("tcp", ":"+strconv.Itoa(*port)) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + + s := grpc.NewServer() + + log.Printf("Launching unary server") + pb.RegisterGreeterServer(s, &server{}) + + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} From ee27c25f666badb61bade6e2e4f7d86db88053bb Mon Sep 17 00:00:00 2001 From: Topspin Date: Sat, 6 Apr 2024 13:54:12 +0000 Subject: [PATCH 2/5] initial commit for kprobe_for_http1 --- kprobe_http1/README.md | 4 + kprobe_http1/go.mod | 10 + kprobe_http1/go.sum | 4 + kprobe_http1/module_src/bpfwrapper/kprobes.go | 146 ++++++ .../bpfwrapper/perfbufferreaders.go | 165 ++++++ .../module_src/connections/factory.go | 78 +++ .../module_src/connections/tracker.go | 130 +++++ kprobe_http1/module_src/settings/clock.go | 48 ++ kprobe_http1/module_src/structs/enums.go | 31 ++ kprobe_http1/module_src/structs/structs.go | 103 ++++ kprobe_http1/sniffer/kprobe_http1_src.c | 496 ++++++++++++++++++ kprobe_http1/sniffer/main.go | 103 ++++ 12 files changed, 1318 insertions(+) create mode 100644 kprobe_http1/README.md create mode 100644 kprobe_http1/go.mod create mode 100644 kprobe_http1/go.sum create mode 100644 kprobe_http1/module_src/bpfwrapper/kprobes.go create mode 100644 kprobe_http1/module_src/bpfwrapper/perfbufferreaders.go create mode 100644 kprobe_http1/module_src/connections/factory.go create mode 100644 kprobe_http1/module_src/connections/tracker.go create mode 100644 kprobe_http1/module_src/settings/clock.go create mode 100644 kprobe_http1/module_src/structs/enums.go create mode 100644 kprobe_http1/module_src/structs/structs.go create mode 100644 kprobe_http1/sniffer/kprobe_http1_src.c create mode 100644 kprobe_http1/sniffer/main.go diff --git a/kprobe_http1/README.md b/kprobe_http1/README.md new file mode 100644 index 0000000..067cbee --- /dev/null +++ b/kprobe_http1/README.md @@ -0,0 +1,4 @@ +## Run sniffer +```bash +sudo go run ./sniffer/main.go ./sniffer/kprobe_http1_src.c +``` diff --git a/kprobe_http1/go.mod b/kprobe_http1/go.mod new file mode 100644 index 0000000..da0cb38 --- /dev/null +++ b/kprobe_http1/go.mod @@ -0,0 +1,10 @@ +module kprobe_http1_modules + +go 1.22 + +replace kprobe_http1_modules => ./ + +require ( + github.com/iovisor/gobpf v0.2.0 + golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 +) diff --git a/kprobe_http1/go.sum b/kprobe_http1/go.sum new file mode 100644 index 0000000..1f46f61 --- /dev/null +++ b/kprobe_http1/go.sum @@ -0,0 +1,4 @@ +github.com/iovisor/gobpf v0.2.0 h1:34xkQxft+35GagXBk3n23eqhm0v7q0ejeVirb8sqEOQ= +github.com/iovisor/gobpf v0.2.0/go.mod h1:WSY9Jj5RhdgC3ci1QaacvbFdQ8cbrEjrpiZbLHLt2s4= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/kprobe_http1/module_src/bpfwrapper/kprobes.go b/kprobe_http1/module_src/bpfwrapper/kprobes.go new file mode 100644 index 0000000..486f6d8 --- /dev/null +++ b/kprobe_http1/module_src/bpfwrapper/kprobes.go @@ -0,0 +1,146 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package bpfwrapper + +import ( + "fmt" + "log" + + bpf "github.com/iovisor/gobpf/bcc" +) + +const ( + maxActiveConnections = 1024 +) + +// ProbeType represents whether the probe is an entry or a return. +type ProbeType int + +const ( + EntryType ProbeType = 0 + ReturnType ProbeType = 1 +) + +// Kprobe represents a single Kprobe hook. +type Kprobe struct { + // The name of the function to hook. + FunctionToHook string + // The name of the hook function. + HookName string + // Whether a Kprobe or ret-Kprobe. + Type ProbeType + // Whether the function to hook is syscall or not. + IsSyscall bool +} + +// AttachKprobes attaches the given Kprobe list. +func AttachKprobes(bpfModule *bpf.Module) error { + for _, probe := range defaultKprobes { + log.Printf("Loading %q for %q as %d\n", probe.HookName, probe.FunctionToHook, probe.Type) + functionToHook := probe.FunctionToHook + if probe.IsSyscall { + functionToHook = bpf.GetSyscallFnName(probe.FunctionToHook) + } + + probeFD, err := bpfModule.LoadKprobe(probe.HookName) + if err != nil { + return fmt.Errorf("failed to load %q due to: %v", probe.HookName, err) + } + + switch probe.Type { + case EntryType: + if err = bpfModule.AttachKprobe(functionToHook, probeFD, maxActiveConnections); err != nil { + return fmt.Errorf("failed to attach kprobe %q to %q due to: %v", probe.HookName, functionToHook, err) + } + case ReturnType: + if err = bpfModule.AttachKretprobe(functionToHook, probeFD, maxActiveConnections); err != nil { + return fmt.Errorf("failed to attach kretprobe %q to %q due to: %v", probe.HookName, functionToHook, err) + } + default: + return fmt.Errorf("unknown Kprobe type %d given for %q", probe.Type, probe.HookName) + } + } + return nil +} + +var ( + // defaultKprobes is the default kprobes to attach. + defaultKprobes = []Kprobe{ + { + FunctionToHook: "accept", + HookName: "syscall__probe_entry_accept", + Type: EntryType, + IsSyscall: true, + }, + { + FunctionToHook: "accept", + HookName: "syscall__probe_ret_accept", + Type: ReturnType, + IsSyscall: true, + }, + { + FunctionToHook: "accept4", + HookName: "syscall__probe_entry_accept4", + Type: EntryType, + IsSyscall: true, + }, + { + FunctionToHook: "accept4", + HookName: "syscall__probe_ret_accept4", + Type: ReturnType, + IsSyscall: true, + }, + { + FunctionToHook: "write", + HookName: "syscall__probe_entry_write", + Type: EntryType, + IsSyscall: true, + }, + { + FunctionToHook: "write", + HookName: "syscall__probe_ret_write", + Type: ReturnType, + IsSyscall: true, + }, + { + FunctionToHook: "read", + HookName: "syscall__probe_entry_read", + Type: EntryType, + IsSyscall: true, + }, + { + FunctionToHook: "read", + HookName: "syscall__probe_ret_read", + Type: ReturnType, + IsSyscall: true, + }, + { + FunctionToHook: "close", + HookName: "syscall__probe_entry_close", + Type: EntryType, + IsSyscall: true, + }, + { + FunctionToHook: "close", + HookName: "syscall__probe_ret_close", + Type: ReturnType, + IsSyscall: true, + }, + } +) \ No newline at end of file diff --git a/kprobe_http1/module_src/bpfwrapper/perfbufferreaders.go b/kprobe_http1/module_src/bpfwrapper/perfbufferreaders.go new file mode 100644 index 0000000..af0bca9 --- /dev/null +++ b/kprobe_http1/module_src/bpfwrapper/perfbufferreaders.go @@ -0,0 +1,165 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package bpfwrapper + +import ( + "bytes" + "encoding/binary" + "fmt" + "kprobe_http1_modules/module_src/connections" + "kprobe_http1_modules/module_src/settings" + "kprobe_http1_modules/module_src/structs" + "log" + "unsafe" + + bpf "github.com/iovisor/gobpf/bcc" +) + +// ProbeEventLoop is the signature for the callback functions to extract the events from the input channel. +type ProbeEventLoop func(inputChan chan []byte, connectionFactory *connections.Factory) + +// ProbeChannel represents a single handler to a channel of events in the BPF. +type ProbeChannel struct { + // Name of the BPF channel. + name string + // Event loop handler, a method which receive a channel for the input events from the implementation, and parse them. + eventLoop ProbeEventLoop + // A go channel which holds the messages from the BPF module. + eventChannel chan []byte + // A go channel for lost events. + lostEventsChannel chan uint64 + // The bpf perf map that links our user mode channel to the BPF module. + perfMap *bpf.PerfMap +} + +// NewProbeChannel creates a new probe channel with the given handle for the given bpf channel name. +func NewProbeChannel(name string, handler ProbeEventLoop) *ProbeChannel { + return &ProbeChannel{ + name: name, + eventLoop: handler, + } +} + +// Start initiate a goroutine for the event loop handler, for a lost events messages and the perf map. +func (probeChannel *ProbeChannel) Start(module *bpf.Module, connectionFactory *connections.Factory) error { + probeChannel.eventChannel = make(chan []byte) + probeChannel.lostEventsChannel = make(chan uint64) + + table := bpf.NewTable(module.TableId(probeChannel.name), module) + + var err error + probeChannel.perfMap, err = bpf.InitPerfMapWithPageCnt(table, probeChannel.eventChannel, probeChannel.lostEventsChannel, 8192) + if err != nil { + return fmt.Errorf("failed to init perf mapping for %q due to: %v", probeChannel.name, err) + } + + go probeChannel.eventLoop(probeChannel.eventChannel, connectionFactory) + go func() { + for { + <-probeChannel.lostEventsChannel + } + }() + + probeChannel.perfMap.Start() + return nil +} + +// LaunchPerfBufferConsumers launches all probe channels. +func LaunchPerfBufferConsumers(module *bpf.Module, connectionFactory *connections.Factory) error { + for _, probeChannel := range defaultPerfBufferHandlers { + if err := probeChannel.Start(module, connectionFactory); err != nil { + return err + } + } + + return nil +} + +var ( + // defaultPerfBufferHandlers is the default handlers for the events coming from the kernel. + defaultPerfBufferHandlers = []*ProbeChannel{ + NewProbeChannel("socket_data_events", socketDataEventCallback), + NewProbeChannel("socket_open_events", socketOpenEventCallback), + NewProbeChannel("socket_close_events", socketCloseEventCallback), + } + + eventAttributesSize = int(unsafe.Sizeof(structs.SocketDataEventAttr{})) +) + +func socketDataEventCallback(inputChan chan []byte, connectionFactory *connections.Factory) { + for data := range inputChan { + if data == nil { + return + } + if len(data) < eventAttributesSize { + log.Printf("Buffer's for SocketDataEvent is smaller (%d) than the minimum required (%d)", len(data), eventAttributesSize) + continue + } else if len(data) > structs.EventBodyMaxSize+eventAttributesSize { + log.Printf("Buffer's for SocketDataEvent is bigger (%d) than the maximum for the struct (%d)", len(data), structs.EventBodyMaxSize+eventAttributesSize) + continue + } + var event structs.SocketDataEvent + + // binary.Read require the input data to be at the same size of the object. + // Since the Msg field might be mostly empty, binary.read fails. + // So we split the loading into the fixed size attribute parts, and copying the message separately. + if err := binary.Read(bytes.NewReader(data[:eventAttributesSize]), bpf.GetHostByteOrder(), &event.Attr); err != nil { + log.Printf("Failed to decode received data: %+v", err) + continue + } + + // If there is at least single byte over the required minimum, thus we should copy it. + if len(data) > eventAttributesSize { + copy(event.Msg[:], data[eventAttributesSize:eventAttributesSize+int(event.Attr.MsgSize)]) + } + event.Attr.TimestampNano += settings.GetRealTimeOffset() + connectionFactory.GetOrCreate(event.Attr.ConnID).AddDataEvent(event) + } +} + +func socketOpenEventCallback(inputChan chan []byte, connectionFactory *connections.Factory) { + for data := range inputChan { + if data == nil { + return + } + var event structs.SocketOpenEvent + + if err := binary.Read(bytes.NewReader(data), bpf.GetHostByteOrder(), &event); err != nil { + log.Printf("Failed to decode received data: %+v", err) + continue + } + event.TimestampNano += settings.GetRealTimeOffset() + connectionFactory.GetOrCreate(event.ConnID).AddOpenEvent(event) + } +} + +func socketCloseEventCallback(inputChan chan []byte, connectionFactory *connections.Factory) { + for data := range inputChan { + if data == nil { + return + } + var event structs.SocketCloseEvent + if err := binary.Read(bytes.NewReader(data), bpf.GetHostByteOrder(), &event); err != nil { + log.Printf("Failed to decode received data: %+v", err) + continue + } + event.TimestampNano += settings.GetRealTimeOffset() + connectionFactory.GetOrCreate(event.ConnID).AddCloseEvent(event) + } +} diff --git a/kprobe_http1/module_src/connections/factory.go b/kprobe_http1/module_src/connections/factory.go new file mode 100644 index 0000000..08d9fc5 --- /dev/null +++ b/kprobe_http1/module_src/connections/factory.go @@ -0,0 +1,78 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package connections + +import ( + "fmt" + "kprobe_http1_modules/module_src/structs" + "sync" + "time" +) + +// Factory is a routine-safe container that holds a trackers with unique ID, and able to create new tracker. +type Factory struct { + connections map[structs.ConnID]*Tracker + inactivityThreshold time.Duration + mutex *sync.RWMutex +} + +// NewFactory creates a new instance of the factory. +func NewFactory(inactivityThreshold time.Duration) *Factory { + return &Factory{ + connections: make(map[structs.ConnID]*Tracker), + mutex: &sync.RWMutex{}, + inactivityThreshold: inactivityThreshold, + } +} + +func (factory *Factory) HandleReadyConnections() { + trackersToDelete := make(map[structs.ConnID]struct{}) + + for connID, tracker := range factory.connections { + if tracker.IsComplete() { + trackersToDelete[connID] = struct{}{} + if len(tracker.sentBuf) == 0 && len(tracker.recvBuf) == 0 { + continue + } + fmt.Printf("========================>\nFound HTTP payload\nRequest->\n%s\n\nResponse->\n%s\n\n<========================\n", tracker.recvBuf, tracker.sentBuf) + } else if tracker.Malformed() { + trackersToDelete[connID] = struct{}{} + } else if tracker.IsInactive(factory.inactivityThreshold) { + trackersToDelete[connID] = struct{}{} + } + } + factory.mutex.Lock() + defer factory.mutex.Unlock() + for key := range trackersToDelete { + delete(factory.connections, key) + } +} + +// GetOrCreate returns a tracker that related to the given connection and transaction ids. If there is no such tracker +// we create a new one. +func (factory *Factory) GetOrCreate(connectionID structs.ConnID) *Tracker { + factory.mutex.Lock() + defer factory.mutex.Unlock() + tracker, ok := factory.connections[connectionID] + if !ok { + factory.connections[connectionID] = NewTracker(connectionID) + return factory.connections[connectionID] + } + return tracker +} diff --git a/kprobe_http1/module_src/connections/tracker.go b/kprobe_http1/module_src/connections/tracker.go new file mode 100644 index 0000000..fbe0a6b --- /dev/null +++ b/kprobe_http1/module_src/connections/tracker.go @@ -0,0 +1,130 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package connections + +import ( + structs2 "kprobe_http1_modules/module_src/structs" + "log" + "sync" + "time" +) + +const ( + maxBufferSize = 100 * 1024 // 100KB +) + +type Tracker struct { + connID structs2.ConnID + + addr structs2.SockAddrIn + openTimestamp uint64 + closeTimestamp uint64 + totalWrittenBytes uint64 + totalReadBytes uint64 + + // Indicates the tracker stopped tracking due to closing the session. + lastActivityTimestamp uint64 + sentBytes uint64 + recvBytes uint64 + + recvBuf []byte + sentBuf []byte + mutex sync.RWMutex +} + +func NewTracker(connID structs2.ConnID) *Tracker { + return &Tracker{ + connID: connID, + recvBuf: make([]byte, 0, maxBufferSize), + sentBuf: make([]byte, 0, maxBufferSize), + mutex: sync.RWMutex{}, + } +} + +func (conn *Tracker) ToBytes() ([]byte, []byte) { + conn.mutex.RLock() + defer conn.mutex.RUnlock() + return conn.recvBuf, conn.sentBuf +} + +func (conn *Tracker) IsInactive(duration time.Duration) bool { + conn.mutex.RLock() + defer conn.mutex.RUnlock() + return uint64(time.Now().UnixNano())-conn.lastActivityTimestamp > uint64(duration.Nanoseconds()) +} + +func (conn *Tracker) IsComplete() bool { + conn.mutex.RLock() + defer conn.mutex.RUnlock() + return conn.closeTimestamp != 0 && + conn.totalReadBytes == conn.recvBytes && + conn.totalWrittenBytes == conn.sentBytes +} + +func (conn *Tracker) Malformed() bool { + conn.mutex.RLock() + defer conn.mutex.RUnlock() + return conn.closeTimestamp != 0 && + conn.totalReadBytes != conn.recvBytes && + conn.totalWrittenBytes != conn.sentBytes +} + +func (conn *Tracker) AddDataEvent(event structs2.SocketDataEvent) { + conn.mutex.Lock() + defer conn.mutex.Unlock() + conn.updateTimestamps() + + switch event.Attr.Direction { + case structs2.EgressTraffic: + conn.sentBuf = append(conn.sentBuf, event.Msg[:event.Attr.MsgSize]...) + conn.sentBytes += uint64(event.Attr.MsgSize) + case structs2.IngressTraffic: + conn.recvBuf = append(conn.recvBuf, event.Msg[:event.Attr.MsgSize]...) + conn.recvBytes += uint64(event.Attr.MsgSize) + default: + } +} + +func (conn *Tracker) AddOpenEvent(event structs2.SocketOpenEvent) { + conn.mutex.Lock() + defer conn.mutex.Unlock() + conn.updateTimestamps() + conn.addr = event.Addr + if conn.openTimestamp != 0 && conn.openTimestamp != event.TimestampNano { + log.Printf("Changed open info timestamp from %v to %v", conn.openTimestamp, event.TimestampNano) + } + conn.openTimestamp = event.TimestampNano +} + +func (conn *Tracker) AddCloseEvent(event structs2.SocketCloseEvent) { + conn.mutex.Lock() + defer conn.mutex.Unlock() + conn.updateTimestamps() + if conn.closeTimestamp != 0 && conn.closeTimestamp != event.TimestampNano { + log.Printf("changed close info timestamp from %v to %v", conn.closeTimestamp, event.TimestampNano) + } + conn.closeTimestamp = event.TimestampNano + + conn.totalWrittenBytes = uint64(event.WrittenBytes) + conn.totalReadBytes = uint64(event.ReadBytes) +} + +func (conn *Tracker) updateTimestamps() { + conn.lastActivityTimestamp = uint64(time.Now().UnixNano()) +} diff --git a/kprobe_http1/module_src/settings/clock.go b/kprobe_http1/module_src/settings/clock.go new file mode 100644 index 0000000..18c3fc7 --- /dev/null +++ b/kprobe_http1/module_src/settings/clock.go @@ -0,0 +1,48 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package settings + +import ( + "fmt" + "time" + + "golang.org/x/sys/unix" +) + +var ( + realTimeOffset uint64 = 0 +) + +// InitRealTimeOffset calculates the offset between the real clock and the monotonic clock used in the BPF. +func InitRealTimeOffset() error { + var monotonicTime, realTime unix.Timespec + if err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &monotonicTime); err != nil { + return fmt.Errorf("failed getting monotonic clock due to: %v", err) + } + if err := unix.ClockGettime(unix.CLOCK_REALTIME, &realTime); err != nil { + return fmt.Errorf("failed getting real clock time due to: %v", err) + } + realTimeOffset = uint64(time.Second)*(uint64(realTime.Sec)-uint64(monotonicTime.Sec)) + uint64(realTime.Nsec) - uint64(monotonicTime.Nsec) + return nil +} + +// GetRealTimeOffset is a getter for the real-time-offset. +func GetRealTimeOffset() uint64 { + return realTimeOffset +} diff --git a/kprobe_http1/module_src/structs/enums.go b/kprobe_http1/module_src/structs/enums.go new file mode 100644 index 0000000..ff5075a --- /dev/null +++ b/kprobe_http1/module_src/structs/enums.go @@ -0,0 +1,31 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package structs + +// TrafficDirectionEnum is a GO-equivalent for the following enum. +// enum traffic_direction_t { +// kEgress, +// kIngress, +// };. +type TrafficDirectionEnum int32 + +const ( + EgressTraffic TrafficDirectionEnum = 0 + IngressTraffic TrafficDirectionEnum = 1 +) diff --git a/kprobe_http1/module_src/structs/structs.go b/kprobe_http1/module_src/structs/structs.go new file mode 100644 index 0000000..6c51618 --- /dev/null +++ b/kprobe_http1/module_src/structs/structs.go @@ -0,0 +1,103 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package structs + +// ConnID is a conversion of the following C-Struct into GO. +// struct conn_id_t { +// uint32_t tgid; +// int32_t fd; +// uint64_t tsid; +// };. +type ConnID struct { + TGID uint32 + FD int32 + TsID uint64 +} + +// SockAddrIn is a conversion of the following C-Struct into GO. +// struct sockaddr_in { +// unsigned short int sin_family; +// uint16_t sin_port; +// struct in_addr sin_addr; +// +// /* _to size of `struct sockaddr'. */ +// unsigned char sin_zero[8]; +// };. +type SockAddrIn struct { + SinFamily uint16 + SinPort uint16 + SinAddr uint32 + SinZero [8]byte +} + +// SocketDataEventAttr is a conversion of the following C-Struct into GO. +// struct attr_t { +// uint64_t timestamp_ns; +// struct conn_id_t conn_id; +// enum traffic_direction_t direction; +// uint32_t msg_size; +// uint64_t pos; +// };. +type SocketDataEventAttr struct { + TimestampNano uint64 + ConnID ConnID + Direction TrafficDirectionEnum + MsgSize uint32 + Pos uint64 +} + +const ( + EventBodyMaxSize = 30720 +) + +// SocketDataEvent is a conversion of the following C-Struct into GO. +// struct socket_data_event_t { +// struct attr_t attr; +// char msg[30720]; +// };. +type SocketDataEvent struct { + Attr SocketDataEventAttr + Msg [EventBodyMaxSize]byte +} + +// SocketOpenEvent is a conversion of the following C-Struct into GO. +// struct socket_open_event_t { +// uint64_t timestamp_ns; +// struct conn_id_t conn_id; +// struct sockaddr_in* addr; +//};. +type SocketOpenEvent struct { + TimestampNano uint64 + ConnID ConnID + Addr SockAddrIn +} + +// SocketCloseEvent is a conversion of the following C-Struct into GO. +// struct socket_control_event_t { +// uint64_t timestamp_ns; +// struct conn_id_t conn_id; +// int64_t wr_bytes; +// int64_t rd_bytes; +//};. +type SocketCloseEvent struct { + TimestampNano uint64 + ConnID ConnID + WrittenBytes int64 + ReadBytes int64 +} diff --git a/kprobe_http1/sniffer/kprobe_http1_src.c b/kprobe_http1/sniffer/kprobe_http1_src.c new file mode 100644 index 0000000..ae6683b --- /dev/null +++ b/kprobe_http1/sniffer/kprobe_http1_src.c @@ -0,0 +1,496 @@ +// +build ignore + +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +// Defines + +#define socklen_t size_t + +// Data buffer message size. BPF can submit at most this amount of data to a perf buffer. +// Kernel size limit is 32KiB. See https://github.com/iovisor/bcc/issues/2519 for more details. +#define MAX_MSG_SIZE 30720 // 30KiB + +// This defines how many chunks a perf_submit can support. +// This applies to messages that are over MAX_MSG_SIZE, +// and effectively makes the maximum message size to be CHUNK_LIMIT*MAX_MSG_SIZE. +#define CHUNK_LIMIT 4 + +enum traffic_direction_t { + kEgress, + kIngress, +}; + +// Structs + +// A struct representing a unique ID that is composed of the pid, the file +// descriptor and the creation time of the struct. +struct conn_id_t { + // Process ID + uint32_t pid; + // The file descriptor to the opened network connection. + int32_t fd; + // Timestamp at the initialization of the struct. + uint64_t tsid; +}; + +// This struct contains information collected when a connection is established, +// via an accept4() syscall. +struct conn_info_t { + // Connection identifier. + struct conn_id_t conn_id; + + // The number of bytes written/read on this connection. + int64_t wr_bytes; + int64_t rd_bytes; + + // A flag indicating we identified the connection as HTTP. + bool is_http; +}; + +// An helper struct that hold the addr argument of the syscall. +struct accept_args_t { + struct sockaddr_in* addr; +}; + +// An helper struct to cache input argument of read/write syscalls between the +// entry hook and the exit hook. +struct data_args_t { + int32_t fd; + const char* buf; +}; + +// An helper struct that hold the input arguments of the close syscall. +struct close_args_t { + int32_t fd; +}; + +// A struct describing the event that we send to the user mode upon a new connection. +struct socket_open_event_t { + // The time of the event. + uint64_t timestamp_ns; + // A unique ID for the connection. + struct conn_id_t conn_id; + // The address of the client. + struct sockaddr_in addr; +}; + +// Struct describing the close event being sent to the user mode. +struct socket_close_event_t { + // Timestamp of the close syscall + uint64_t timestamp_ns; + // The unique ID of the connection + struct conn_id_t conn_id; + // Total number of bytes written on that connection + int64_t wr_bytes; + // Total number of bytes read on that connection + int64_t rd_bytes; +}; + +struct socket_data_event_t { + // We split attributes into a separate struct, because BPF gets upset if you do lots of + // size arithmetic. This makes it so that it's attributes followed by message. + struct attr_t { + // The timestamp when syscall completed (return probe was triggered). + uint64_t timestamp_ns; + + // Connection identifier (PID, FD, etc.). + struct conn_id_t conn_id; + + // The type of the actual data that the msg field encodes, which is used by the caller + // to determine how to interpret the data. + enum traffic_direction_t direction; + + // The size of the original message. We use this to truncate msg field to minimize the amount + // of data being transferred. + uint32_t msg_size; + + // A 0-based position number for this event on the connection, in terms of byte position. + // The position is for the first byte of this message. + uint64_t pos; + } attr; + char msg[MAX_MSG_SIZE]; +}; + +// Maps + +// A map of the active connections. The name of the map is conn_info_map +// the key is of type uint64_t, the value is of type struct conn_info_t, +// and the map won't be bigger than 128KB. +BPF_HASH(conn_info_map, uint64_t, struct conn_info_t, 131072); +// An helper map that will help us cache the input arguments of the accept syscall +// between the entry hook and the return hook. +BPF_HASH(active_accept_args_map, uint64_t, struct accept_args_t); +// Perf buffer to send to the user-mode the data events. +BPF_PERF_OUTPUT(socket_data_events); +// A perf buffer that allows us send events from kernel to user mode. +// This perf buffer is dedicated for special type of events - open events. +BPF_PERF_OUTPUT(socket_open_events); +// Perf buffer to send to the user-mode the close events. +BPF_PERF_OUTPUT(socket_close_events); +BPF_PERCPU_ARRAY(socket_data_event_buffer_heap, struct socket_data_event_t, 1); +BPF_HASH(active_write_args_map, uint64_t, struct data_args_t); +// Helper map to store read syscall arguments between entry and exit hooks. +BPF_HASH(active_read_args_map, uint64_t, struct data_args_t); +// An helper map to store close syscall arguments between entry and exit syscalls. +BPF_HASH(active_close_args_map, uint64_t, struct close_args_t); + +// Helper functions + +// Generates a unique identifier using a tgid (Thread Global ID) and a fd (File Descriptor). +static __inline uint64_t gen_tgid_fd(uint32_t tgid, int fd) { + return ((uint64_t)tgid << 32) | (uint32_t)fd; +} + +// An helper function that checks if the syscall finished successfully and if it did +// saves the new connection in a dedicated map of connections +static __inline void process_syscall_accept(struct pt_regs* ctx, uint64_t id, const struct accept_args_t* args) { + // Extracting the return code, and checking if it represent a failure, + // if it does, we abort the as we have nothing to do. + int ret_fd = PT_REGS_RC(ctx); + if (ret_fd <= 0) { + return; + } + + struct conn_info_t conn_info = {}; + uint32_t pid = id >> 32; + conn_info.conn_id.pid = pid; + conn_info.conn_id.fd = ret_fd; + conn_info.conn_id.tsid = bpf_ktime_get_ns(); + + uint64_t pid_fd = ((uint64_t)pid << 32) | (uint32_t)ret_fd; + // Saving the connection info in a global map, so in the other syscalls + // (read, write and close) we will be able to know that we have seen + // the connection + conn_info_map.update(&pid_fd, &conn_info); + + // Sending an open event to the user mode, to let the user mode know that we + // have identified a new connection. + struct socket_open_event_t open_event = {}; + open_event.timestamp_ns = bpf_ktime_get_ns(); + open_event.conn_id = conn_info.conn_id; + bpf_probe_read(&open_event.addr, sizeof(open_event.addr), args->addr); + + socket_open_events.perf_submit(ctx, &open_event, sizeof(struct socket_open_event_t)); +} + +static inline __attribute__((__always_inline__)) void process_syscall_close(struct pt_regs* ctx, uint64_t id, + const struct close_args_t* close_args) { + int ret_val = PT_REGS_RC(ctx); + if (ret_val < 0) { + return; + } + + uint32_t tgid = id >> 32; + uint64_t tgid_fd = gen_tgid_fd(tgid, close_args->fd); + struct conn_info_t* conn_info = conn_info_map.lookup(&tgid_fd); + if (conn_info == NULL) { + // The FD being closed does not represent an IPv4 socket FD. + return; + } + + // Send to the user mode an event indicating the connection was closed. + struct socket_close_event_t close_event = {}; + close_event.timestamp_ns = bpf_ktime_get_ns(); + close_event.conn_id = conn_info->conn_id; + close_event.rd_bytes = conn_info->rd_bytes; + close_event.wr_bytes = conn_info->wr_bytes; + + socket_close_events.perf_submit(ctx, &close_event, sizeof(struct socket_close_event_t)); + + // Remove the connection from the mapping. + conn_info_map.delete(&tgid_fd); +} + +static inline __attribute__((__always_inline__)) bool is_http_connection(struct conn_info_t* conn_info, const char* buf, size_t count) { + // If the connection was already identified as HTTP connection, no need to re-check it. + if (conn_info->is_http) { + return true; + } + + // The minimum length of http request or response. + if (count < 16) { + return false; + } + + bool res = false; + if (buf[0] == 'H' && buf[1] == 'T' && buf[2] == 'T' && buf[3] == 'P') { + res = true; + } + if (buf[0] == 'G' && buf[1] == 'E' && buf[2] == 'T') { + res = true; + } + if (buf[0] == 'P' && buf[1] == 'O' && buf[2] == 'S' && buf[3] == 'T') { + res = true; + } + + if (res) { + conn_info->is_http = true; + } + + return res; +} + +static __inline void perf_submit_buf(struct pt_regs* ctx, const enum traffic_direction_t direction, + const char* buf, size_t buf_size, size_t offset, + struct conn_info_t* conn_info, + struct socket_data_event_t* event) { + switch (direction) { + case kEgress: + event->attr.pos = conn_info->wr_bytes + offset; + break; + case kIngress: + event->attr.pos = conn_info->rd_bytes + offset; + break; + } + + // Note that buf_size_minus_1 will be positive due to the if-statement above. + size_t buf_size_minus_1 = buf_size - 1; + + // Clang is too smart for us, and tries to remove some of the obvious hints we are leaving for the + // BPF verifier. So we add this NOP volatile statement, so clang can't optimize away some of our + // if-statements below. + // By telling clang that buf_size_minus_1 is both an input and output to some black box assembly + // code, clang has to discard any assumptions on what values this variable can take. + asm volatile("" : "+r"(buf_size_minus_1) :); + + buf_size = buf_size_minus_1 + 1; + + // 4.14 kernels reject bpf_probe_read with size that they may think is zero. + // Without the if statement, it somehow can't reason that the bpf_probe_read is non-zero. + size_t amount_copied = 0; + if (buf_size_minus_1 < MAX_MSG_SIZE) { + bpf_probe_read(&event->msg, buf_size, buf); + amount_copied = buf_size; + } else { + bpf_probe_read(&event->msg, MAX_MSG_SIZE, buf); + amount_copied = MAX_MSG_SIZE; + } + + // If-statement is redundant, but is required to keep the 4.14 verifier happy. + if (amount_copied > 0) { + event->attr.msg_size = amount_copied; + socket_data_events.perf_submit(ctx, event, sizeof(event->attr) + amount_copied); + } +} + +static __inline void perf_submit_wrapper(struct pt_regs* ctx, + const enum traffic_direction_t direction, const char* buf, + const size_t buf_size, struct conn_info_t* conn_info, + struct socket_data_event_t* event) { + int bytes_sent = 0; + unsigned int i; +#pragma unroll + for (i = 0; i < CHUNK_LIMIT; ++i) { + const int bytes_remaining = buf_size - bytes_sent; + const size_t current_size = (bytes_remaining > MAX_MSG_SIZE && (i != CHUNK_LIMIT - 1)) ? MAX_MSG_SIZE : bytes_remaining; + perf_submit_buf(ctx, direction, buf + bytes_sent, current_size, bytes_sent, conn_info, event); + bytes_sent += current_size; + if (buf_size == bytes_sent) { + return; + } + } +} + +static inline __attribute__((__always_inline__)) void process_data(struct pt_regs* ctx, uint64_t id, + enum traffic_direction_t direction, + const struct data_args_t* args, ssize_t bytes_count) { + // Always check access to pointer before accessing them. + if (args->buf == NULL) { + return; + } + + // For read and write syscall, the return code is the number of bytes written or read, so zero means nothing + // was written or read, and negative means that the syscall failed. Anyhow, we have nothing to do with that syscall. + if (bytes_count <= 0) { + return; + } + + uint32_t pid = id >> 32; + uint64_t pid_fd = ((uint64_t)pid << 32) | (uint32_t)args->fd; + struct conn_info_t* conn_info = conn_info_map.lookup(&pid_fd); + if (conn_info == NULL) { + // The FD being read/written does not represent an IPv4 socket FD. + return; + } + + // Check if the connection is already HTTP, or check if that's a new connection, check protocol and return true if that's HTTP. + if (is_http_connection(conn_info, args->buf, bytes_count)) { + // allocate new event. + uint32_t kZero = 0; + struct socket_data_event_t* event = socket_data_event_buffer_heap.lookup(&kZero); + if (event == NULL) { + return; + } + + // Fill the metadata of the data event. + event->attr.timestamp_ns = bpf_ktime_get_ns(); + event->attr.direction = direction; + event->attr.conn_id = conn_info->conn_id; + + perf_submit_wrapper(ctx, direction, args->buf, bytes_count, conn_info, event); + } + + // Update the conn_info total written/read bytes. + switch (direction) { + case kEgress: + conn_info->wr_bytes += bytes_count; + break; + case kIngress: + conn_info->rd_bytes += bytes_count; + break; + } +} + +// Hooks +int syscall__probe_entry_accept(struct pt_regs* ctx, int sockfd, struct sockaddr* addr, socklen_t* addrlen) { + uint64_t id = bpf_get_current_pid_tgid(); + + // Keep the addr in a map to use during the exit method. + struct accept_args_t accept_args = {}; + accept_args.addr = (struct sockaddr_in *)addr; + active_accept_args_map.update(&id, &accept_args); + + return 0; +} + +int syscall__probe_ret_accept(struct pt_regs* ctx) { + uint64_t id = bpf_get_current_pid_tgid(); + + // Pulling the addr from the map. + struct accept_args_t* accept_args = active_accept_args_map.lookup(&id); + if (accept_args != NULL) { + process_syscall_accept(ctx, id, accept_args); + } + + active_accept_args_map.delete(&id); + return 0; +} + + +// Hooking the entry of accept4 +// the signature of the syscall is int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +int syscall__probe_entry_accept4(struct pt_regs* ctx, int sockfd, struct sockaddr* addr, socklen_t* addrlen) { + // Getting a unique ID for the relevant thread in the relevant pid. + // That way we can link different calls from the same thread. + uint64_t id = bpf_get_current_pid_tgid(); + + // Keep the addr in a map to use during the accpet4 exit hook. + struct accept_args_t accept_args = {}; + accept_args.addr = (struct sockaddr_in *)addr; + active_accept_args_map.update(&id, &accept_args); + + return 0; +} + +// Hooking the exit of accept4 +int syscall__probe_ret_accept4(struct pt_regs* ctx) { + uint64_t id = bpf_get_current_pid_tgid(); + + // Pulling the addr from the map. + struct accept_args_t* accept_args = active_accept_args_map.lookup(&id); + // If the id exist in the map, we will get a non empty pointer that holds + // the input address argument from the entry of the syscall. + if (accept_args != NULL) { + process_syscall_accept(ctx, id, accept_args); + } + + // Anyway, in the end clean the map. + active_accept_args_map.delete(&id); + return 0; +} + +// original signature: ssize_t write(int fd, const void *buf, size_t count); +int syscall__probe_entry_write(struct pt_regs* ctx, int fd, char* buf, size_t count) { + uint64_t id = bpf_get_current_pid_tgid(); + + struct data_args_t write_args = {}; + write_args.fd = fd; + write_args.buf = buf; + active_write_args_map.update(&id, &write_args); + + return 0; +} + +int syscall__probe_ret_write(struct pt_regs* ctx) { + uint64_t id = bpf_get_current_pid_tgid(); + ssize_t bytes_count = PT_REGS_RC(ctx); // Also stands for return code. + + // Unstash arguments, and process syscall. + struct data_args_t* write_args = active_write_args_map.lookup(&id); + if (write_args != NULL) { + process_data(ctx, id, kEgress, write_args, bytes_count); + } + + active_write_args_map.delete(&id); + return 0; +} + +// original signature: ssize_t read(int fd, void *buf, size_t count); +int syscall__probe_entry_read(struct pt_regs* ctx, int fd, char* buf, size_t count) { + uint64_t id = bpf_get_current_pid_tgid(); + + // Stash arguments. + struct data_args_t read_args = {}; + read_args.fd = fd; + read_args.buf = buf; + active_read_args_map.update(&id, &read_args); + + return 0; +} + +int syscall__probe_ret_read(struct pt_regs* ctx) { + uint64_t id = bpf_get_current_pid_tgid(); + + // The return code the syscall is the number of bytes read as well. + ssize_t bytes_count = PT_REGS_RC(ctx); + struct data_args_t* read_args = active_read_args_map.lookup(&id); + if (read_args != NULL) { + // kIngress is an enum value that let's the process_data function + // to know whether the input buffer is incoming or outgoing. + process_data(ctx, id, kIngress, read_args, bytes_count); + } + + active_read_args_map.delete(&id); + return 0; +} +// original signature: int close(int fd) +int syscall__probe_entry_close(struct pt_regs* ctx, int fd) { + uint64_t id = bpf_get_current_pid_tgid(); + struct close_args_t close_args; + close_args.fd = fd; + active_close_args_map.update(&id, &close_args); + + return 0; +} + +int syscall__probe_ret_close(struct pt_regs* ctx) { + uint64_t id = bpf_get_current_pid_tgid(); + const struct close_args_t* close_args = active_close_args_map.lookup(&id); + if (close_args != NULL) { + process_syscall_close(ctx, id, close_args); + } + + active_close_args_map.delete(&id); + return 0; +} diff --git a/kprobe_http1/sniffer/main.go b/kprobe_http1/sniffer/main.go new file mode 100644 index 0000000..a7e4256 --- /dev/null +++ b/kprobe_http1/sniffer/main.go @@ -0,0 +1,103 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package main + +import ( + "fmt" + "io/ioutil" + bpfwrapper2 "kprobe_http1_modules/module_src/bpfwrapper" + "kprobe_http1_modules/module_src/connections" + "kprobe_http1_modules/module_src/settings" + "log" + "os" + "os/signal" + "os/user" + "runtime/debug" + "syscall" + "time" + + "github.com/iovisor/gobpf/bcc" +) + +// abortIfNotRoot checks the current user permissions, if the permissions are not elevated, we abort. +func abortIfNotRoot() { + current, err := user.Current() + if err != nil { + log.Panic(err) + } + + if current.Uid != "0" { + log.Panic("sniffer must run under superuser privileges") + } +} + +// recoverFromCrashes is a defer function that caches all panics being thrown from the application. +func recoverFromCrashes() { + if err := recover(); err != nil { + log.Printf("Application crashed: %v\nstack: %s\n", err, string(debug.Stack())) + } +} + +func main() { + if len(os.Args) != 2 { + fmt.Println("Usage: go run main.go ") + os.Exit(1) + } + bpfSourceCodeFile := os.Args[1] + bpfSourceCodeContent, err := ioutil.ReadFile(bpfSourceCodeFile) + if err != nil { + log.Panic(err) + } + + defer recoverFromCrashes() + abortIfNotRoot() + + if err := settings.InitRealTimeOffset(); err != nil { + log.Printf("Failed fixing BPF clock, timings will be offseted: %v", err) + } + + // Catching all termination signals to perform a cleanup when being stopped. + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM) + + bpfModule := bcc.NewModule(string(bpfSourceCodeContent), nil) + if bpfModule == nil { + log.Panic("bpf is nil") + } + defer bpfModule.Close() + + connectionFactory := connections.NewFactory(time.Minute) + go func() { + for { + connectionFactory.HandleReadyConnections() + time.Sleep(10 * time.Second) + } + }() + if err := bpfwrapper2.LaunchPerfBufferConsumers(bpfModule, connectionFactory); err != nil { + log.Panic(err) + } + + // Lastly, after everything is ready and configured, attach the kprobes and start capturing traffic. + if err := bpfwrapper2.AttachKprobes(bpfModule); err != nil { + log.Panic(err) + } + log.Println("Sniffer is ready") + <-sig + log.Println("Signaled to terminate") +} From bea0b18f61a07b83863cd53ff5da0d1e0d7f6627 Mon Sep 17 00:00:00 2001 From: Topspin Date: Wed, 17 Apr 2024 15:59:23 +0000 Subject: [PATCH 3/5] [Style] added license (#11) --- kprobe_http1/LICENSE | 201 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 kprobe_http1/LICENSE diff --git a/kprobe_http1/LICENSE b/kprobe_http1/LICENSE new file mode 100644 index 0000000..75ea20e --- /dev/null +++ b/kprobe_http1/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018- The Pixie Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file From 5de4d0d45cdeafb0dbf806d4f0cf926e29e81b60 Mon Sep 17 00:00:00 2001 From: Topspin Date: Mon, 22 Apr 2024 00:59:29 +0000 Subject: [PATCH 4/5] [Dependencies] Added dependencies for ebpf http observability (#11) --- ebpf_http_dependencies/README.md | 3 +++ ebpf_http_dependencies/bcc_install.sh | 14 ++++++++++++++ ebpf_http_dependencies/general_install.sh | 10 ++++++++++ ebpf_http_dependencies/go_install.sh | 3 +++ 4 files changed, 30 insertions(+) create mode 100644 ebpf_http_dependencies/README.md create mode 100644 ebpf_http_dependencies/bcc_install.sh create mode 100644 ebpf_http_dependencies/general_install.sh create mode 100644 ebpf_http_dependencies/go_install.sh diff --git a/ebpf_http_dependencies/README.md b/ebpf_http_dependencies/README.md new file mode 100644 index 0000000..9384b06 --- /dev/null +++ b/ebpf_http_dependencies/README.md @@ -0,0 +1,3 @@ +- general_install.sh: script for installing packages for general ebpf support +- bcc_install.sh: script for installing v0.24.0 of bcc +- go_install.sh: script for installing Golang \ No newline at end of file diff --git a/ebpf_http_dependencies/bcc_install.sh b/ebpf_http_dependencies/bcc_install.sh new file mode 100644 index 0000000..8c536e9 --- /dev/null +++ b/ebpf_http_dependencies/bcc_install.sh @@ -0,0 +1,14 @@ +sudo apt install -y zip bison build-essential cmake flex git libedit-dev libllvm14 llvm-14-dev libclang-14-dev python3 zlib1g-dev libelf-dev libfl-dev python3-setuptools liblzma-dev libdebuginfod-dev arping netperf iperf + +git clone --branch v0.24.0 https://github.com/iovisor/bcc.git +mkdir bcc/build; cd bcc/build +cmake .. +make +sudo make install +# cmake -DPYTHON_CMD=python3 .. # build python3 binding +cmake -DPYTHON_CMD=python3 .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DENABLE_EXAMPLES=0 -DENABLE_TESTS=0 -DENABLE_MAN=0 -DENABLE_LLVM_SHARED=1 +pushd src/python/ +make +sudo make install +popd \ No newline at end of file diff --git a/ebpf_http_dependencies/general_install.sh b/ebpf_http_dependencies/general_install.sh new file mode 100644 index 0000000..f4449ac --- /dev/null +++ b/ebpf_http_dependencies/general_install.sh @@ -0,0 +1,10 @@ +sudo apt install linux-headers-$(uname -r) \ + libbpfcc-dev \ + libbpf-dev \ + llvm \ + clang \ + gcc-multilib \ + build-essential \ + linux-tools-$(uname -r) \ + linux-tools-common \ + linux-tools-generic \ No newline at end of file diff --git a/ebpf_http_dependencies/go_install.sh b/ebpf_http_dependencies/go_install.sh new file mode 100644 index 0000000..16193a5 --- /dev/null +++ b/ebpf_http_dependencies/go_install.sh @@ -0,0 +1,3 @@ +sudo add-apt-repository ppa:longsleep/golang-backports +sudo apt update +sudo apt install golang-go \ No newline at end of file From eb9834e2d60c2434d4044250505d7570cc2d3527 Mon Sep 17 00:00:00 2001 From: Topspin Date: Mon, 22 Apr 2024 01:37:56 +0000 Subject: [PATCH 5/5] [Documentation] Updated README file to a more detailed version (#11) --- kprobe_http1/README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/kprobe_http1/README.md b/kprobe_http1/README.md index 067cbee..09f2e54 100644 --- a/kprobe_http1/README.md +++ b/kprobe_http1/README.md @@ -1,4 +1,27 @@ -## Run sniffer +# Kprobe Sniffer for HTTP/1.x Observability + +## Overview + +This directory contains the source code for an HTTP/1.x packet sniffer leveraging eBPF programs attached to kprobes in the kernel. This allows for non-intrusive observability for HTTP/1.x protocols while also offering high performance under minimal maintainence effort. + +We run a user-space agent that attaches eBPF programs to syscalls that are integral for HTTP/1.x communication, handles the transfer of data between user-space and kernel-space, and outputs the captured data to the terminal. + +## Components + +- **module_src**: Contains Golang implementations of attaching kprobes, structs for communication between user-space and kernel-space, read/write buffers etc. +- **sniffer**: Contains source code for the user-space agent and eBPF programs. + +## Dependencies +Please refer to the `ebpf_http_dependencies` directory. + +## Usage +Run the sniffer using the following command ```bash sudo go run ./sniffer/main.go ./sniffer/kprobe_http1_src.c ``` +Ensure that you have the necessary packages installed and the correct version of bcc (v0.24.0) compiled. Exit the sniffer using Ctrl-C. + +## Note +At the current stage, this sniffer only supports the observation of HTTP/1.x through kprobes. This will be upgraded later to support uprobes as well as observability for HTTP/2.0 and HTTPS protocols. + +We also plan to use Grafana to provide a better interface for users, replacing the current mode of outputting to the terminal.