remove remaining EBPF-based selective tracing, replacing it with picotls's sampling-based logging; sni-based is turned off for now

This commit is contained in:
Kazuho Oku 2024-11-28 14:40:28 +09:00
parent 0d5a0fb2ee
commit 8827f8d38c
17 changed files with 47 additions and 547 deletions

View File

@ -29,19 +29,6 @@
*/
provider h2o {
/**
* When a new connection is accepted, h2o invokes this probe to obtain the flags to be associated to the new connection. The
* probe MUST write the result into the h2o_return map; see ebpf.h for details. `original_flags` contain the flags that will be
* associated when the probe is not attached.
*
* Do not use it for a tracing event.
*/
probe _private_socket_lookup_flags(pid_t tid, uint64_t original_flags, struct st_h2o_ebpf_map_key_t *info);
/**
* Same as `_private_socket_lookup_flags`, expect that this probe is invoked when SNI is being obtained.
*/
probe _private_socket_lookup_flags_sni(pid_t tid, uint64_t original_flags, const char *server_name, size_t server_name_len);
/**
* socket write at H2O socket abstraction layer
*/

View File

@ -534,7 +534,6 @@
E9F677D520074770006476D3 /* roundrobin.c in Sources */ = {isa = PBXBuildFile; fileRef = D08137391FD400F4004679DF /* roundrobin.c */; };
E9F677D6200775FF006476D3 /* least_conn.c in Sources */ = {isa = PBXBuildFile; fileRef = D081373D1FD40431004679DF /* least_conn.c */; };
E9F677D720077603006476D3 /* roundrobin.c in Sources */ = {isa = PBXBuildFile; fileRef = D081373E1FD40431004679DF /* roundrobin.c */; };
E9FB8DC423991D08007BFC59 /* ebpf.h in Headers */ = {isa = PBXBuildFile; fileRef = E908A2A923163CB70039BCEE /* ebpf.h */; };
E9FF84BC24C5AA47002577CA /* quicly-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = E9FF84BB24C5AA46002577CA /* quicly-probes.d */; };
E9FF84BD24C5AA47002577CA /* quicly-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = E9FF84BB24C5AA46002577CA /* quicly-probes.d */; };
E9FF84BE24C5AA52002577CA /* quicly-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = E9FF84BB24C5AA46002577CA /* quicly-probes.d */; };
@ -1118,7 +1117,6 @@
E901C439213E59A000D17C93 /* qpack.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = qpack.c; sourceTree = "<group>"; };
E901C43C213FAE0300D17C93 /* qpack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = qpack.h; sourceTree = "<group>"; };
E901C43F2140CCB500D17C93 /* qpack.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = qpack.c; sourceTree = "<group>"; };
E908A2A923163CB70039BCEE /* ebpf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ebpf.h; sourceTree = "<group>"; };
E908A2AB231CF3D10039BCEE /* 50reverse-proxy-chunked-post-termination.t */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "50reverse-proxy-chunked-post-termination.t"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.perl; };
E908A2AE2320AE4C0039BCEE /* check.mk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = check.mk; sourceTree = "<group>"; };
E90A95F21E30795100483D6C /* headers_util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = headers_util.c; sourceTree = "<group>"; };
@ -1993,7 +1991,6 @@
10DA969B1CD2BF9000679165 /* cache.h */,
1024A3FD1D22546800EB13F1 /* cache_digests.h */,
105534D71A3C785000627ECB /* configurator.h */,
E908A2A923163CB70039BCEE /* ebpf.h */,
107D4D521B5B2412004A9B21 /* file.h */,
1044812D1BFD0FBE0007863F /* filecache.h */,
7D2DF4ED20297EE0004AD361 /* header.h */,
@ -2961,7 +2958,6 @@
107923A519A3215F00C52AD6 /* http1.h in Headers */,
E901C3E6213E1C3600D17C93 /* ranges.h in Headers */,
E98884CA21E7F3AF0060F010 /* sentmap.h in Headers */,
E9FB8DC423991D08007BFC59 /* ebpf.h in Headers */,
10A3D3D31B4CDF1200327CF9 /* memcached.h in Headers */,
E901C3ED213E1C3600D17C93 /* constants.h in Headers */,
08790DE01D8015A400A04BC1 /* sds.h in Headers */,

View File

@ -375,11 +375,6 @@ struct st_h2o_globalconf_t {
* setuid user (or NULL)
*/
char *user;
/**
* sets up the h2o_return map if true.
*/
int usdt_selective_tracing;
/**
* SSL handshake timeout
*/
@ -935,9 +930,9 @@ typedef struct st_h2o_conn_callbacks_t {
*/
ptls_t *(*get_ptls)(h2o_conn_t *conn);
/**
* returns if the connection is target of tracing
* returns a random number between 0 and 1, unique to the connection (see ptls_log for how it is being used)
*/
int (*skip_tracing)(h2o_conn_t *conn);
float (*log_random)(h2o_conn_t *conn);
/**
* optional (i.e. may be NULL) callback for server push
*/

View File

@ -1,75 +0,0 @@
/*
* Copyright (c) 2019 Fastly Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef h2o__ebpf_h
#define h2o__ebpf_h
/*
* This file may be included by a BPF program. A BPF program written in C can include a few system headers, for example
* linux/sched.h and inttypes.h. However, most of the standard C/POSIX headers, for example stdio.h and socket.h, are unavailable.
* ebpf.h MUST only depend on the include files available to the BPF compiler.
*/
typedef struct st_h2o_ebpf_address_t {
uint8_t ip[16];
uint16_t port;
} h2o_ebpf_address_t;
typedef struct st_h2o_ebpf_map_key_t {
uint8_t family;
uint8_t protocol;
h2o_ebpf_address_t local, remote;
} h2o_ebpf_map_key_t;
/**
* `H2O_EBPF_FLAGS_*` are the value type of the pinned BPF maps: h2o_map and h2o_return
*/
#define H2O_EBPF_FLAGS_SKIP_TRACING_BIT 0x01
/**
* QUIC_SEND_RETRY bits take 2 bits for 3 state: default, on, off
*/
#define H2O_EBPF_FLAGS_QUIC_SEND_RETRY_MASK 0x06
#define H2O_EBPF_FLAGS_QUIC_SEND_RETRY_BITS_ON 0x02
#define H2O_EBPF_FLAGS_QUIC_SEND_RETRY_BITS_OFF 0x04
/**
* A pinned BPF map to control connection flags.
* The key type is h2o_ebpf_map_key_t, and the value type is uint64_t that contains `H2O_EBPF_FLAGS_*`.
*/
#define H2O_EBPF_MAP_PATH "/sys/fs/bpf/h2o_map"
/**
* A pinned BPF map to control connection flags, used together with h2o:socket_lookup_flags probe.
* The key type is a thread ID typed as pid_t obtained by `gettid()`, and the value type is uint64_t that contains
* `H2O_EBPF_FLAGS_*`.
* See also h2o-probes.d.
*/
#define H2O_EBPF_RETURN_MAP_NAME "h2o_return"
#define H2O_EBPF_RETURN_MAP_PATH "/sys/fs/bpf/" H2O_EBPF_RETURN_MAP_NAME
/**
* The size of pinned BPF objects.
* The size must be much larger than the number of worker threads and the safe value seems to depend on the system,
*/
#define H2O_EBPF_RETURN_MAP_SIZE 1024
#endif

View File

@ -53,7 +53,7 @@ void h2o_http3_server_init_context(h2o_context_t *h2o, h2o_quic_ctx_t *ctx, h2o_
*/
h2o_http3_conn_t *h2o_http3_server_accept(h2o_http3_server_ctx_t *ctx, quicly_address_t *destaddr, quicly_address_t *srcaddr,
quicly_decoded_packet_t *packet, quicly_address_token_plaintext_t *address_token,
int skip_tracing, const h2o_http3_conn_callbacks_t *h3_callbacks);
const h2o_http3_conn_callbacks_t *h3_callbacks);
/**
* amends the quicly context so that it could be used for the server
*/

View File

@ -37,7 +37,6 @@ extern "C" {
#include "picotls.h"
#include "picotls/openssl.h" /* for H2O_CAN_OSSL_ASYNC */
#include "h2o/cache.h"
#include "h2o/ebpf.h"
#include "h2o/memory.h"
#include "h2o/openssl_backport.h"
#include "h2o/string_.h"
@ -188,9 +187,9 @@ struct st_h2o_socket_t {
*/
uint64_t bytes_written;
/**
* boolean flag to indicate if sock is NOT being traced
* see ptls_log
*/
unsigned _skip_tracing : 1;
float _log_random;
struct {
void (*cb)(void *data);
void *data;
@ -486,11 +485,11 @@ int h2o_socket_set_df_bit(int fd, int domain);
/**
* helper to check if socket the socket is target of tracing
*/
static int h2o_socket_skip_tracing(h2o_socket_t *sock);
static float h2o_socket_log_random(h2o_socket_t *sock);
/**
*
*/
void h2o_socket_set_skip_tracing(h2o_socket_t *sock, int skip_tracing);
void h2o_socket_set_log_random(h2o_socket_t *sock, float r);
#if H2O_CAN_OSSL_ASYNC
/**
@ -532,28 +531,6 @@ int h2o_socket_recycle_is_empty(void);
*/
size_t h2o_sendfile(int sockfd, int filefd, off_t off, size_t len);
/**
* Prepares eBPF maps. Requires root privileges and thus should be called before dropping the privileges. Returns a boolean
* indicating if operation succeeded.
*/
int h2o_socket_ebpf_setup(void);
/**
* Function to lookup if the connection is tagged for special treatment. The result is a union of `H2O_EBPF_FLAGS_*`.
*/
uint64_t h2o_socket_ebpf_lookup_flags(h2o_loop_t *loop, int (*init_key)(h2o_ebpf_map_key_t *key, void *cbdata), void *cbdata);
/**
*
*/
uint64_t h2o_socket_ebpf_lookup_flags_sni(h2o_loop_t *loop, uint64_t flags, const char *server_name, size_t server_name_len);
/**
* function for initializing the ebpf lookup key from raw information
*/
int h2o_socket_ebpf_init_key_raw(h2o_ebpf_map_key_t *key, int sock_type, struct sockaddr *local, struct sockaddr *remote);
/**
* callback for initializing the ebpf lookup key from `h2o_socket_t`
*/
int h2o_socket_ebpf_init_key(h2o_ebpf_map_key_t *key, void *sock);
#ifdef OPENSSL_IS_BORINGSSL
/**
* returns SSL_[gs]et_ext_data slot used to store `ptls_async_job_t` for handling async TLS handshake signature generation
@ -640,9 +617,9 @@ inline void h2o_sliding_counter_start(h2o_sliding_counter_t *counter, uint64_t n
counter->cur.start_at = now;
}
inline int h2o_socket_skip_tracing(h2o_socket_t *sock)
inline float h2o_socket_log_random(h2o_socket_t *sock)
{
return sock->_skip_tracing;
return sock->_log_random;
}
#ifdef __cplusplus

View File

@ -56,10 +56,6 @@
#define TCP_NOTSENT_LOWAT 25
#endif
#if H2O_USE_DTRACE && defined(__linux__)
#define H2O_USE_EBPF_MAP 1
#endif
#define OPENSSL_HOSTNAME_VALIDATION_LINKAGE static
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
@ -200,6 +196,7 @@ __thread h2o_mem_recycle_t h2o_socket_zerocopy_buffer_allocator = {&h2o_socket_s
__thread size_t h2o_socket_num_zerocopy_buffers_inflight;
int h2o_socket_use_ktls = 0;
uint16_t h2o_socket_trace_ratio = UINT16_MAX;
const char h2o_socket_error_out_of_memory[] = "out of memory";
const char h2o_socket_error_io[] = "I/O error";
@ -1944,14 +1941,9 @@ static void proceed_handshake_undetermined(h2o_socket_t *sock)
ptls_buffer_t wbuf;
ptls_buffer_init(&wbuf, "", 0);
#if PICOTLS_USE_DTRACE
unsigned ptls_skip_tracing_backup = ptls_default_skip_tracing;
ptls_default_skip_tracing = sock->_skip_tracing;
#endif
ptls_log_random_override = &sock->_log_random;
ptls_t *ptls = ptls_new(ptls_ctx, 1);
#if PICOTLS_USE_DTRACE
ptls_default_skip_tracing = ptls_skip_tracing_backup;
#endif
ptls_log_random_override = NULL;
if (ptls == NULL)
h2o_fatal("no memory");
*ptls_get_data_ptr(ptls) = sock;
@ -2303,11 +2295,11 @@ int h2o_socket_set_df_bit(int fd, int domain)
#undef SETSOCKOPT
}
void h2o_socket_set_skip_tracing(h2o_socket_t *sock, int skip_tracing)
void h2o_socket_set_log_random(h2o_socket_t *sock, float r)
{
sock->_skip_tracing = skip_tracing;
sock->_log_random = r;
if (sock->ssl != NULL && sock->ssl->ptls != NULL)
ptls_set_skip_tracing(sock->ssl->ptls, skip_tracing);
ptls_set_log_random(sock->ssl->ptls, r);
}
void h2o_sliding_counter_stop(h2o_sliding_counter_t *counter, uint64_t now)
@ -2451,332 +2443,6 @@ int h2o_socket_recycle_is_empty(void)
h2o_mem_recycle_is_empty(&h2o_socket_zerocopy_buffer_allocator);
}
#if H2O_USE_EBPF_MAP
#include <linux/bpf.h>
#include <linux/unistd.h>
#include <sys/stat.h>
#include "h2o/multithread.h"
#include "h2o-probes.h"
static int ebpf_map_create(uint32_t map_type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, const char *map_name)
{
union bpf_attr attr = {
.map_type = map_type,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries,
};
strncpy(attr.map_name, map_name, sizeof(attr.map_name));
return syscall(SYS_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
}
static int ebpf_obj_pin(int bpf_fd, const char *pathname)
{
union bpf_attr attr = {
.bpf_fd = (uint32_t)bpf_fd,
.pathname = (uint64_t)pathname,
};
return syscall(SYS_bpf, BPF_OBJ_PIN, &attr, sizeof(attr));
}
static int ebpf_obj_get(const char *pathname)
{
union bpf_attr attr = {
.pathname = (uint64_t)pathname,
};
return syscall(SYS_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
}
static int ebpf_obj_get_info_by_fd(int fd, struct bpf_map_info *info)
{
union bpf_attr attr = {
.info =
{
.bpf_fd = fd,
.info = (uint64_t)info,
.info_len = sizeof(*info),
},
};
return syscall(SYS_bpf, BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
}
static int ebpf_map_lookup(int fd, const void *key, void *value)
{
union bpf_attr attr = {
.map_fd = fd,
.key = (uint64_t)key,
.value = (uint64_t)value,
};
return syscall(SYS_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}
static int ebpf_map_delete(int fd, const void *key)
{
union bpf_attr attr = {
.map_fd = fd,
.key = (uint64_t)key,
};
return syscall(SYS_bpf, BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
}
static int return_map_fd = -1; // for h2o_return
int h2o_socket_ebpf_setup(void)
{
const struct {
int type;
uint32_t key_size;
uint32_t value_size;
} map_attr = {
.type = BPF_MAP_TYPE_LRU_HASH,
.key_size = sizeof(pid_t),
.value_size = sizeof(uint64_t),
};
int fd = -1;
if (getuid() != 0) {
h2o_error_printf("failed to set up eBPF maps because bpf(2) requires root privileges\n");
goto Error;
}
fd = ebpf_obj_get(H2O_EBPF_RETURN_MAP_PATH);
if (fd < 0) {
if (errno != ENOENT) {
h2o_perror("BPF_OBJ_GET failed");
goto Error;
}
/* Pinned eBPF map does not exist. Create one and pin it to the BPF filesystem. */
fd = ebpf_map_create(map_attr.type, map_attr.key_size, map_attr.value_size, H2O_EBPF_RETURN_MAP_SIZE,
H2O_EBPF_RETURN_MAP_NAME);
if (fd < 0) {
if (errno == EPERM) {
h2o_error_printf("BPF_MAP_CREATE failed with EPERM, maybe because RLIMIT_MEMLOCK is too small.\n");
} else {
h2o_perror("BPF_MAP_CREATE failed");
}
goto Error;
}
if (ebpf_obj_pin(fd, H2O_EBPF_RETURN_MAP_PATH) != 0) {
if (errno == ENOENT) {
h2o_error_printf("BPF_OBJ_PIN failed with ENOENT, because /sys/fs/bpf is not mounted as a BPF filesystem.\n");
} else {
h2o_perror("BPF_OBJ_PIN failed");
}
goto Error;
}
} else {
/* BPF_OBJ_GET successfully opened a pinned eBPF map. Make sure the critical attributes (type, key size, value size) are
* correct, otherwise usdt-selective-tracing does not work. */
struct bpf_map_info m;
if (ebpf_obj_get_info_by_fd(fd, &m) != 0) {
h2o_perror("BPF_OBJ_GET_INFO_BY_FD failed");
goto Error;
}
if (m.type != map_attr.type) {
h2o_error_printf(H2O_EBPF_RETURN_MAP_PATH " has an unexpected map type: expected %d but got %d\n", map_attr.type,
m.type);
goto Error;
}
if (m.key_size != map_attr.key_size) {
h2o_error_printf(H2O_EBPF_RETURN_MAP_PATH " has an unexpected map key size: expected %" PRIu32 " but got %" PRIu32 "\n",
map_attr.key_size, m.key_size);
goto Error;
}
if (m.value_size != map_attr.value_size) {
h2o_error_printf(H2O_EBPF_RETURN_MAP_PATH " has an unexpected map value size: expected %" PRIu32 " but got %" PRIu32
"\n",
map_attr.value_size, m.value_size);
goto Error;
}
}
/* success */
return_map_fd = fd;
return 1;
Error:
if (fd >= 0)
close(fd);
return 0;
}
static void get_map_fd(h2o_loop_t *loop, const char *map_path, int *fd, uint64_t *last_attempt)
{
// only check every second
uint64_t now = h2o_now(loop);
if (*last_attempt - now < 1000)
return;
*last_attempt = now;
struct stat s;
if (stat(map_path, &s) != 0) {
// map path unavailable, cleanup fd if needed and leave
if (*fd >= 0) {
close(*fd);
*fd = -1;
}
return;
}
if (*fd >= 0)
return; // map still exists and we have a fd
// map exists, try connect
*fd = ebpf_obj_get(map_path);
if (*fd < 0)
h2o_perror("BPF_OBJ_GET failed");
}
static int get_tracing_map_fd(h2o_loop_t *loop)
{
static __thread int fd = -1;
static __thread uint64_t last_attempt = 0;
get_map_fd(loop, H2O_EBPF_MAP_PATH, &fd, &last_attempt);
return fd;
}
static inline int set_ebpf_map_key_tuples(const struct sockaddr *sa, h2o_ebpf_address_t *ea)
{
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (void *)sa;
memcpy(ea->ip, &sin->sin_addr, sizeof(sin->sin_addr));
ea->port = sin->sin_port;
return 1;
} else if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin = (void *)sa;
memcpy(ea->ip, &sin->sin6_addr, sizeof(sin->sin6_addr));
ea->port = sin->sin6_port;
return 1;
} else {
return 0;
}
}
int h2o_socket_ebpf_init_key_raw(h2o_ebpf_map_key_t *key, int sock_type, struct sockaddr *local, struct sockaddr *remote)
{
memset(key, 0, sizeof(*key));
if (!set_ebpf_map_key_tuples(local, &key->local))
return 0;
if (!set_ebpf_map_key_tuples(remote, &key->remote))
return 0;
key->family = local->sa_family == AF_INET6 ? 6 : 4;
key->protocol = sock_type;
return 1;
}
int h2o_socket_ebpf_init_key(h2o_ebpf_map_key_t *key, void *_sock)
{
h2o_socket_t *sock = _sock;
struct sockaddr_storage local, remote;
unsigned int sock_type, sock_type_len = sizeof(sock_type_len);
/* fetch info */
if (h2o_socket_getsockname(sock, (void *)&local) == 0)
return 0;
if (h2o_socket_getpeername(sock, (void *)&remote) == 0)
return 0;
if (getsockopt(h2o_socket_get_fd(sock), SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len) != 0) /* can't the info be cached? */
return 0;
return h2o_socket_ebpf_init_key_raw(key, sock_type, (void *)&local, (void *)&remote);
}
static void report_ebpf_lookup_errors(h2o_error_reporter_t *reporter, uint64_t total_successes, uint64_t cur_successes)
{
fprintf(stderr,
"BPF_MAP_LOOKUP_ELEM failed with ENOENT %" PRIu64 " time%s, succeeded: %" PRIu64 " time%s, over the last minute.\n",
reporter->cur_errors, reporter->cur_errors > 1 ? "s" : "", cur_successes, cur_successes > 1 ? "s" : "");
}
static h2o_error_reporter_t track_ebpf_lookup = H2O_ERROR_REPORTER_INITIALIZER(report_ebpf_lookup_errors);
#define DO_EBPF_RETURN_LOOKUP(func) \
do { \
if (return_map_fd >= 0) { \
pid_t tid = (pid_t)syscall(SYS_gettid); /* gettid() was not available until glibc 2.30 (2019) */ \
/* Make sure old flags do not exist, otherwise the subsequent logic will be unreliable. */ \
if (ebpf_map_delete(return_map_fd, &tid) == 0 || errno == ENOENT) { \
do { \
func \
} while (0); \
if (ebpf_map_lookup(return_map_fd, &tid, &flags) == 0) { \
h2o_error_reporter_record_success(&track_ebpf_lookup); \
} else { \
if (errno == ENOENT) { \
/* ENOENT could be issued in some reasons even if BPF tries to insert the entry, for example: \
* * the entry in LRU hash was evicted \
* * the insert operation in BPF program failed with ENOMEM \
* We don't know the frequency for this ENOENT, so cap the number of logs. \
* \
* Other than the above reasons, ENOENT is issued when the tracer does not set the flags via h2o_return \
* map, See h2o:_private_socket_lookup_flags handler in h2olog for details. */ \
h2o_error_reporter_record_error(loop, &track_ebpf_lookup, 60000, 0); \
} else { \
h2o_perror("BPF_MAP_LOOKUP failed"); \
} \
} \
} else { \
h2o_perror("BPF_MAP_DELETE failed"); \
} \
} \
} while (0)
uint64_t h2o_socket_ebpf_lookup_flags(h2o_loop_t *loop, int (*init_key)(h2o_ebpf_map_key_t *key, void *cbdata), void *cbdata)
{
uint64_t flags = 0;
int tracing_map_fd = get_tracing_map_fd(loop);
h2o_ebpf_map_key_t key;
if ((tracing_map_fd >= 0 || H2O__PRIVATE_SOCKET_LOOKUP_FLAGS_ENABLED()) && init_key(&key, cbdata)) {
if (tracing_map_fd >= 0)
ebpf_map_lookup(tracing_map_fd, &key, &flags);
if (H2O__PRIVATE_SOCKET_LOOKUP_FLAGS_ENABLED())
DO_EBPF_RETURN_LOOKUP({ H2O__PRIVATE_SOCKET_LOOKUP_FLAGS(tid, flags, &key); });
}
return flags;
}
uint64_t h2o_socket_ebpf_lookup_flags_sni(h2o_loop_t *loop, uint64_t flags, const char *server_name, size_t server_name_len)
{
if (H2O__PRIVATE_SOCKET_LOOKUP_FLAGS_SNI_ENABLED())
DO_EBPF_RETURN_LOOKUP({ H2O__PRIVATE_SOCKET_LOOKUP_FLAGS_SNI(tid, flags, server_name, server_name_len); });
return flags;
}
#undef DO_EBPF_RETURN_LOOKUP
#else
int h2o_socket_ebpf_setup(void)
{
return 0;
}
int h2o_socket_ebpf_init_key_raw(h2o_ebpf_map_key_t *key, int sock_type, struct sockaddr *local, struct sockaddr *remote)
{
h2o_fatal("unimplemented");
}
int h2o_socket_ebpf_init_key(h2o_ebpf_map_key_t *key, void *sock)
{
h2o_fatal("unimplemented");
}
uint64_t h2o_socket_ebpf_lookup_flags(h2o_loop_t *loop, int (*init_key)(h2o_ebpf_map_key_t *key, void *cbdata), void *cbdata)
{
return 0;
}
uint64_t h2o_socket_ebpf_lookup_flags_sni(h2o_loop_t *loop, uint64_t flags, const char *server_name, size_t server_name_len)
{
return flags;
}
#endif
#ifdef OPENSSL_IS_BORINGSSL
int h2o_socket_boringssl_get_async_job_index(void)

View File

@ -728,9 +728,7 @@ h2o_socket_t *h2o_evloop_socket_accept(h2o_socket_t *_listener)
if (peeraddr != NULL && *peeraddrlen <= sizeof(*peeraddr))
h2o_socket_setpeername(sock, (struct sockaddr *)peeraddr, *peeraddrlen);
uint64_t flags = h2o_socket_ebpf_lookup_flags(listener->loop, h2o_socket_ebpf_init_key, sock);
if ((flags & H2O_EBPF_FLAGS_SKIP_TRACING_BIT) != 0)
sock->_skip_tracing = 1;
sock->_log_random = ptls_generate_log_random(ptls_openssl_random_bytes);
return sock;
}

View File

@ -375,9 +375,7 @@ h2o_socket_t *h2o_uv_socket_create(uv_handle_t *handle, uv_close_cb close_cb)
sock->close_cb = close_cb;
sock->handle->data = sock;
h2o_timer_init(&sock->write_cb_timer, on_call_write_success);
uint64_t flags = h2o_socket_ebpf_lookup_flags(sock->handle->loop, h2o_socket_ebpf_init_key, &sock->super);
if ((flags & H2O_EBPF_FLAGS_SKIP_TRACING_BIT) != 0)
sock->super._skip_tracing = 1;
sock->super._log_random = ptls_generate_log_random(ptls_openssl_random_bytes);
return &sock->super;
}

View File

@ -1004,15 +1004,6 @@ static int on_config_stash(h2o_configurator_command_t *cmd, h2o_configurator_con
return 0;
}
static int on_config_usdt_selective_tracing(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
{
ssize_t on;
if ((on = h2o_configurator_get_one_of(cmd, node, "OFF,ON")) == -1)
return -1;
ctx->globalconf->usdt_selective_tracing = (int)on;
return 0;
}
void h2o_configurator__init_core(h2o_globalconf_t *conf)
{
/* check if already initialized */
@ -1160,9 +1151,6 @@ void h2o_configurator__init_core(h2o_globalconf_t *conf)
H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
on_config_send_informational);
h2o_configurator_define_command(&c->super, "stash", H2O_CONFIGURATOR_FLAG_ALL_LEVELS, on_config_stash);
h2o_configurator_define_command(&c->super, "usdt-selective-tracing",
H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
on_config_usdt_selective_tracing);
}
}

View File

@ -24,6 +24,7 @@
static int on_req(h2o_handler_t *_self, h2o_req_t *req)
{
struct sockaddr_storage local;
float sample_ratio = 1;
char *trace = h2o_mem_alloc(req->path.len + 2 /* should be enough */), *trace_tail = trace;
h2o_socket_t *sock;
h2o_socket_export_t export_info;
@ -38,7 +39,13 @@ static int on_req(h2o_handler_t *_self, h2o_req_t *req)
const char *name;
size_t name_len;
while ((name = h2o_next_token(&iter, '&', '&', &name_len, &value)) != NULL) {
if (h2o_memis(name, name_len, H2O_STRLIT("trace"))) {
if (h2o_memis(name, name_len, H2O_STRLIT("sample-ratio"))) {
if (sscanf(h2o_strdup(&req->pool, value.base, value.len).base, "%f", &sample_ratio) != 1 ||
!(0 <= sample_ratio && sample_ratio <= 1)) {
h2o_send_error_400(req, "Bad Request", "sample-rate must be a number between 0 and 1", 0);
return 0;
}
} else if (h2o_memis(name, name_len, H2O_STRLIT("trace"))) {
h2o_memcpy(trace_tail, value.base, value.len);
trace_tail += value.len;
*trace_tail++ = '\0';
@ -58,7 +65,7 @@ static int on_req(h2o_handler_t *_self, h2o_req_t *req)
(void)write(export_info.fd, H2O_STRLIT("HTTP/1.1 200 OK\r\n\r\n"));
/* register log fd after writing HTTP response, as log is written by multiple threads */
if (ptls_log_add_fd(export_info.fd, trace) != 0)
if (ptls_log_add_fd(export_info.fd, sample_ratio, trace) != 0)
h2o_fatal("failed to add fd to h2olog");
return 0;

View File

@ -408,7 +408,7 @@ static socklen_t get_peername(h2o_conn_t *_conn, struct sockaddr *sa)
return conn->remote.len;
}
static int skip_tracing(h2o_conn_t *conn)
static float log_random(h2o_conn_t *conn)
{
return 1;
}
@ -638,7 +638,7 @@ static struct st_mruby_subreq_t *create_subreq(h2o_mruby_context_t *ctx, mrb_val
static const h2o_conn_callbacks_t callbacks = {
.get_sockname = get_sockname,
.get_peername = get_peername,
.skip_tracing = skip_tracing,
.log_random = log_random,
.get_req_id = get_req_id,
};

View File

@ -1151,10 +1151,10 @@ static ptls_t *get_ptls(h2o_conn_t *_conn)
return h2o_socket_get_ptls(conn->sock);
}
static int skip_tracing(h2o_conn_t *_conn)
static float log_random(h2o_conn_t *_conn)
{
struct st_h2o_http1_conn_t *conn = (void *)_conn;
return h2o_socket_skip_tracing(conn->sock);
return h2o_socket_log_random(conn->sock);
}
static int can_zerocopy(h2o_conn_t *_conn)
@ -1228,7 +1228,7 @@ static const h2o_conn_callbacks_t h1_callbacks = {
.get_sockname = get_sockname,
.get_peername = get_peername,
.get_ptls = get_ptls,
.skip_tracing = skip_tracing,
.log_random = log_random,
.close_idle_connection = close_idle_connection,
.foreach_request = foreach_request,
.request_shutdown = initiate_graceful_shutdown,

View File

@ -1620,11 +1620,11 @@ static ptls_t *get_ptls(h2o_conn_t *_conn)
return h2o_socket_get_ptls(conn->sock);
}
static int skip_tracing(h2o_conn_t *_conn)
static float log_random(h2o_conn_t *_conn)
{
struct st_h2o_http2_conn_t *conn = (void *)_conn;
assert(conn->sock != NULL && "it never becomes NULL, right?");
return h2o_socket_skip_tracing(conn->sock);
return h2o_socket_log_random(conn->sock);
}
static uint64_t get_req_id(h2o_req_t *req)
@ -1756,7 +1756,7 @@ static h2o_http2_conn_t *create_conn(h2o_context_t *ctx, h2o_hostconf_t **hosts,
.get_sockname = get_sockname,
.get_peername = get_peername,
.get_ptls = get_ptls,
.skip_tracing = skip_tracing,
.log_random = log_random,
.get_req_id = get_req_id,
.push_path = push_path,
.get_debug_state = h2o_http2_get_debug_state,

View File

@ -561,10 +561,10 @@ static ptls_t *get_ptls(h2o_conn_t *_conn)
return quicly_get_tls(conn->h3.super.quic);
}
static int get_skip_tracing(h2o_conn_t *conn)
static float log_random(h2o_conn_t *conn)
{
ptls_t *ptls = get_ptls(conn);
return ptls_skip_tracing(ptls);
return ptls_log_random(ptls);
}
static uint64_t get_req_id(h2o_req_t *req)
@ -2150,13 +2150,13 @@ void h2o_http3_server_init_context(h2o_context_t *h2o, h2o_quic_ctx_t *ctx, h2o_
h2o_http3_conn_t *h2o_http3_server_accept(h2o_http3_server_ctx_t *ctx, quicly_address_t *destaddr, quicly_address_t *srcaddr,
quicly_decoded_packet_t *packet, quicly_address_token_plaintext_t *address_token,
int skip_tracing, const h2o_http3_conn_callbacks_t *h3_callbacks)
const h2o_http3_conn_callbacks_t *h3_callbacks)
{
static const h2o_conn_callbacks_t conn_callbacks = {
.get_sockname = get_sockname,
.get_peername = get_peername,
.get_ptls = get_ptls,
.skip_tracing = get_skip_tracing,
.log_random = log_random,
.get_req_id = get_req_id,
.close_idle_connection = close_idle_connection,
.foreach_request = foreach_request,
@ -2218,18 +2218,11 @@ h2o_http3_conn_t *h2o_http3_server_accept(h2o_http3_server_ctx_t *ctx, quicly_ad
/* accept connection */
assert(ctx->super.next_cid != NULL && "to set next_cid, h2o_quic_set_context_identifier must be called");
#if PICOTLS_USE_DTRACE
unsigned orig_skip_tracing = ptls_default_skip_tracing;
ptls_default_skip_tracing = skip_tracing;
#endif
quicly_conn_t *qconn;
int accept_ret = quicly_accept(
&qconn, ctx->super.quic, &destaddr->sa, &srcaddr->sa, packet, address_token, ctx->super.next_cid,
&conn->handshake_properties,
&conn->h3 /* back pointer is set up here so that callbacks being called while parsing ClientHello can refer to `conn` */);
#if PICOTLS_USE_DTRACE
ptls_default_skip_tracing = orig_skip_tracing;
#endif
if (accept_ret != 0) {
h2o_http3_conn_t *ret = NULL;
if (accept_ret == QUICLY_ERROR_DECRYPTION_FAILED)

View File

@ -31,9 +31,7 @@
if (!ptls_log_point_is_active(&logpoint)) \
break; \
h2o_conn_t *conn_ = (_conn); \
if (conn_->callbacks->skip_tracing(conn_)) \
break; \
PTLS_LOG__DO_LOG(h2o, _name, { \
PTLS_LOG__DO_LOG(h2o, _name, conn_->callbacks->log_random(conn_), { \
PTLS_LOG_ELEMENT_UNSIGNED(conn_id, conn_->id); \
do { \
_block \
@ -150,7 +148,8 @@ static inline void h2o_probe_log_request(h2o_req_t *req, uint64_t req_index)
PTLS_LOG_DEFINE_POINT(h2o, receive_request_header, receive_request_header_logpoint);
if (PTLS_UNLIKELY(H2O_RECEIVE_REQUEST_HEADER_ENABLED()) ||
(ptls_log_point_is_active(&receive_request_header_logpoint) && !req->conn->callbacks->skip_tracing(req->conn))) {
(ptls_log_point_is_active(&receive_request_header_logpoint) &&
req->conn->callbacks->log_random(req->conn) < ptls_log__max_sample_ratio)) {
if (req->input.authority.base != NULL)
h2o_probe_request_header(req, req_index, H2O_TOKEN_AUTHORITY->buf, req->input.authority);
if (req->input.method.base != NULL)
@ -176,7 +175,8 @@ static inline void h2o_probe_log_response(h2o_req_t *req, uint64_t req_index)
});
PTLS_LOG_DEFINE_POINT(h2o, send_response_header, send_response_header_logpoint);
if (PTLS_UNLIKELY(H2O_SEND_RESPONSE_HEADER_ENABLED()) ||
(ptls_log_point_is_active(&send_response_header_logpoint) && !req->conn->callbacks->skip_tracing(req->conn))) {
(ptls_log_point_is_active(&send_response_header_logpoint) &&
req->conn->callbacks->log_random(req->conn) < ptls_log__max_sample_ratio)) {
if (req->res.content_length != SIZE_MAX) {
char buf[sizeof(H2O_SIZE_T_LONGEST_STR)];
size_t len = (size_t)sprintf(buf, "%zu", req->res.content_length);

View File

@ -910,6 +910,7 @@ static void setup_ecc_key(SSL_CTX *ssl_ctx)
static void on_sni_update_tracing(void *conn, int is_quic, const char *server_name, size_t server_name_len)
{
#if 0 /* FIXME */
int cur_skip_tracing;
if (is_quic) {
@ -930,6 +931,7 @@ static void on_sni_update_tracing(void *conn, int is_quic, const char *server_na
h2o_socket_set_skip_tracing(conn, new_skip_tracing);
}
}
#endif
}
static struct listener_ssl_config_t *resolve_sni(struct listener_config_t *listener, const char *name, size_t name_len)
@ -3909,16 +3911,6 @@ static void on_accept(h2o_socket_t *listener, const char *err)
} while (--num_accepts != 0);
}
struct init_ebpf_key_info_t {
struct sockaddr *local, *remote;
};
static int init_ebpf_key(h2o_ebpf_map_key_t *key, void *_info)
{
struct init_ebpf_key_info_t *info = _info;
return h2o_socket_ebpf_init_key_raw(key, SOCK_DGRAM, info->local, info->remote);
}
static int validate_token(h2o_http3_server_ctx_t *ctx, struct sockaddr *remote, ptls_iovec_t client_cid, ptls_iovec_t server_cid,
quicly_address_token_plaintext_t *token)
{
@ -3970,12 +3962,6 @@ static h2o_quic_conn_t *on_http3_accept(h2o_quic_ctx_t *_ctx, quicly_address_t *
return NULL;
}
struct init_ebpf_key_info_t ebpf_key_info = {
.local = &destaddr->sa,
.remote = &srcaddr->sa,
};
uint64_t flags = h2o_socket_ebpf_lookup_flags(ctx->super.loop, init_ebpf_key, &ebpf_key_info);
quicly_address_token_plaintext_t *token = NULL, token_buf;
h2o_http3_conn_t *conn = NULL;
@ -3999,18 +3985,7 @@ static h2o_quic_conn_t *on_http3_accept(h2o_quic_ctx_t *_ctx, quicly_address_t *
/* send retry if necessary */
if (token == NULL || token->type != QUICLY_ADDRESS_TOKEN_TYPE_RETRY) {
int send_retry = ctx->send_retry;
switch (flags & H2O_EBPF_FLAGS_QUIC_SEND_RETRY_MASK) {
case H2O_EBPF_FLAGS_QUIC_SEND_RETRY_BITS_ON:
send_retry = 1;
break;
case H2O_EBPF_FLAGS_QUIC_SEND_RETRY_BITS_OFF:
send_retry = 0;
break;
default:
break;
}
if (send_retry) {
if (ctx->send_retry) {
static __thread struct {
ptls_aead_context_t *v1;
ptls_aead_context_t *draft29;
@ -4048,8 +4023,7 @@ static h2o_quic_conn_t *on_http3_accept(h2o_quic_ctx_t *_ctx, quicly_address_t *
}
/* accept the connection */
conn = h2o_http3_server_accept(ctx, destaddr, srcaddr, packet, token, (H2O_EBPF_FLAGS_SKIP_TRACING_BIT & flags) != 0,
&conf.quic.conn_callbacks);
conn = h2o_http3_server_accept(ctx, destaddr, srcaddr, packet, token, &conf.quic.conn_callbacks);
if (conn == NULL || &conn->super == &h2o_quic_accept_conn_decryption_failed) {
goto Exit;
} else if (conn == &h2o_http3_accept_conn_closed) {
@ -4943,10 +4917,6 @@ int main(int argc, char **argv)
#endif
setup_signal_handlers();
if (conf.globalconf.usdt_selective_tracing && !h2o_socket_ebpf_setup()) {
h2o_error_printf("usdt-selective-tracing is set to ON but failed to setup eBPF\n");
return EX_CONFIG;
}
/* open the log file to redirect STDIN/STDERR to, before calling setuid */
if (conf.error_log != NULL) {