Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ proto_library(
"//envoy/config/resource_monitor/fixed_heap/v2alpha:fixed_heap",
"//envoy/config/resource_monitor/injected_resource/v2alpha:injected_resource",
"//envoy/config/trace/v2:trace",
"//envoy/config/transport_socket/capture/v2alpha:capture",
"//envoy/config/transport_socket/tap/v2alpha:tap",
"//envoy/data/accesslog/v2:accesslog",
"//envoy/data/core/v2alpha:health_check_event",
"//envoy/data/tap/v2alpha:capture",
"//envoy/data/tap/v2alpha:http",
"//envoy/data/tap/v2alpha:transport",
"//envoy/data/tap/v2alpha:wrapper",
"//envoy/service/accesslog/v2:als",
"//envoy/service/auth/v2:attribute_context",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ load("//bazel:api_build_system.bzl", "api_proto_library_internal")
licenses(["notice"]) # Apache 2

api_proto_library_internal(
name = "capture",
srcs = ["capture.proto"],
name = "tap",
srcs = ["tap.proto"],
deps = [
"//envoy/api/v2/core:base",
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
syntax = "proto3";

package envoy.config.transport_socket.capture.v2alpha;
option java_package = "io.envoyproxy.envoy.config.transport_socket.capture.v2alpha";
package envoy.config.transport_socket.tap.v2alpha;
option java_package = "io.envoyproxy.envoy.config.transport_socket.tap.v2alpha";
option go_package = "v2";

// [#protodoc-title: Capture]
// [#protodoc-title: Tap]

import "envoy/api/v2/core/base.proto";

Expand Down Expand Up @@ -33,9 +33,9 @@ message FileSink {
Format format = 2;
}

// Configuration for capture transport socket. This wraps another transport socket, providing the
// Configuration for tap transport socket. This wraps another transport socket, providing the
// ability to interpose and record in plain text any traffic that is surfaced to Envoy.
message Capture {
message Tap {
oneof sink_selector {
// Trace is to be written to a file sink.
FileSink file_sink = 1;
Expand Down
4 changes: 2 additions & 2 deletions api/envoy/data/tap/v2alpha/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ load("//bazel:api_build_system.bzl", "api_proto_library_internal")
licenses(["notice"]) # Apache 2

api_proto_library_internal(
name = "capture",
srcs = ["capture.proto"],
name = "transport",
srcs = ["transport.proto"],
deps = ["//envoy/api/v2/core:address"],
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
syntax = "proto3";

// [#protodoc-title: Common tap]
// Trace capture format for the capture transport socket extension. This dumps plain text read/write
// [#protodoc-title: Transport tap data]
// Trace format for the tap transport socket extension. This dumps plain text read/write
// sequences on a socket.

package envoy.data.tap.v2alpha;
Expand All @@ -23,7 +23,7 @@ message Connection {
envoy.api.v2.core.Address remote_address = 3;
}

// Event in a capture trace.
// Event in a trace.
message Event {
// Timestamp for event.
google.protobuf.Timestamp timestamp = 1;
Expand All @@ -47,7 +47,7 @@ message Event {
}
}

// Sequence of read/write events that constitute a captured trace on a socket.
// Sequence of read/write events that constitute a trace on a socket.
// Multiple Trace messages might be emitted for a given connection ID, with the
// sink (e.g. file set, network) responsible for later reassembly.
message Trace {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're probably going to want to move from a "entire connection trace" to a chunked trace for streaming in a later PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'm going to be working on the streaming stuff in later PRs for sure.

Expand Down
16 changes: 8 additions & 8 deletions api/tools/BUILD
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
licenses(["notice"]) # Apache 2

py_binary(
name = "capture2pcap",
srcs = ["capture2pcap.py"],
name = "tap2pcap",
srcs = ["tap2pcap.py"],
licenses = ["notice"], # Apache 2
visibility = ["//visibility:public"],
deps = [
"//envoy/data/tap/v2alpha:capture_py",
"//envoy/data/tap/v2alpha:transport_py",
],
)

py_test(
name = "capture2pcap_test",
srcs = ["capture2pcap_test.py"],
name = "tap2pcap_test",
srcs = ["tap2pcap_test.py"],
data = [
"data/capture2pcap_h2_ipv4.pb_text",
"data/capture2pcap_h2_ipv4.txt",
"data/tap2pcap_h2_ipv4.pb_text",
"data/tap2pcap_h2_ipv4.txt",
],
# Don't run this by default, since we don't want to force local dependency on Wireshark/tshark,
# will explicitly invoke in CI.
tags = ["manual"],
visibility = ["//visibility:public"],
deps = [":capture2pcap"],
deps = [":tap2pcap"],
)

py_binary(
Expand Down
24 changes: 12 additions & 12 deletions api/tools/capture2pcap.py → api/tools/tap2pcap.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""Tool to convert Envoy capture trace format to PCAP.
"""Tool to convert Envoy tap trace format to PCAP.

Uses od and text2pcap (part of Wireshark) utilities to translate the Envoy
capture trace proto format to a PCAP file suitable for consuming in Wireshark
tap trace proto format to a PCAP file suitable for consuming in Wireshark
and other tools in the PCAP ecosystem. The TCP stream in the output PCAP is
synthesized based on the known IP/port/timestamps that Envoy produces in its
capture files; it is not a literal wire capture.
tap files; it is not a literal wire tap.

Usage:

bazel run @envoy_api//tools:capture2pcap <capture .pb/.pb_text> <pcap path>
bazel run @envoy_api//tools:tap2pcap <tap .pb/.pb_text> <pcap path>

Known issues:
- IPv6 PCAP generation has malformed TCP packets. This appears to be a text2pcap
Expand All @@ -28,7 +28,7 @@

from google.protobuf import text_format

from envoy.data.tap.v2alpha import capture_pb2
from envoy.data.tap.v2alpha import transport_pb2


def DumpEvent(direction, timestamp, data):
Expand All @@ -43,13 +43,13 @@ def DumpEvent(direction, timestamp, data):
return dump.getvalue()


def Capture2Pcap(capture_path, pcap_path):
trace = capture_pb2.Trace()
if capture_path.endswith('.pb_text'):
with open(capture_path, 'r') as f:
def Tap2Pcap(tap_path, pcap_path):
trace = transport_pb2.Trace()
if tap_path.endswith('.pb_text'):
with open(tap_path, 'r') as f:
text_format.Merge(f.read(), trace)
else:
with open(capture_path, 'r') as f:
with open(tap_path, 'r') as f:
trace.ParseFromString(f.read())

local_address = trace.connection.local_address.socket_address.address
Expand Down Expand Up @@ -82,6 +82,6 @@ def Capture2Pcap(capture_path, pcap_path):

if __name__ == '__main__':
if len(sys.argv) != 3:
print('Usage: %s <capture .pb/.pb_text> <pcap path>' % sys.argv[0])
print('Usage: %s <tap .pb/.pb_text> <pcap path>' % sys.argv[0])
sys.exit(1)
Capture2Pcap(sys.argv[1], sys.argv[2])
Tap2Pcap(sys.argv[1], sys.argv[2])
14 changes: 7 additions & 7 deletions api/tools/capture2pcap_test.py → api/tools/tap2pcap_test.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
"""Tests for capture2pcap."""
"""Tests for tap2pcap."""
from __future__ import print_function

import os
import subprocess as sp
import sys

import capture2pcap
import tap2pcap

# Validate that the captured trace when run through capture2cap | tshark matches
# a golden output file for the tshark dump. Since we run capture2pcap in a
# Validate that the tapped trace when run through tap2cap | tshark matches
# a golden output file for the tshark dump. Since we run tap2pcap in a
# subshell with a limited environment, the inferred time zone should be UTC.
if __name__ == '__main__':
srcdir = os.path.join(os.getenv('TEST_SRCDIR'), 'envoy_api')
capture_path = os.path.join(srcdir, 'tools/data/capture2pcap_h2_ipv4.pb_text')
expected_path = os.path.join(srcdir, 'tools/data/capture2pcap_h2_ipv4.txt')
tap_path = os.path.join(srcdir, 'tools/data/tap2pcap_h2_ipv4.pb_text')
expected_path = os.path.join(srcdir, 'tools/data/tap2pcap_h2_ipv4.txt')
pcap_path = os.path.join(os.getenv('TEST_TMPDIR'), 'generated.pcap')

capture2pcap.Capture2Pcap(capture_path, pcap_path)
tap2pcap.Tap2Pcap(tap_path, pcap_path)
actual_output = sp.check_output(['tshark', '-r', pcap_path, '-d', 'tcp.port==10000,http2', '-P'])
with open(expected_path, 'r') as f:
expected_output = f.read()
Expand Down
16 changes: 8 additions & 8 deletions ci/do_ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,19 @@ elif [[ "$1" == "bazel.asan" ]]; then
echo "Building and testing..."
bazel_with_collection test ${BAZEL_TEST_OPTIONS} -c dbg --config=clang-asan @envoy//test/... \
//:echo2_integration_test //:envoy_binary_test
# Also validate that integration test traffic capture (useful when debugging etc.)
# works. This requires that we set CAPTURE_ENV. We do this under bazel.asan to
# Also validate that integration test traffic tapping (useful when debugging etc.)
# works. This requires that we set TAP_PATH. We do this under bazel.asan to
# ensure a debug build in CI.
CAPTURE_TMP=/tmp/capture/
rm -rf "${CAPTURE_TMP}"
mkdir -p "${CAPTURE_TMP}"
TAP_TMP=/tmp/tap/
rm -rf "${TAP_TMP}"
mkdir -p "${TAP_TMP}"
bazel_with_collection test ${BAZEL_TEST_OPTIONS} -c dbg --config=clang-asan \
@envoy//test/integration:ssl_integration_test \
--test_env=CAPTURE_PATH="${CAPTURE_TMP}/capture"
--test_env=TAP_PATH="${TAP_TMP}/tap"
# Verify that some pb_text files have been created. We can't check for pcap,
# since tcpdump is not available in general due to CircleCI lack of support
# for privileged Docker executors.
ls -l "${CAPTURE_TMP}"/*.pb_text > /dev/null
ls -l "${TAP_TMP}"/tap_*.pb_text > /dev/null
exit 0
elif [[ "$1" == "bazel.tsan" ]]; then
setup_clang_toolchain
Expand Down Expand Up @@ -201,7 +201,7 @@ elif [[ "$1" == "bazel.api" ]]; then
bazel build ${BAZEL_BUILD_OPTIONS} -c fastbuild @envoy_api//envoy/...
echo "Testing API..."
bazel_with_collection test ${BAZEL_TEST_OPTIONS} -c fastbuild @envoy_api//test/... @envoy_api//tools/... \
@envoy_api//tools:capture2pcap_test
@envoy_api//tools:tap2pcap_test
exit 0
elif [[ "$1" == "bazel.coverage" ]]; then
setup_gcc_toolchain
Expand Down
4 changes: 2 additions & 2 deletions docs/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ PROTO_RST="
/envoy/config/rbac/v2alpha/rbac/envoy/config/rbac/v2alpha/rbac.proto.rst
/envoy/config/resource_monitor/fixed_heap/v2alpha/fixed_heap/envoy/config/resource_monitor/fixed_heap/v2alpha/fixed_heap.proto.rst
/envoy/config/resource_monitor/injected_resource/v2alpha/injected_resource/envoy/config/resource_monitor/injected_resource/v2alpha/injected_resource.proto.rst
/envoy/config/transport_socket/capture/v2alpha/capture/envoy/config/transport_socket/capture/v2alpha/capture.proto.rst
/envoy/config/transport_socket/tap/v2alpha/tap/envoy/config/transport_socket/tap/v2alpha/tap.proto.rst
/envoy/data/accesslog/v2/accesslog/envoy/data/accesslog/v2/accesslog.proto.rst
/envoy/data/core/v2alpha/health_check_event/envoy/data/core/v2alpha/health_check_event.proto.rst
/envoy/data/tap/v2alpha/capture/envoy/data/tap/v2alpha/capture.proto.rst
/envoy/data/tap/v2alpha/transport/envoy/data/tap/v2alpha/transport.proto.rst
/envoy/data/tap/v2alpha/http/envoy/data/tap/v2alpha/http.proto.rst
/envoy/data/tap/v2alpha/wrapper/envoy/data/tap/v2alpha/wrapper.proto.rst
/envoy/service/accesslog/v2/als/envoy/service/accesslog/v2/als.proto.rst
Expand Down
2 changes: 1 addition & 1 deletion docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ Version history
header generation.
* router: added 'unavailable' to the retriable gRPC status codes that can be specified
through :ref:`x-envoy-retry-grpc-on <config_http_filters_router_x-envoy-retry-grpc-on>`.
* sockets: added :ref:`capture transport socket extension <operations_traffic_capture>` to support
* sockets: added :ref:`tap transport socket extension <operations_traffic_tapping>` to support
recording plain text traffic and PCAP generation.
* sockets: added `IP_FREEBIND` socket option support for :ref:`listeners
<envoy_api_field_Listener.freebind>` and upstream connections via
Expand Down
2 changes: 1 addition & 1 deletion docs/root/operations/operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Operations and administration
stats_overview
runtime
fs_flags
traffic_capture
traffic_tapping
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
.. _operations_traffic_capture:
.. _operations_traffic_tapping:

Traffic capture
Traffic tapping
===============

Envoy currently provides an experimental :ref:`transport socket extension
<envoy_api_msg_core.TransportSocket>` that can intercept traffic and write to a :ref:`protobuf
capture file <envoy_api_msg_data.tap.v2alpha.Trace>`.
Envoy currently provides two experimental extensions that can tap traffic:

* :ref:`HTTP tap filter <config_http_filters_tap>`. See the linked filter documentation for more
information.
* :ref:`Tap transport socket extension <envoy_api_msg_core.TransportSocket>` that can intercept
traffic and write to a :ref:`protobuf trace file <envoy_api_msg_data.tap.v2alpha.Trace>`. The
remainder of this document describes the configuration of the tap transport socket.

Tap transport socket configuration
----------------------------------

.. warning::
This feature is experimental and has a known limitation that it will OOM for large traces on a
given socket. It can also be disabled in the build if there are security concerns, see
https://github.com/envoyproxy/envoy/blob/master/bazel/README.md#disabling-extensions.

Configuration
-------------

Capture can be configured on :ref:`Listener
Tapping can be configured on :ref:`Listener
<envoy_api_field_listener.FilterChain.transport_socket>` and :ref:`Cluster
<envoy_api_field_Cluster.transport_socket>` transport sockets, providing the ability to interpose on
downstream and upstream L4 connections respectively.

To configure traffic capture, add an `envoy.transport_sockets.capture` transport socket
:ref:`configuration <envoy_api_msg_config.transport_socket.capture.v2alpha.Capture>` to the listener
To configure traffic tapping, add an `envoy.transport_sockets.tap` transport socket
:ref:`configuration <envoy_api_msg_config.transport_socket.tap.v2alpha.Tap>` to the listener
or cluster. For a plain text socket this might look like:

.. code-block:: yaml

transport_socket:
name: envoy.transport_sockets.capture
name: envoy.transport_sockets.tap
config:
file_sink:
path_prefix: /some/capture/path
path_prefix: /some/tap/path
transport_socket:
name: raw_buffer

Expand All @@ -39,10 +43,10 @@ For a TLS socket, this will be:
.. code-block:: yaml

transport_socket:
name: envoy.transport_sockets.capture
name: envoy.transport_sockets.tap
config:
file_sink:
path_prefix: /some/capture/path
path_prefix: /some/tap/path
transport_socket:
name: ssl
config: <TLS context>
Expand All @@ -53,19 +57,19 @@ where the TLS context configuration replaces any existing :ref:`downstream
TLS configuration on the listener or cluster, respectively.

Each unique socket instance will generate a trace file prefixed with `path_prefix`. E.g.
`/some/capture/path_0.pb`.
`/some/tap/path_0.pb`.

PCAP generation
---------------

The generated trace file can be converted to `libpcap format
<https://wiki.wireshark.org/Development/LibpcapFileFormat>`_, suitable for
analysis with tools such as `Wireshark <https://www.wireshark.org/>`_ with the
`capture2pcap` utility, e.g.:
`tap2pcap` utility, e.g.:

.. code-block:: bash

bazel run @envoy_api//tools:capture2pcap /some/capture/path_0.pb path_0.pcap
bazel run @envoy_api//tools:tap2pcap /some/tap/path_0.pb path_0.pcap
tshark -r path_0.pcap -d "tcp.port==10000,http2" -P
1 0.000000 127.0.0.1 → 127.0.0.1 HTTP2 157 Magic, SETTINGS, WINDOW_UPDATE, HEADERS
2 0.013713 127.0.0.1 → 127.0.0.1 HTTP2 91 SETTINGS, SETTINGS, WINDOW_UPDATE
Expand Down
7 changes: 6 additions & 1 deletion source/extensions/common/tap/tap_matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ using MatcherPtr = std::unique_ptr<Matcher>;
* - A per-stream/request matching status must be kept in order to compute interim match status.
* - In order to make this computationally efficient, the matching tree is kept in a vector, with
* all references to other matchers implemented using an index into the vector. The vector is
* effectively a flattened N-ary tree.
* effectively a preorder traversal flattened N-ary tree.
* - The previous point allows the creation of a per-stream/request vector of booleans of the same
* size as the matcher vector. Then, when match status is updated given new information, the
* vector of booleans can be easily updated using the same indexes as in the constant match
Expand All @@ -46,6 +46,11 @@ class Matcher {
* @param response_headers supplies the response headers, if available.
* @param statuses supplies the per-stream-request match status vector which must be the same
* size as the match tree vector (see above).
*
* TODO(mattklein123): Currently, this performs a recursive updateMatchStatus() call on any
* child match nodes. It's possible that we can short circuit this in certain cases but this
* needs more thinking (e.g., if an OR matcher already has one match and it's not possible for
* a matcher to flip from true to false).
*/
virtual bool updateMatchStatus(const Http::HeaderMap* request_headers,
const Http::HeaderMap* response_headers,
Expand Down
Loading