Skip to content

Commit

Permalink
Add HTTP2 protocol identify (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrproliu authored Sep 7, 2022
1 parent b034d7d commit 404e93f
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/rover.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ jobs:
config: test/e2e/cases/profiling/network/c_plus_plus/e2e.yaml
- name: Nodejs Profiling
config: test/e2e/cases/profiling/network/nodejs/e2e.yaml
- name: HTTP2 Profiling
config: test/e2e/cases/profiling/network/http2/e2e.yaml
steps:
- uses: actions/checkout@v2
with:
Expand Down
81 changes: 81 additions & 0 deletions bpf/profiling/network/protocol_analyze.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,85 @@ static __inline __u32 infer_http_message(const char* buf, size_t count) {
return kUnknown;
}

// frame format: https://www.rfc-editor.org/rfc/rfc7540.html#section-4.1
static __inline __u32 infer_http2_message(const char* buf_src, size_t count) {
static const uint8_t kFrameBasicSize = 0x9; // including Length, Type, Flags, Reserved, Stream Identity
static const uint8_t kFrameTypeHeader = 0x1; // the type of the frame: https://www.rfc-editor.org/rfc/rfc7540.html#section-6.2
static const uint8_t kFrameLoopCount = 5;

static const uint8_t kStaticTableMaxSize = 61;// https://www.rfc-editor.org/rfc/rfc7541#appendix-A
static const uint8_t kStaticTableAuth = 1;
static const uint8_t kStaticTableGet = 2;
static const uint8_t kStaticTablePost = 3;
static const uint8_t kStaticTablePath1 = 4;
static const uint8_t kStaticTablePath2 = 5;

// the buffer size must bigger than basic frame size
if (count < kFrameBasicSize) {
return kUnknown;
}

// frame info
__u8 frame[21] = { 0 };
__u32 frameOffset = 0;
// header info
__u8 staticInx, headerBlockFragmentOffset;

// each all frame
#pragma unroll
for (__u8 i = 0; i < kFrameLoopCount; i++) {
if (frameOffset >= count) {
break;
}

// read frame
bpf_probe_read(frame, sizeof(frame), buf_src + frameOffset);
frameOffset += (bpf_ntohl(*(__u32 *) frame) >> 8) + kFrameBasicSize;

// is header frame
if (frame[3] != kFrameTypeHeader) {
continue;
}

// validate the header(unset): not HTTP2 protocol
// this frame must is a send request
if ((frame[4] & 0xd2) || frame[5] & 0x01) {
return kUnknown;
}

// locate the header block fragment offset
headerBlockFragmentOffset = kFrameBasicSize;
if (frame[4] & 0x20) { // PADDED flag is set
headerBlockFragmentOffset += 1;
}
if (frame[4] & 0x20) { // PRIORITY flag is set
headerBlockFragmentOffset += 5;
}

#pragma unroll
for (__u8 j = 0; j <= kStaticTablePath2; j++) {
if (headerBlockFragmentOffset > count) {
return kUnknown;
}
staticInx = frame[headerBlockFragmentOffset] & 0x7f;
if (staticInx <= kStaticTableMaxSize && staticInx > 0) {
if (staticInx == kStaticTableAuth ||
staticInx == kStaticTableGet ||
staticInx == kStaticTablePost ||
staticInx == kStaticTablePath1 ||
staticInx == kStaticTablePath2) {
return kRequest;
} else {
return kResponse;
}
}
headerBlockFragmentOffset++;
}
}

return kUnknown;
}

// Cassandra frame:
// 0 8 16 24 32 40
// +---------+---------+---------+---------+---------+
Expand Down Expand Up @@ -678,6 +757,8 @@ static __inline enum message_type_t analyze_protocol(char *buf, __u32 count, str
// PROTOCOL_LIST: Requires update on new protocols.
if ((inferred_message.type = infer_http_message(buf, count)) != kUnknown) {
inferred_message.protocol = kProtocolHTTP;
} else if ((inferred_message.type = infer_http2_message(buf, count)) != kUnknown) {
inferred_message.protocol = kProtocolHTTP2;
} else if ((inferred_message.type = infer_cql_message(buf, count)) != kUnknown) {
inferred_message.protocol = kProtocolCQL;
} else if ((inferred_message.type = infer_mongo_message(buf, count)) != kUnknown) {
Expand Down
13 changes: 7 additions & 6 deletions docs/en/setup/configuration/profiling.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ Off CPU Profiling task is attach the `finish_task_switch` in `krobe` to profilin
Network Profiling task is intercept IO-related syscall and `urprobe` in process to identify the network traffic and generate the metrics.
Also, the following protocol are supported for analyzing using OpenSSL library, BoringSSL library, GoTLS, NodeTLS or plaintext:

1. HTTP
2. MySQL
3. CQL(The Cassandra Query Language)
4. MongoDB
5. Kafka
6. DNS
1. HTTP/1.x
2. HTTP/2
3. MySQL
4. CQL(The Cassandra Query Language)
5. MongoDB
6. Kafka
7. DNS

#### Metrics

Expand Down
14 changes: 14 additions & 0 deletions test/e2e/cases/profiling/network/base/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,18 @@ http {
proxy_http_version 1.1;
}
}

server {
listen 9000 ssl http2;
server_name proxy;
include mime.types;
default_type application/octet-stream;

ssl_certificate /ssl_data/proxy.crt;
ssl_certificate_key /ssl_data/proxy.key;

location / {
grpc_pass grpcs://service:9000;
}
}
}
38 changes: 38 additions & 0 deletions test/e2e/cases/profiling/network/http2/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.

FROM golang:1.17

COPY http2/ /service

WORKDIR /
RUN apt update && apt install -y zip && \
mkdir protoc && cd protoc && \
curl -sL https://github.com/protocolbuffers/protobuf/releases/download/v21.5/protoc-21.5-linux-x86_64.zip -o protoc.zip && \
unzip protoc.zip

WORKDIR /service

RUN go get -u google.golang.org/protobuf/cmd/[email protected] && \
go get -u google.golang.org/grpc/cmd/[email protected] && \
go get google.golang.org/grpc/internal/[email protected] && \
/protoc/bin/protoc --go_out=. --go-grpc_out=. service.proto && \
go build .

COPY base/ssl /usr/local/share/ca-certificates
RUN update-ca-certificates

CMD ["/service/test"]
64 changes: 64 additions & 0 deletions test/e2e/cases/profiling/network/http2/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.

version: '2.1'

services:
service:
build:
context: ../
dockerfile: http2/Dockerfile
volumes:
- ./../base/ssl:/ssl_data
networks:
- e2e
ports:
- 8080:8080
healthcheck:
test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/8080" ]
interval: 5s
timeout: 60s
retries: 120

proxy:
extends:
file: ../base/docker-compose.yml
service: proxy
networks:
- e2e
depends_on:
service:
condition: service_healthy

oap:
extends:
file: ../base/docker-compose.yml
service: oap
ports:
- 12800:12800

rover:
extends:
file: ../base/docker-compose.yml
service: rover
environment:
ROVER_PROCESS_DISCOVERY_REGEX_SCANNER_MATCH_CMD2: service/test
ROVER_PROCESS_DISCOVERY_REGEX_SCANNER_PROCESS_NAME2: service
depends_on:
oap:
condition: service_healthy

networks:
e2e:
45 changes: 45 additions & 0 deletions test/e2e/cases/profiling/network/http2/e2e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.

setup:
env: compose
file: docker-compose.yml
timeout: 20m
init-system-environment: ../../../../base/env
steps:
- name: set PATH
command: export PATH=/tmp/skywalking-infra-e2e/bin:$PATH
- name: install yq
command: bash test/e2e/base/scripts/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e/base/scripts/prepare/setup-e2e-shell/install.sh swctl

trigger:
action: http
interval: 3s
times: 10
url: http://${service_host}:${service_8080}/singleCall
method: GET

verify:
# verify with retry strategy
retry:
# max retry count
count: 20
# the interval between two retries, in millisecond.
interval: 10s
cases:
- includes:
- ../network-cases.yaml
33 changes: 33 additions & 0 deletions test/e2e/cases/profiling/network/http2/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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.

module test

go 1.17

require (
google.golang.org/grpc v1.44.0
google.golang.org/protobuf v1.25.0
)

require (
github.com/golang/protobuf v1.4.3 // indirect
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
golang.org/x/text v0.3.0 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
)
Loading

0 comments on commit 404e93f

Please sign in to comment.