From 4061df91229ad4f98fef2c126f21e8d1fd291f99 Mon Sep 17 00:00:00 2001 From: "randomizedcoder dave.seddon.ca@gmail.com" Date: Wed, 8 Jul 2026 22:11:24 -0700 Subject: [PATCH] feat(web): clamp IPv4 TTL / IPv6 hop limit on xtcp2's TCP listeners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an opt-in per-listener TTL / hop-limit clamp so xtcp2's replies can't travel far if the host is unexpectedly internet-exposed — the per-listener analogue of a host-level nftables TTL clamp, mirroring prometheus/exporter-toolkit#396. - New pkg/ipsockopt: a net.ListenConfig.Control that setsockopt IP_TTL / IPV6_UNICAST_HOPS on the listening socket (inherited by accepted conns). Returns nil when both are 0 (kernel default); swallows ENOPROTOOPT for the non-matching address family. - Config: ipv4_ttl (184) + ipv6_hop_limit (185) on XtcpConfig (0 = default, validated <= 255). Flags -ipv4Ttl / -ipv6HopLimit, env IPV4_TTL / IPV6_HOP_LIMIT. - Applied to BOTH host-exposed listeners: the Prometheus /metrics server (cmd/xtcp2 now net.Listen + srv.Serve instead of ListenAndServe) and the gRPC server (grpc_server.go). - Tests: pkg/ipsockopt binds real v4/v6 listeners and getsockopt-verifies the value; cmd/xtcp2 env + buildConfig cover the new knobs. Default 0 → no behavior change for existing deployments. Co-Authored-By: Claude Opus 4.8 --- cmd/xtcp2/xtcp2.go | 45 +++++-- cmd/xtcp2/xtcp2_test.go | 20 ++- dart/xtcp_config/v1/xtcp_config.pb.dart | 71 +++++++--- dart/xtcp_config/v1/xtcp_config.pbjson.dart | 22 ++-- docs/output-and-destinations.md | 2 + gen/xtcp_config/v1/xtcp_config.pb.cc | 123 ++++++++++++------ gen/xtcp_config/v1/xtcp_config.pb.h | 72 +++++++++- gen/xtcp_config/v1/xtcp_config.pb.validate.cc | 36 ++++- pkg/ipsockopt/ipsockopt.go | 50 +++++++ pkg/ipsockopt/ipsockopt_test.go | 70 ++++++++++ pkg/xtcp/grpc_server.go | 5 +- pkg/xtcp_config/xtcp_config.pb.go | 29 ++++- pkg/xtcp_config/xtcp_config_vtproto.pb.go | 58 +++++++++ proto/xtcp_config/v1/xtcp_config.proto | 19 +++ python/xtcp_config/v1/xtcp_config_pb2.py | 20 +-- python/xtcp_config/v1/xtcp_config_pb2.pyi | 8 +- xtcp_config/v1/xtcp_config.swagger.json | 10 ++ 17 files changed, 565 insertions(+), 95 deletions(-) create mode 100644 pkg/ipsockopt/ipsockopt.go create mode 100644 pkg/ipsockopt/ipsockopt_test.go diff --git a/cmd/xtcp2/xtcp2.go b/cmd/xtcp2/xtcp2.go index 2838c415..34611815 100644 --- a/cmd/xtcp2/xtcp2.go +++ b/cmd/xtcp2/xtcp2.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "log" + "net" "net/http" "os" "os/signal" @@ -30,6 +31,7 @@ import ( "github.com/pkg/profile" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/randomizedcoder/xtcp2/pkg/ipsockopt" "github.com/randomizedcoder/xtcp2/pkg/misc" "github.com/randomizedcoder/xtcp2/pkg/xtcp" "github.com/randomizedcoder/xtcp2/pkg/xtcp_config" @@ -137,6 +139,9 @@ const ( hostnameCst = "" resolveContainerIdCst = false + ipv4TtlCst uint = 0 + ipv6HopLimitCst uint = 0 + deserializersCst = "all" grpcPortCst = 8889 @@ -207,6 +212,8 @@ type mainFlags struct { location *string hostname *string resolveContainerId *bool + ipv4Ttl *uint + ipv6HopLimit *uint grpcPort *uint deserializers *string promListen *string @@ -264,6 +271,8 @@ func defineFlags() *mainFlags { f.location = flag.String("location", locationCst, "deployment grouping/facility this daemon runs in (data center, PoP, region, site, …); stamped on every record's `location`. Falls back to LOCATION env.") f.hostname = flag.String("hostname", hostnameCst, "hostname stamped on records; defaults to os.Hostname(). Set this in a container, where os.Hostname() returns the container id, not the host. Falls back to XTCP_HOSTNAME env (NOT HOSTNAME).") f.resolveContainerId = flag.Bool("resolveContainerId", resolveContainerIdCst, "resolve each socket's owning container id from its cgroup into container_id/container_runtime; needs /sys/fs/cgroup readable (mount it + --cgroupns=host in a container). Falls back to CONTAINER_ID_RESOLVE env.") + f.ipv4Ttl = flag.Uint("ipv4Ttl", ipv4TtlCst, "outgoing IPv4 TTL for xtcp2's TCP listeners (Prometheus + gRPC); 0 = kernel default. A low value keeps replies from travelling far if the host is internet-exposed. Falls back to IPV4_TTL env.") + f.ipv6HopLimit = flag.Uint("ipv6HopLimit", ipv6HopLimitCst, "outgoing IPv6 unicast hop limit for xtcp2's TCP listeners; 0 = kernel default. Falls back to IPV6_HOP_LIMIT env.") f.grpcPort = flag.Uint("grpcPort", grpcPortCst, "GRPC listening port") f.deserializers = flag.String("deserializers", deserializersCst, fmt.Sprintf("Comma separated list of deserializers,%v", xtcp.GetAllDeserializers())) f.promListen = flag.String("promListen", promListenCst, "Prometheus http listening socket") @@ -378,6 +387,8 @@ func buildConfig(f *mainFlags, des *xtcp_config.EnabledDeserializers) *xtcp_conf Location: *f.location, Hostname: *f.hostname, ResolveContainerId: *f.resolveContainerId, + Ipv4Ttl: uint32(*f.ipv4Ttl), + Ipv6HopLimit: uint32(*f.ipv6HopLimit), GrpcPort: uint32(*f.grpcPort), EnabledDeserializers: des, @@ -529,8 +540,8 @@ var daemonRunner = runDaemonDefault // promHandlerStarter is the indirection point for the prom-handler // goroutine launch. Default starts the real handler; tests swap it for // a no-op to skip the port-bind. -var promHandlerStarter = func(promPath, promListen string) { - go initPromHandler(promPath, promListen) +var promHandlerStarter = func(promPath, promListen string, ipv4TTL, ipv6HopLimit uint32) { + go initPromHandler(promPath, promListen, ipv4TTL, ipv6HopLimit) } func main() { @@ -582,7 +593,7 @@ func runMain(parentCtx context.Context) int { debugLevel)() environmentOverrideProm(f.promListen, f.promPath, debugLevel) - promHandlerStarter(*f.promPath, *f.promListen) + promHandlerStarter(*f.promPath, *f.promListen, c.Ipv4Ttl, c.Ipv6HopLimit) if debugLevel > 10 { log.Println("Prometheus http listener started on:", *f.promListen, *f.promPath) } @@ -670,7 +681,7 @@ func awaitSignalAndShutdown( // ListenAndServe error branch is exercisable without exiting. var fatalf = log.Fatalf -func initPromHandler(promPath string, promListen string) { +func initPromHandler(promPath string, promListen string, ipv4TTL, ipv6HopLimit uint32) { http.Handle(promPath, promhttp.HandlerFor( prometheus.DefaultGatherer, promhttp.HandlerOpts{ @@ -678,22 +689,30 @@ func initPromHandler(promPath string, promListen string) { MaxRequestsInFlight: promMaxRequestsInFlight, }, )) - go servePromHandler(promListen) + go servePromHandler(promListen, ipv4TTL, ipv6HopLimit) } // servePromHandler runs the prom HTTP server on promListen. On // ListenAndServe failure it invokes fatalf (default log.Fatalf in // production, swapped to a capture by tests). Extracted from // initPromHandler so tests can drive the error path in isolation. -func servePromHandler(promListen string) { +func servePromHandler(promListen string, ipv4TTL, ipv6HopLimit uint32) { srv := &http.Server{ - Addr: promListen, ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 30 * time.Second, } - if err := srv.ListenAndServe(); err != nil { + // net.Listen (not srv.ListenAndServe) so the IPv4 TTL / IPv6 hop limit can + // be clamped on the listening socket before bind (inherited by accepted + // connections). ipsockopt.Control is nil when both are 0 → kernel default. + lc := net.ListenConfig{Control: ipsockopt.Control(ipv4TTL, ipv6HopLimit)} + ln, err := lc.Listen(context.Background(), "tcp", promListen) + if err != nil { + fatalf("prometheus error, listen: %v", err) + return + } + if err := srv.Serve(ln); err != nil { fatalf("prometheus error: %v", err) } } @@ -1040,6 +1059,14 @@ func envOverrideLabeling(c *xtcp_config.XtcpConfig, debugLevel uint) { c.ResolveContainerId = v logEnv("CONTAINER_ID_RESOLVE", fmt.Sprintf("c.ResolveContainerId:%t", v), debugLevel) } + if v, ok := envUint32("IPV4_TTL"); ok { + c.Ipv4Ttl = v + logEnv("IPV4_TTL", fmt.Sprintf("c.Ipv4Ttl:%d", v), debugLevel) + } + if v, ok := envUint32("IPV6_HOP_LIMIT"); ok { + c.Ipv6HopLimit = v + logEnv("IPV6_HOP_LIMIT", fmt.Sprintf("c.Ipv6HopLimit:%d", v), debugLevel) + } if v, ok := envUint32("GRPC_PORT"); ok { c.GrpcPort = v logEnv("GRPC_PORT", fmt.Sprintf("c.GrpcPort:%d", v), debugLevel) @@ -1076,6 +1103,8 @@ func printConfig(c *xtcp_config.XtcpConfig, comment string) { fmt.Println("c.KafkaSchemaUrl:", c.KafkaSchemaUrl) fmt.Println("c.KafkaProduceTimeout:", c.KafkaProduceTimeout.AsDuration()) fmt.Println("c.DebugLevel:", c.DebugLevel) + fmt.Println("c.Ipv4Ttl:", c.Ipv4Ttl) + fmt.Println("c.Ipv6HopLimit:", c.Ipv6HopLimit) fmt.Println("c.Label:", c.Label) fmt.Println("c.Tag:", c.Tag) fmt.Println("c.Location:", c.Location) diff --git a/cmd/xtcp2/xtcp2_test.go b/cmd/xtcp2/xtcp2_test.go index 792cd592..e4a941fc 100644 --- a/cmd/xtcp2/xtcp2_test.go +++ b/cmd/xtcp2/xtcp2_test.go @@ -307,11 +307,16 @@ func TestEnvOverrideLabeling(t *testing.T) { t.Setenv("LOCATION", "eu-ro-1") t.Setenv("XTCP_HOSTNAME", "runpod435") t.Setenv("CONTAINER_ID_RESOLVE", "true") + t.Setenv("IPV4_TTL", "3") + t.Setenv("IPV6_HOP_LIMIT", "9") t.Setenv("GRPC_PORT", "9000") envOverrideLabeling(c, 0) if c.Label != "prod" || c.Tag != "host=foo" || c.GrpcPort != 9000 { t.Errorf("envOverrideLabeling mismatch: %+v", c) } + if c.Ipv4Ttl != 3 || c.Ipv6HopLimit != 9 { + t.Errorf("envOverrideLabeling ttl mismatch: Ipv4Ttl=%d Ipv6HopLimit=%d", c.Ipv4Ttl, c.Ipv6HopLimit) + } if c.Location != "eu-ro-1" || c.Hostname != "runpod435" || !c.ResolveContainerId { t.Errorf("envOverrideLabeling identity mismatch: Location=%q Hostname=%q Resolve=%v", c.Location, c.Hostname, c.ResolveContainerId) } @@ -415,7 +420,7 @@ func TestServePromHandler_bindError(t *testing.T) { } t.Cleanup(func() { fatalf = prev }) - servePromHandler("invalid-host:-1") + servePromHandler("invalid-host:-1", 0, 0) if !strings.Contains(captured, "prometheus error") { t.Errorf("fatalf not invoked; got %q", captured) } @@ -432,7 +437,7 @@ func TestRunMain_version(t *testing.T) { // Stub the prom handler starter so it doesn't bind a port. prevProm := promHandlerStarter - promHandlerStarter = func(_, _ string) {} + promHandlerStarter = func(_, _ string, _, _ uint32) {} t.Cleanup(func() { promHandlerStarter = prevProm }) // runMain spawns a signal-handler goroutine that blocks on signal.Notify. @@ -452,7 +457,7 @@ func TestRunMain_conf(t *testing.T) { t.Cleanup(func() { os.Args = prevArgs }) prevProm := promHandlerStarter - promHandlerStarter = func(_, _ string) {} + promHandlerStarter = func(_, _ string, _, _ uint32) {} t.Cleanup(func() { promHandlerStarter = prevProm }) captureLog(t, func() { @@ -471,7 +476,7 @@ func TestRunMain_stubbedDaemon(t *testing.T) { t.Cleanup(func() { os.Args = prevArgs }) prevProm := promHandlerStarter - promHandlerStarter = func(_, _ string) {} + promHandlerStarter = func(_, _ string, _, _ uint32) {} t.Cleanup(func() { promHandlerStarter = prevProm }) prevDaemon := daemonRunner @@ -500,7 +505,7 @@ func TestInitPromHandler_smoke(t *testing.T) { fatalf = func(string, ...any) {} // swallow t.Cleanup(func() { fatalf = prevFatalf }) - initPromHandler("/metrics", ":0") + initPromHandler("/metrics", ":0", 0, 0) time.Sleep(10 * time.Millisecond) } @@ -773,6 +778,8 @@ func TestBuildConfig(t *testing.T) { label := "lbl" tag := "host=a" gp := uint(8888) + ttl := uint(3) + hop := uint(9) pl := ":9088" pp := "/metrics" gmp := uint(8) @@ -809,6 +816,7 @@ func TestBuildConfig(t *testing.T) { dest: &dst, destWriteFiles: &dwf, topic: &topic, xtcpProtoFile: &xp, kafkaSchemaUrl: &ksu, produceTimeout: &pto, label: &label, tag: &tag, grpcPort: &gp, + ipv4Ttl: &ttl, ipv6HopLimit: &hop, deserializers: &ds, promListen: &pl, promPath: &pp, goMaxProcs: &gmp, profileMode: &pm, v: &v, conf: &conf, d: &d, ioUring: &iu, ioUringRecvBatch: &iurb, ioUringCqeBatch: &iucb, @@ -843,6 +851,8 @@ func TestBuildConfig(t *testing.T) { {"Hostname", c.Hostname, "protoText"}, {"ResolveContainerId", c.ResolveContainerId, true}, {"GrpcPort", c.GrpcPort, uint32(8888)}, + {"Ipv4Ttl", c.Ipv4Ttl, uint32(3)}, + {"Ipv6HopLimit", c.Ipv6HopLimit, uint32(9)}, {"S3SkipBucketProbe", c.S3SkipBucketProbe, true}, } for _, ck := range checks { diff --git a/dart/xtcp_config/v1/xtcp_config.pb.dart b/dart/xtcp_config/v1/xtcp_config.pb.dart index 15688da0..d966b767 100644 --- a/dart/xtcp_config/v1/xtcp_config.pb.dart +++ b/dart/xtcp_config/v1/xtcp_config.pb.dart @@ -373,6 +373,8 @@ class XtcpConfig extends $pb.GeneratedMessage { $core.String? location, $core.String? hostname, $core.bool? resolveContainerId, + $core.int? ipv4Ttl, + $core.int? ipv6HopLimit, $core.int? grpcPort, EnabledDeserializers? enabledDeserializers, $core.bool? ioUring, @@ -501,6 +503,12 @@ class XtcpConfig extends $pb.GeneratedMessage { if (resolveContainerId != null) { $result.resolveContainerId = resolveContainerId; } + if (ipv4Ttl != null) { + $result.ipv4Ttl = ipv4Ttl; + } + if (ipv6HopLimit != null) { + $result.ipv6HopLimit = ipv6HopLimit; + } if (grpcPort != null) { $result.grpcPort = grpcPort; } @@ -566,6 +574,8 @@ class XtcpConfig extends $pb.GeneratedMessage { ..aOS(181, _omitFieldNames ? '' : 'location') ..aOS(182, _omitFieldNames ? '' : 'hostname') ..aOB(183, _omitFieldNames ? '' : 'resolveContainerId') + ..a<$core.int>(184, _omitFieldNames ? '' : 'ipv4Ttl', $pb.PbFieldType.OU3) + ..a<$core.int>(185, _omitFieldNames ? '' : 'ipv6HopLimit', $pb.PbFieldType.OU3) ..a<$core.int>(190, _omitFieldNames ? '' : 'grpcPort', $pb.PbFieldType.OU3) ..aOM(200, _omitFieldNames ? '' : 'enabledDeserializers', subBuilder: EnabledDeserializers.create) ..aOB(210, _omitFieldNames ? '' : 'ioUring') @@ -1098,37 +1108,62 @@ class XtcpConfig extends $pb.GeneratedMessage { @$pb.TagNumber(183) void clearResolveContainerId() => clearField(183); + /// Outgoing IPv4 TTL for xtcp2's own TCP listeners (Prometheus + gRPC). + /// 0 = kernel default. A low value (e.g. 3) keeps replies from travelling + /// far if the host is unexpectedly internet-exposed — the per-listener + /// analogue of the host nftables TTL clamp. Set via -ipv4Ttl / IPV4_TTL. + /// (cf. prometheus/exporter-toolkit#396.) + @$pb.TagNumber(184) + $core.int get ipv4Ttl => $_getIZ(40); + @$pb.TagNumber(184) + set ipv4Ttl($core.int v) { $_setUnsignedInt32(40, v); } + @$pb.TagNumber(184) + $core.bool hasIpv4Ttl() => $_has(40); + @$pb.TagNumber(184) + void clearIpv4Ttl() => clearField(184); + + /// Outgoing IPv6 unicast hop limit for xtcp2's own TCP listeners. 0 = kernel + /// default. Same intent as ipv4_ttl. Set via -ipv6HopLimit / IPV6_HOP_LIMIT. + @$pb.TagNumber(185) + $core.int get ipv6HopLimit => $_getIZ(41); + @$pb.TagNumber(185) + set ipv6HopLimit($core.int v) { $_setUnsignedInt32(41, v); } + @$pb.TagNumber(185) + $core.bool hasIpv6HopLimit() => $_has(41); + @$pb.TagNumber(185) + void clearIpv6HopLimit() => clearField(185); + /// GRPC listening port @$pb.TagNumber(190) - $core.int get grpcPort => $_getIZ(40); + $core.int get grpcPort => $_getIZ(42); @$pb.TagNumber(190) - set grpcPort($core.int v) { $_setUnsignedInt32(40, v); } + set grpcPort($core.int v) { $_setUnsignedInt32(42, v); } @$pb.TagNumber(190) - $core.bool hasGrpcPort() => $_has(40); + $core.bool hasGrpcPort() => $_has(42); @$pb.TagNumber(190) void clearGrpcPort() => clearField(190); @$pb.TagNumber(200) - EnabledDeserializers get enabledDeserializers => $_getN(41); + EnabledDeserializers get enabledDeserializers => $_getN(43); @$pb.TagNumber(200) set enabledDeserializers(EnabledDeserializers v) { setField(200, v); } @$pb.TagNumber(200) - $core.bool hasEnabledDeserializers() => $_has(41); + $core.bool hasEnabledDeserializers() => $_has(43); @$pb.TagNumber(200) void clearEnabledDeserializers() => clearField(200); @$pb.TagNumber(200) - EnabledDeserializers ensureEnabledDeserializers() => $_ensure(41); + EnabledDeserializers ensureEnabledDeserializers() => $_ensure(43); /// When true, route netlink reads and raw-socket destination writes /// through an io_uring ring per Netlinker. Requires Linux 6.1+. /// Library-backed destinations (kafka, nsq, nats, valkey) ignore this /// flag — they continue to use their own client sockets unchanged. @$pb.TagNumber(210) - $core.bool get ioUring => $_getBF(42); + $core.bool get ioUring => $_getBF(44); @$pb.TagNumber(210) - set ioUring($core.bool v) { $_setBool(42, v); } + set ioUring($core.bool v) { $_setBool(44, v); } @$pb.TagNumber(210) - $core.bool hasIoUring() => $_has(42); + $core.bool hasIoUring() => $_has(44); @$pb.TagNumber(210) void clearIoUring() => clearField(210); @@ -1137,11 +1172,11 @@ class XtcpConfig extends $pb.GeneratedMessage { /// many sockets, at the cost of more pinned buffers from packet pool. /// Ignored unless io_uring=true. Default 64. @$pb.TagNumber(211) - $core.int get ioUringRecvBatchSize => $_getIZ(43); + $core.int get ioUringRecvBatchSize => $_getIZ(45); @$pb.TagNumber(211) - set ioUringRecvBatchSize($core.int v) { $_setUnsignedInt32(43, v); } + set ioUringRecvBatchSize($core.int v) { $_setUnsignedInt32(45, v); } @$pb.TagNumber(211) - $core.bool hasIoUringRecvBatchSize() => $_has(43); + $core.bool hasIoUringRecvBatchSize() => $_has(45); @$pb.TagNumber(211) void clearIoUringRecvBatchSize() => clearField(211); @@ -1149,11 +1184,11 @@ class XtcpConfig extends $pb.GeneratedMessage { /// userland loop overhead but increase scheduling latency for the /// netlinker goroutine. Ignored unless io_uring=true. Default 128. @$pb.TagNumber(212) - $core.int get ioUringCqeBatchSize => $_getIZ(44); + $core.int get ioUringCqeBatchSize => $_getIZ(46); @$pb.TagNumber(212) - set ioUringCqeBatchSize($core.int v) { $_setUnsignedInt32(44, v); } + set ioUringCqeBatchSize($core.int v) { $_setUnsignedInt32(46, v); } @$pb.TagNumber(212) - $core.bool hasIoUringCqeBatchSize() => $_has(44); + $core.bool hasIoUringCqeBatchSize() => $_has(46); @$pb.TagNumber(212) void clearIoUringCqeBatchSize() => clearField(212); @@ -1162,11 +1197,11 @@ class XtcpConfig extends $pb.GeneratedMessage { /// "hostname,inetDiagMsgSocketSourcePort,inetDiagMsgState,tcpInfoRtt"). /// Empty = all fields. Ignored by non-tabular marshallers. @$pb.TagNumber(220) - $core.String get csvColumns => $_getSZ(45); + $core.String get csvColumns => $_getSZ(47); @$pb.TagNumber(220) - set csvColumns($core.String v) { $_setString(45, v); } + set csvColumns($core.String v) { $_setString(47, v); } @$pb.TagNumber(220) - $core.bool hasCsvColumns() => $_has(45); + $core.bool hasCsvColumns() => $_has(47); @$pb.TagNumber(220) void clearCsvColumns() => clearField(220); } diff --git a/dart/xtcp_config/v1/xtcp_config.pbjson.dart b/dart/xtcp_config/v1/xtcp_config.pbjson.dart index b8b5a145..6624b5ea 100644 --- a/dart/xtcp_config/v1/xtcp_config.pbjson.dart +++ b/dart/xtcp_config/v1/xtcp_config.pbjson.dart @@ -137,6 +137,8 @@ const XtcpConfig$json = { {'1': 'location', '3': 181, '4': 1, '5': 9, '8': {}, '10': 'location'}, {'1': 'hostname', '3': 182, '4': 1, '5': 9, '8': {}, '10': 'hostname'}, {'1': 'resolve_container_id', '3': 183, '4': 1, '5': 8, '8': {}, '10': 'resolveContainerId'}, + {'1': 'ipv4_ttl', '3': 184, '4': 1, '5': 13, '8': {}, '10': 'ipv4Ttl'}, + {'1': 'ipv6_hop_limit', '3': 185, '4': 1, '5': 13, '8': {}, '10': 'ipv6HopLimit'}, {'1': 'grpc_port', '3': 190, '4': 1, '5': 13, '8': {}, '10': 'grpcPort'}, {'1': 'enabled_deserializers', '3': 200, '4': 1, '5': 11, '6': '.xtcp_config.v1.EnabledDeserializers', '8': {}, '10': 'enabledDeserializers'}, {'1': 'io_uring', '3': 210, '4': 1, '5': 8, '8': {}, '10': 'ioUring'}, @@ -188,15 +190,17 @@ final $typed_data.Uint8List xtcpConfigDescriptor = $convert.base64Decode( 'dGFnGLQBIAEoCUIKukgHyAEAcgIYKFIDdGFnEigKCGxvY2F0aW9uGLUBIAEoCUILukgIyAEAcg' 'MY/QFSCGxvY2F0aW9uEigKCGhvc3RuYW1lGLYBIAEoCUILukgIyAEAcgMY/QFSCGhvc3RuYW1l' 'EjkKFHJlc29sdmVfY29udGFpbmVyX2lkGLcBIAEoCEIGukgDyAEAUhJyZXNvbHZlQ29udGFpbm' - 'VySWQSLAoJZ3JwY19wb3J0GL4BIAEoDUIOukgLyAEBKgYY//8DKAFSCGdycGNQb3J0EmIKFWVu' - 'YWJsZWRfZGVzZXJpYWxpemVycxjIASABKAsyJC54dGNwX2NvbmZpZy52MS5FbmFibGVkRGVzZX' - 'JpYWxpemVyc0IGukgDyAEAUhRlbmFibGVkRGVzZXJpYWxpemVycxIiCghpb191cmluZxjSASAB' - 'KAhCBrpIA8gBAFIHaW9VcmluZxJGChhpb191cmluZ19yZWN2X2JhdGNoX3NpemUY0wEgASgNQg' - '26SArIAQAqBRiAICgBUhRpb1VyaW5nUmVjdkJhdGNoU2l6ZRJEChdpb191cmluZ19jcWVfYmF0' - 'Y2hfc2l6ZRjUASABKA1CDbpICsgBACoFGIAgKAFSE2lvVXJpbmdDcWVCYXRjaFNpemUSKAoLY3' - 'N2X2NvbHVtbnMY3AEgASgJQga6SAPIAQBSCmNzdkNvbHVtbnM6c7pIcBpuCg9YdGNwQ29uZmln' - 'LnBvbGwSMlBvbGwgdGltZW91dCBtdXN0IGJlIGxlc3MgdGhhbiBwb2xsIHBvbGxfZnJlcXVlbm' - 'N5Gid0aGlzLnBvbGxfZnJlcXVlbmN5ID4gdGhpcy5wb2xsX3RpbWVvdXQ='); + 'VySWQSJwoIaXB2NF90dGwYuAEgASgNQgu6SAjIAQAqAxj/AVIHaXB2NFR0bBIyCg5pcHY2X2hv' + 'cF9saW1pdBi5ASABKA1CC7pICMgBACoDGP8BUgxpcHY2SG9wTGltaXQSLAoJZ3JwY19wb3J0GL' + '4BIAEoDUIOukgLyAEBKgYY//8DKAFSCGdycGNQb3J0EmIKFWVuYWJsZWRfZGVzZXJpYWxpemVy' + 'cxjIASABKAsyJC54dGNwX2NvbmZpZy52MS5FbmFibGVkRGVzZXJpYWxpemVyc0IGukgDyAEAUh' + 'RlbmFibGVkRGVzZXJpYWxpemVycxIiCghpb191cmluZxjSASABKAhCBrpIA8gBAFIHaW9Vcmlu' + 'ZxJGChhpb191cmluZ19yZWN2X2JhdGNoX3NpemUY0wEgASgNQg26SArIAQAqBRiAICgBUhRpb1' + 'VyaW5nUmVjdkJhdGNoU2l6ZRJEChdpb191cmluZ19jcWVfYmF0Y2hfc2l6ZRjUASABKA1CDbpI' + 'CsgBACoFGIAgKAFSE2lvVXJpbmdDcWVCYXRjaFNpemUSKAoLY3N2X2NvbHVtbnMY3AEgASgJQg' + 'a6SAPIAQBSCmNzdkNvbHVtbnM6c7pIcBpuCg9YdGNwQ29uZmlnLnBvbGwSMlBvbGwgdGltZW91' + 'dCBtdXN0IGJlIGxlc3MgdGhhbiBwb2xsIHBvbGxfZnJlcXVlbmN5Gid0aGlzLnBvbGxfZnJlcX' + 'VlbmN5ID4gdGhpcy5wb2xsX3RpbWVvdXQ='); @$core.Deprecated('Use enabledDeserializersDescriptor instead') const EnabledDeserializers$json = { diff --git a/docs/output-and-destinations.md b/docs/output-and-destinations.md index 1a90c64e..a5ff04da 100644 --- a/docs/output-and-destinations.md +++ b/docs/output-and-destinations.md @@ -144,6 +144,8 @@ docker logs xtcp2 # header + humanized rows | `-location` | — | Deployment grouping/facility (data center, PoP, region, …) stamped on every record's `location` (`LOCATION`). | | `-hostname` | — | Hostname override stamped on records; defaults to `os.Hostname()`. Required in a container, where `os.Hostname()` returns the container id. `XTCP_HOSTNAME` (NOT `HOSTNAME`). | | `-resolveContainerId` | `false` | Resolve each socket's owning container id/runtime from its cgroup into `container_id`/`container_runtime` (`CONTAINER_ID_RESOLVE`). Needs `/sys/fs/cgroup` (mount it + `--cgroupns=host` in a container). | +| `-ipv4Ttl` | `0` | Outgoing IPv4 TTL on xtcp2's TCP listeners (Prometheus + gRPC); `0` = kernel default. A low value keeps replies from travelling far if the host is internet-exposed (`IPV4_TTL`). | +| `-ipv6HopLimit` | `0` | Outgoing IPv6 unicast hop limit on xtcp2's TCP listeners; `0` = kernel default (`IPV6_HOP_LIMIT`). | | `-s3Endpoint` | — | S3 endpoint URL (or `S3_ENDPOINT`). | | `-s3Bucket` | — | S3 bucket (or `S3_BUCKET`); must already exist. | | `-s3Prefix` | — | Key prefix within the bucket. | diff --git a/gen/xtcp_config/v1/xtcp_config.pb.cc b/gen/xtcp_config/v1/xtcp_config.pb.cc index 7c4d44ec..0d807579 100644 --- a/gen/xtcp_config/v1/xtcp_config.pb.cc +++ b/gen/xtcp_config/v1/xtcp_config.pb.cc @@ -200,6 +200,8 @@ inline constexpr XtcpConfig::Impl_::Impl_( s3_skip_bucket_probe_{false}, resolve_container_id_{false}, io_uring_{false}, + ipv4_ttl_{0u}, + ipv6_hop_limit_{0u}, grpc_port_{0u}, io_uring_recv_batch_size_{0u}, io_uring_cqe_batch_size_{0u} {} @@ -440,6 +442,8 @@ const ::uint32_t PROTOBUF_FIELD_OFFSET(::xtcp_config::v1::XtcpConfig, _impl_.location_), PROTOBUF_FIELD_OFFSET(::xtcp_config::v1::XtcpConfig, _impl_.hostname_), PROTOBUF_FIELD_OFFSET(::xtcp_config::v1::XtcpConfig, _impl_.resolve_container_id_), + PROTOBUF_FIELD_OFFSET(::xtcp_config::v1::XtcpConfig, _impl_.ipv4_ttl_), + PROTOBUF_FIELD_OFFSET(::xtcp_config::v1::XtcpConfig, _impl_.ipv6_hop_limit_), PROTOBUF_FIELD_OFFSET(::xtcp_config::v1::XtcpConfig, _impl_.grpc_port_), PROTOBUF_FIELD_OFFSET(::xtcp_config::v1::XtcpConfig, _impl_.enabled_deserializers_), PROTOBUF_FIELD_OFFSET(::xtcp_config::v1::XtcpConfig, _impl_.io_uring_), @@ -487,6 +491,8 @@ const ::uint32_t ~0u, ~0u, ~0u, + ~0u, + ~0u, 3, ~0u, ~0u, @@ -523,9 +529,9 @@ static const ::_pbi::MigrationSchema {28, 37, -1, sizeof(::xtcp_config::v1::SetResponse)}, {38, 48, -1, sizeof(::xtcp_config::v1::SetPollFrequencyRequest)}, {50, 59, -1, sizeof(::xtcp_config::v1::SetPollFrequencyResponse)}, - {60, 114, -1, sizeof(::xtcp_config::v1::XtcpConfig)}, - {160, 170, -1, sizeof(::xtcp_config::v1::EnabledDeserializers_EnabledEntry_DoNotUse)}, - {172, -1, -1, sizeof(::xtcp_config::v1::EnabledDeserializers)}, + {60, 116, -1, sizeof(::xtcp_config::v1::XtcpConfig)}, + {164, 174, -1, sizeof(::xtcp_config::v1::EnabledDeserializers_EnabledEntry_DoNotUse)}, + {176, -1, -1, sizeof(::xtcp_config::v1::EnabledDeserializers)}, }; static const ::_pb::Message* const file_default_instances[] = { &::xtcp_config::v1::_GetRequest_default_instance_._instance, @@ -558,7 +564,7 @@ const char descriptor_table_protodef_xtcp_5fconfig_2fv1_2fxtcp_5fconfig_2eproto[ " than poll poll_frequency\032\'this.poll_tim" "eout < this.poll_frequency\"N\n\030SetPollFre" "quencyResponse\0222\n\006config\030\001 \001(\0132\032.xtcp_co" - "nfig.v1.XtcpConfigR\006config\"\333\024\n\nXtcpConfi" + "nfig.v1.XtcpConfigR\006config\"\270\025\n\nXtcpConfi" "g\022F\n\027nl_timeout_milliseconds\030\n \001(\004B\016\272H\0132" "\006\030\240\215\006(\000\310\001\001R\025nlTimeoutMilliseconds\022S\n\016pol" "l_frequency\030\024 \001(\0132\031.google.protobuf.Dura" @@ -612,36 +618,39 @@ const char descriptor_table_protodef_xtcp_5fconfig_2fv1_2fxtcp_5fconfig_2eproto[ "H\007r\002\030(\310\001\000R\003tag\022(\n\010location\030\265\001 \001(\tB\013\272H\010r\003" "\030\375\001\310\001\000R\010location\022(\n\010hostname\030\266\001 \001(\tB\013\272H\010" "r\003\030\375\001\310\001\000R\010hostname\0229\n\024resolve_container_" - "id\030\267\001 \001(\010B\006\272H\003\310\001\000R\022resolveContainerId\022,\n" - "\tgrpc_port\030\276\001 \001(\rB\016\272H\013*\006\030\377\377\003(\001\310\001\001R\010grpcP" - "ort\022b\n\025enabled_deserializers\030\310\001 \001(\0132$.xt" - "cp_config.v1.EnabledDeserializersB\006\272H\003\310\001" - "\000R\024enabledDeserializers\022\"\n\010io_uring\030\322\001 \001" - "(\010B\006\272H\003\310\001\000R\007ioUring\022F\n\030io_uring_recv_bat" - "ch_size\030\323\001 \001(\rB\r\272H\n*\005\030\200 (\001\310\001\000R\024ioUringRe" - "cvBatchSize\022D\n\027io_uring_cqe_batch_size\030\324" - "\001 \001(\rB\r\272H\n*\005\030\200 (\001\310\001\000R\023ioUringCqeBatchSiz" - "e\022(\n\013csv_columns\030\334\001 \001(\tB\006\272H\003\310\001\000R\ncsvColu" - "mns:s\272Hp\032n\n\017XtcpConfig.poll\0222Poll timeou" - "t must be less than poll poll_frequency\032" - "\'this.poll_frequency > this.poll_timeout" - "\"\237\001\n\024EnabledDeserializers\022K\n\007enabled\030\001 \003" - "(\01321.xtcp_config.v1.EnabledDeserializers" - ".EnabledEntryR\007enabled\032:\n\014EnabledEntry\022\020" - "\n\003key\030\001 \001(\tR\003key\022\024\n\005value\030\002 \001(\010R\005value:\002" - "8\0012\341\002\n\rConfigService\022]\n\003Get\022\032.xtcp_confi" - "g.v1.GetRequest\032\033.xtcp_config.v1.GetResp" - "onse\"\035\202\323\344\223\002\027\032\022/ConfigService/Get:\001*\022]\n\003S" - "et\022\032.xtcp_config.v1.SetRequest\032\033.xtcp_co" - "nfig.v1.SetResponse\"\035\202\323\344\223\002\027\032\022/ConfigServ" - "ice/Set:\001*\022\221\001\n\020SetPollFrequency\022\'.xtcp_c" - "onfig.v1.SetPollFrequencyRequest\032(.xtcp_" - "config.v1.SetPollFrequencyResponse\"*\202\323\344\223" - "\002$\032\037/ConfigService/SetPollFrequency:\001*B\215" - "\001\n\022com.xtcp_config.v1B\017XtcpConfigProtoP\001" - "Z\021./pkg/xtcp_config\242\002\003XXX\252\002\rXtcpConfig.V" - "1\312\002\rXtcpConfig\\V1\342\002\031XtcpConfig\\V1\\GPBMet" - "adata\352\002\016XtcpConfig::V1b\006proto3" + "id\030\267\001 \001(\010B\006\272H\003\310\001\000R\022resolveContainerId\022\'\n" + "\010ipv4_ttl\030\270\001 \001(\rB\013\272H\010*\003\030\377\001\310\001\000R\007ipv4Ttl\0222" + "\n\016ipv6_hop_limit\030\271\001 \001(\rB\013\272H\010*\003\030\377\001\310\001\000R\014ip" + "v6HopLimit\022,\n\tgrpc_port\030\276\001 \001(\rB\016\272H\013*\006\030\377\377" + "\003(\001\310\001\001R\010grpcPort\022b\n\025enabled_deserializer" + "s\030\310\001 \001(\0132$.xtcp_config.v1.EnabledDeseria" + "lizersB\006\272H\003\310\001\000R\024enabledDeserializers\022\"\n\010" + "io_uring\030\322\001 \001(\010B\006\272H\003\310\001\000R\007ioUring\022F\n\030io_u" + "ring_recv_batch_size\030\323\001 \001(\rB\r\272H\n*\005\030\200 (\001\310" + "\001\000R\024ioUringRecvBatchSize\022D\n\027io_uring_cqe" + "_batch_size\030\324\001 \001(\rB\r\272H\n*\005\030\200 (\001\310\001\000R\023ioUri" + "ngCqeBatchSize\022(\n\013csv_columns\030\334\001 \001(\tB\006\272H" + "\003\310\001\000R\ncsvColumns:s\272Hp\032n\n\017XtcpConfig.poll" + "\0222Poll timeout must be less than poll po" + "ll_frequency\032\'this.poll_frequency > this" + ".poll_timeout\"\237\001\n\024EnabledDeserializers\022K" + "\n\007enabled\030\001 \003(\01321.xtcp_config.v1.Enabled" + "Deserializers.EnabledEntryR\007enabled\032:\n\014E" + "nabledEntry\022\020\n\003key\030\001 \001(\tR\003key\022\024\n\005value\030\002" + " \001(\010R\005value:\0028\0012\341\002\n\rConfigService\022]\n\003Get" + "\022\032.xtcp_config.v1.GetRequest\032\033.xtcp_conf" + "ig.v1.GetResponse\"\035\202\323\344\223\002\027\032\022/ConfigServic" + "e/Get:\001*\022]\n\003Set\022\032.xtcp_config.v1.SetRequ" + "est\032\033.xtcp_config.v1.SetResponse\"\035\202\323\344\223\002\027" + "\032\022/ConfigService/Set:\001*\022\221\001\n\020SetPollFrequ" + "ency\022\'.xtcp_config.v1.SetPollFrequencyRe" + "quest\032(.xtcp_config.v1.SetPollFrequencyR" + "esponse\"*\202\323\344\223\002$\032\037/ConfigService/SetPollF" + "requency:\001*B\215\001\n\022com.xtcp_config.v1B\017Xtcp" + "ConfigProtoP\001Z\021./pkg/xtcp_config\242\002\003XXX\252\002" + "\rXtcpConfig.V1\312\002\rXtcpConfig\\V1\342\002\031XtcpCon" + "fig\\V1\\GPBMetadata\352\002\016XtcpConfig::V1b\006pro" + "to3" }; static const ::_pbi::DescriptorTable* const descriptor_table_xtcp_5fconfig_2fv1_2fxtcp_5fconfig_2eproto_deps[3] = { @@ -653,7 +662,7 @@ static ::absl::once_flag descriptor_table_xtcp_5fconfig_2fv1_2fxtcp_5fconfig_2ep PROTOBUF_CONSTINIT const ::_pbi::DescriptorTable descriptor_table_xtcp_5fconfig_2fv1_2fxtcp_5fconfig_2eproto = { false, false, - 4070, + 4163, descriptor_table_protodef_xtcp_5fconfig_2fv1_2fxtcp_5fconfig_2eproto, "xtcp_config/v1/xtcp_config.proto", &descriptor_table_xtcp_5fconfig_2fv1_2fxtcp_5fconfig_2eproto_once, @@ -2285,7 +2294,7 @@ const ::google::protobuf::internal::ClassData* XtcpConfig::GetClassData() const return _class_data_.base(); } PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 -const ::_pbi::TcParseTable<5, 46, 4, 283, 29> XtcpConfig::_table_ = { +const ::_pbi::TcParseTable<5, 48, 4, 291, 29> XtcpConfig::_table_ = { { PROTOBUF_FIELD_OFFSET(XtcpConfig, _impl_._has_bits_), 0, // no _extensions_ @@ -2293,7 +2302,7 @@ const ::_pbi::TcParseTable<5, 46, 4, 283, 29> XtcpConfig::_table_ = { offsetof(decltype(_table_), field_lookup_table), 3757571583, // skipmap offsetof(decltype(_table_), field_entries), - 46, // num_field_entries + 48, // num_field_entries 4, // num_aux_entries offsetof(decltype(_table_), aux_entries), _class_data_.base(), @@ -2372,7 +2381,7 @@ const ::_pbi::TcParseTable<5, 46, 4, 283, 29> XtcpConfig::_table_ = { }}, {{ 40, 0, 12, 62462, 3, 49135, 6, 65279, 8, 61435, 9, 65471, 11, 2050, 12, - 48480, 26, 65279, 34, 4091, 35, 65471, 40, 58366, 41, 65519, 45, + 48480, 26, 65279, 34, 4091, 35, 65468, 40, 58366, 43, 65519, 47, 65535, 65535 }}, {{ // uint64 nl_timeout_milliseconds = 10 [json_name = "nlTimeoutMilliseconds", (.buf.validate.field) = { @@ -2495,6 +2504,12 @@ const ::_pbi::TcParseTable<5, 46, 4, 283, 29> XtcpConfig::_table_ = { // bool resolve_container_id = 183 [json_name = "resolveContainerId", (.buf.validate.field) = { {PROTOBUF_FIELD_OFFSET(XtcpConfig, _impl_.resolve_container_id_), -1, 0, (0 | ::_fl::kFcSingular | ::_fl::kBool)}, + // uint32 ipv4_ttl = 184 [json_name = "ipv4Ttl", (.buf.validate.field) = { + {PROTOBUF_FIELD_OFFSET(XtcpConfig, _impl_.ipv4_ttl_), -1, 0, + (0 | ::_fl::kFcSingular | ::_fl::kUInt32)}, + // uint32 ipv6_hop_limit = 185 [json_name = "ipv6HopLimit", (.buf.validate.field) = { + {PROTOBUF_FIELD_OFFSET(XtcpConfig, _impl_.ipv6_hop_limit_), -1, 0, + (0 | ::_fl::kFcSingular | ::_fl::kUInt32)}, // uint32 grpc_port = 190 [json_name = "grpcPort", (.buf.validate.field) = { {PROTOBUF_FIELD_OFFSET(XtcpConfig, _impl_.grpc_port_), -1, 0, (0 | ::_fl::kFcSingular | ::_fl::kUInt32)}, @@ -2519,7 +2534,7 @@ const ::_pbi::TcParseTable<5, 46, 4, 283, 29> XtcpConfig::_table_ = { {::_pbi::TcParser::GetTable<::google::protobuf::Duration>()}, {::_pbi::TcParser::GetTable<::xtcp_config::v1::EnabledDeserializers>()}, }}, {{ - "\31\0\0\0\0\0\0\0\0\0\0\14\0\12\0\0\21\13\11\11\15\15\4\0\11\0\0\15\22\0\0\5\17\20\0\0\5\3\10\10\0\0\0\0\0\0\13\0" + "\31\0\0\0\0\0\0\0\0\0\0\14\0\12\0\0\21\13\11\11\15\15\4\0\11\0\0\15\22\0\0\5\17\20\0\0\5\3\10\10\0\0\0\0\0\0\0\0\13\0\0\0\0\0\0\0" "xtcp_config.v1.XtcpConfig" "capture_path" "marshal_to" @@ -2912,6 +2927,20 @@ PROTOBUF_NOINLINE void XtcpConfig::Clear() { 183, this_._internal_resolve_container_id(), target); } + // uint32 ipv4_ttl = 184 [json_name = "ipv4Ttl", (.buf.validate.field) = { + if (this_._internal_ipv4_ttl() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray( + 184, this_._internal_ipv4_ttl(), target); + } + + // uint32 ipv6_hop_limit = 185 [json_name = "ipv6HopLimit", (.buf.validate.field) = { + if (this_._internal_ipv6_hop_limit() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray( + 185, this_._internal_ipv6_hop_limit(), target); + } + // uint32 grpc_port = 190 [json_name = "grpcPort", (.buf.validate.field) = { if (this_._internal_grpc_port() != 0) { target = stream->EnsureSpace(target); @@ -3197,6 +3226,16 @@ PROTOBUF_NOINLINE void XtcpConfig::Clear() { if (this_._internal_io_uring() != 0) { total_size += 3; } + // uint32 ipv4_ttl = 184 [json_name = "ipv4Ttl", (.buf.validate.field) = { + if (this_._internal_ipv4_ttl() != 0) { + total_size += 2 + ::_pbi::WireFormatLite::UInt32Size( + this_._internal_ipv4_ttl()); + } + // uint32 ipv6_hop_limit = 185 [json_name = "ipv6HopLimit", (.buf.validate.field) = { + if (this_._internal_ipv6_hop_limit() != 0) { + total_size += 2 + ::_pbi::WireFormatLite::UInt32Size( + this_._internal_ipv6_hop_limit()); + } // uint32 grpc_port = 190 [json_name = "grpcPort", (.buf.validate.field) = { if (this_._internal_grpc_port() != 0) { total_size += 2 + ::_pbi::WireFormatLite::UInt32Size( @@ -3382,6 +3421,12 @@ void XtcpConfig::MergeImpl(::google::protobuf::MessageLite& to_msg, const ::goog if (from._internal_io_uring() != 0) { _this->_impl_.io_uring_ = from._impl_.io_uring_; } + if (from._internal_ipv4_ttl() != 0) { + _this->_impl_.ipv4_ttl_ = from._impl_.ipv4_ttl_; + } + if (from._internal_ipv6_hop_limit() != 0) { + _this->_impl_.ipv6_hop_limit_ = from._impl_.ipv6_hop_limit_; + } if (from._internal_grpc_port() != 0) { _this->_impl_.grpc_port_ = from._impl_.grpc_port_; } diff --git a/gen/xtcp_config/v1/xtcp_config.pb.h b/gen/xtcp_config/v1/xtcp_config.pb.h index 66d4c5ed..98ea7f8f 100644 --- a/gen/xtcp_config/v1/xtcp_config.pb.h +++ b/gen/xtcp_config/v1/xtcp_config.pb.h @@ -886,6 +886,8 @@ class XtcpConfig final : public ::google::protobuf::Message kS3SkipBucketProbeFieldNumber = 134, kResolveContainerIdFieldNumber = 183, kIoUringFieldNumber = 210, + kIpv4TtlFieldNumber = 184, + kIpv6HopLimitFieldNumber = 185, kGrpcPortFieldNumber = 190, kIoUringRecvBatchSizeFieldNumber = 211, kIoUringCqeBatchSizeFieldNumber = 212, @@ -1459,6 +1461,26 @@ class XtcpConfig final : public ::google::protobuf::Message bool _internal_io_uring() const; void _internal_set_io_uring(bool value); + public: + // uint32 ipv4_ttl = 184 [json_name = "ipv4Ttl", (.buf.validate.field) = { + void clear_ipv4_ttl() ; + ::uint32_t ipv4_ttl() const; + void set_ipv4_ttl(::uint32_t value); + + private: + ::uint32_t _internal_ipv4_ttl() const; + void _internal_set_ipv4_ttl(::uint32_t value); + + public: + // uint32 ipv6_hop_limit = 185 [json_name = "ipv6HopLimit", (.buf.validate.field) = { + void clear_ipv6_hop_limit() ; + ::uint32_t ipv6_hop_limit() const; + void set_ipv6_hop_limit(::uint32_t value); + + private: + ::uint32_t _internal_ipv6_hop_limit() const; + void _internal_set_ipv6_hop_limit(::uint32_t value); + public: // uint32 grpc_port = 190 [json_name = "grpcPort", (.buf.validate.field) = { void clear_grpc_port() ; @@ -1495,8 +1517,8 @@ class XtcpConfig final : public ::google::protobuf::Message class _Internal; friend class ::google::protobuf::internal::TcParser; static const ::google::protobuf::internal::TcParseTable< - 5, 46, 4, - 283, 29> + 5, 48, 4, + 291, 29> _table_; friend class ::google::protobuf::MessageLite; @@ -1558,6 +1580,8 @@ class XtcpConfig final : public ::google::protobuf::Message bool s3_skip_bucket_probe_; bool resolve_container_id_; bool io_uring_; + ::uint32_t ipv4_ttl_; + ::uint32_t ipv6_hop_limit_; ::uint32_t grpc_port_; ::uint32_t io_uring_recv_batch_size_; ::uint32_t io_uring_cqe_batch_size_; @@ -4538,6 +4562,50 @@ inline void XtcpConfig::_internal_set_resolve_container_id(bool value) { _impl_.resolve_container_id_ = value; } +// uint32 ipv4_ttl = 184 [json_name = "ipv4Ttl", (.buf.validate.field) = { +inline void XtcpConfig::clear_ipv4_ttl() { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.ipv4_ttl_ = 0u; +} +inline ::uint32_t XtcpConfig::ipv4_ttl() const { + // @@protoc_insertion_point(field_get:xtcp_config.v1.XtcpConfig.ipv4_ttl) + return _internal_ipv4_ttl(); +} +inline void XtcpConfig::set_ipv4_ttl(::uint32_t value) { + _internal_set_ipv4_ttl(value); + // @@protoc_insertion_point(field_set:xtcp_config.v1.XtcpConfig.ipv4_ttl) +} +inline ::uint32_t XtcpConfig::_internal_ipv4_ttl() const { + ::google::protobuf::internal::TSanRead(&_impl_); + return _impl_.ipv4_ttl_; +} +inline void XtcpConfig::_internal_set_ipv4_ttl(::uint32_t value) { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.ipv4_ttl_ = value; +} + +// uint32 ipv6_hop_limit = 185 [json_name = "ipv6HopLimit", (.buf.validate.field) = { +inline void XtcpConfig::clear_ipv6_hop_limit() { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.ipv6_hop_limit_ = 0u; +} +inline ::uint32_t XtcpConfig::ipv6_hop_limit() const { + // @@protoc_insertion_point(field_get:xtcp_config.v1.XtcpConfig.ipv6_hop_limit) + return _internal_ipv6_hop_limit(); +} +inline void XtcpConfig::set_ipv6_hop_limit(::uint32_t value) { + _internal_set_ipv6_hop_limit(value); + // @@protoc_insertion_point(field_set:xtcp_config.v1.XtcpConfig.ipv6_hop_limit) +} +inline ::uint32_t XtcpConfig::_internal_ipv6_hop_limit() const { + ::google::protobuf::internal::TSanRead(&_impl_); + return _impl_.ipv6_hop_limit_; +} +inline void XtcpConfig::_internal_set_ipv6_hop_limit(::uint32_t value) { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.ipv6_hop_limit_ = value; +} + // uint32 grpc_port = 190 [json_name = "grpcPort", (.buf.validate.field) = { inline void XtcpConfig::clear_grpc_port() { ::google::protobuf::internal::TSanWrite(&_impl_); diff --git a/gen/xtcp_config/v1/xtcp_config.pb.validate.cc b/gen/xtcp_config/v1/xtcp_config.pb.validate.cc index cdc56abb..8052a0c4 100644 --- a/gen/xtcp_config/v1/xtcp_config.pb.validate.cc +++ b/gen/xtcp_config/v1/xtcp_config.pb.validate.cc @@ -1104,6 +1104,40 @@ return false; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1187,7 +1221,7 @@ return false; } } -// no validation rules for debug_level// no validation rules for label// no validation rules for tag// no validation rules for location// no validation rules for hostname// no validation rules for resolve_container_id// no validation rules for grpc_port +// no validation rules for debug_level// no validation rules for label// no validation rules for tag// no validation rules for location// no validation rules for hostname// no validation rules for resolve_container_id// no validation rules for ipv4_ttl// no validation rules for ipv6_hop_limit// no validation rules for grpc_port diff --git a/pkg/ipsockopt/ipsockopt.go b/pkg/ipsockopt/ipsockopt.go new file mode 100644 index 00000000..6f59466e --- /dev/null +++ b/pkg/ipsockopt/ipsockopt.go @@ -0,0 +1,50 @@ +// Package ipsockopt clamps the outgoing IPv4 TTL / IPv6 unicast hop limit on a +// listening socket, so a listener's replies can't travel far if the host is +// unexpectedly internet-exposed. It is the per-listener analogue of a host-level +// nftables TTL clamp, and mirrors prometheus/exporter-toolkit#396. +// +// The option is set on the listening socket via net.ListenConfig.Control (before +// bind); the kernel then inherits it onto every accepted connection. +package ipsockopt + +import ( + "errors" + "syscall" + + "golang.org/x/sys/unix" +) + +// Control returns a net.ListenConfig.Control callback that sets IP_TTL (IPv4) +// and/or IPV6_UNICAST_HOPS (IPv6) on the listening socket. A zero value leaves +// the kernel default for that family; if both are zero it returns nil so the +// caller keeps the default (no Control hook at all). +// +// setsockopt is attempted for whichever values are non-zero; ENOPROTOOPT (the +// option not applying to the socket's address family — e.g. IP_TTL on an +// IPv6-only socket) is ignored, matching exporter-toolkit#396. +func Control(ipv4TTL, ipv6HopLimit uint32) func(network, address string, c syscall.RawConn) error { + if ipv4TTL == 0 && ipv6HopLimit == 0 { + return nil + } + return func(_, _ string, c syscall.RawConn) error { + var setErr error + ctrlErr := c.Control(func(fd uintptr) { + if ipv4TTL > 0 { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TTL, int(ipv4TTL)); err != nil && !errors.Is(err, unix.ENOPROTOOPT) { + setErr = err + return + } + } + if ipv6HopLimit > 0 { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, int(ipv6HopLimit)); err != nil && !errors.Is(err, unix.ENOPROTOOPT) { + setErr = err + return + } + } + }) + if ctrlErr != nil { + return ctrlErr + } + return setErr + } +} diff --git a/pkg/ipsockopt/ipsockopt_test.go b/pkg/ipsockopt/ipsockopt_test.go new file mode 100644 index 00000000..5113e5d3 --- /dev/null +++ b/pkg/ipsockopt/ipsockopt_test.go @@ -0,0 +1,70 @@ +package ipsockopt + +import ( + "context" + "net" + "testing" + + "golang.org/x/sys/unix" +) + +func TestControl_nilWhenUnset(t *testing.T) { + if Control(0, 0) != nil { + t.Fatal("Control(0,0) should be nil so the kernel default is kept") + } + if Control(3, 0) == nil || Control(0, 3) == nil { + t.Fatal("Control with a non-zero value should return a callback") + } +} + +// TestControl_setsIPv4TTL binds a real IPv4 listener through the Control hook +// and reads the TTL back with getsockopt to prove it was applied. +func TestControl_setsIPv4TTL(t *testing.T) { + const ttl = 7 + lc := net.ListenConfig{Control: Control(ttl, 0)} + ln, err := lc.Listen(context.Background(), "tcp4", "127.0.0.1:0") + if err != nil { + t.Fatalf("listen: %v", err) + } + defer ln.Close() + + got := getsockoptInt(t, ln, unix.IPPROTO_IP, unix.IP_TTL) + if got != ttl { + t.Fatalf("IP_TTL = %d, want %d", got, ttl) + } +} + +// TestControl_setsIPv6HopLimit does the same for IPv6 unicast hops. +func TestControl_setsIPv6HopLimit(t *testing.T) { + const hops = 5 + lc := net.ListenConfig{Control: Control(0, hops)} + ln, err := lc.Listen(context.Background(), "tcp6", "[::1]:0") + if err != nil { + t.Skipf("IPv6 listen unavailable: %v", err) + } + defer ln.Close() + + got := getsockoptInt(t, ln, unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS) + if got != hops { + t.Fatalf("IPV6_UNICAST_HOPS = %d, want %d", got, hops) + } +} + +func getsockoptInt(t *testing.T, ln net.Listener, level, opt int) int { + t.Helper() + rc, err := ln.(*net.TCPListener).SyscallConn() + if err != nil { + t.Fatalf("SyscallConn: %v", err) + } + var val int + var gErr error + if cErr := rc.Control(func(fd uintptr) { + val, gErr = unix.GetsockoptInt(int(fd), level, opt) + }); cErr != nil { + t.Fatalf("control: %v", cErr) + } + if gErr != nil { + t.Fatalf("getsockopt: %v", gErr) + } + return val +} diff --git a/pkg/xtcp/grpc_server.go b/pkg/xtcp/grpc_server.go index 99f71a3d..85b1c663 100644 --- a/pkg/xtcp/grpc_server.go +++ b/pkg/xtcp/grpc_server.go @@ -8,6 +8,7 @@ import ( "net" "time" + "github.com/randomizedcoder/xtcp2/pkg/ipsockopt" "github.com/randomizedcoder/xtcp2/pkg/xtcp_config" "github.com/randomizedcoder/xtcp2/pkg/xtcp_flat_record" "google.golang.org/grpc" @@ -39,7 +40,9 @@ const ( func (x *XTCP) startGRPCflatRecordService(ctx context.Context) { - var lc net.ListenConfig + // Clamp the IPv4 TTL / IPv6 hop limit on the gRPC listener too (0 = kernel + // default), so its replies can't travel far if the host is internet-exposed. + lc := net.ListenConfig{Control: ipsockopt.Control(x.config.Ipv4Ttl, x.config.Ipv6HopLimit)} lis, err := lc.Listen(ctx, "tcp", fmt.Sprintf(":%d", x.config.GrpcPort)) if err != nil { log.Fatalf("failed to listen: %v", err) diff --git a/pkg/xtcp_config/xtcp_config.pb.go b/pkg/xtcp_config/xtcp_config.pb.go index 8e26a953..9a264cf6 100644 --- a/pkg/xtcp_config/xtcp_config.pb.go +++ b/pkg/xtcp_config/xtcp_config.pb.go @@ -484,6 +484,15 @@ type XtcpConfig struct { // flag or CONTAINER_ID_RESOLVE env. Needs /sys/fs/cgroup readable (mount it // and run --cgroupns=host in a container). ResolveContainerId bool `protobuf:"varint,183,opt,name=resolve_container_id,json=resolveContainerId,proto3" json:"resolve_container_id,omitempty"` + // Outgoing IPv4 TTL for xtcp2's own TCP listeners (Prometheus + gRPC). + // 0 = kernel default. A low value (e.g. 3) keeps replies from travelling + // far if the host is unexpectedly internet-exposed — the per-listener + // analogue of the host nftables TTL clamp. Set via -ipv4Ttl / IPV4_TTL. + // (cf. prometheus/exporter-toolkit#396.) + Ipv4Ttl uint32 `protobuf:"varint,184,opt,name=ipv4_ttl,json=ipv4Ttl,proto3" json:"ipv4_ttl,omitempty"` + // Outgoing IPv6 unicast hop limit for xtcp2's own TCP listeners. 0 = kernel + // default. Same intent as ipv4_ttl. Set via -ipv6HopLimit / IPV6_HOP_LIMIT. + Ipv6HopLimit uint32 `protobuf:"varint,185,opt,name=ipv6_hop_limit,json=ipv6HopLimit,proto3" json:"ipv6_hop_limit,omitempty"` // GRPC listening port GrpcPort uint32 `protobuf:"varint,190,opt,name=grpc_port,json=grpcPort,proto3" json:"grpc_port,omitempty"` EnabledDeserializers *EnabledDeserializers `protobuf:"bytes,200,opt,name=enabled_deserializers,json=enabledDeserializers,proto3" json:"enabled_deserializers,omitempty"` @@ -820,6 +829,20 @@ func (x *XtcpConfig) GetResolveContainerId() bool { return false } +func (x *XtcpConfig) GetIpv4Ttl() uint32 { + if x != nil { + return x.Ipv4Ttl + } + return 0 +} + +func (x *XtcpConfig) GetIpv6HopLimit() uint32 { + if x != nil { + return x.Ipv6HopLimit + } + return 0 +} + func (x *XtcpConfig) GetGrpcPort() uint32 { if x != nil { return x.GrpcPort @@ -925,7 +948,7 @@ const file_xtcp_config_v1_xtcp_config_proto_rawDesc = "" + "\fpoll_timeout\x18\x1e \x01(\v2\x19.google.protobuf.DurationB\x11\xbaH\x0e\xc8\x01\x01\xaa\x01\b\"\x04\b\x80\xf5$2\x00R\vpollTimeout:s\xbaHp\x1an\n" + "\x0fXtcpConfig.poll\x122Poll timeout must be less than poll poll_frequency\x1a'this.poll_timeout < this.poll_frequency\"N\n" + "\x18SetPollFrequencyResponse\x122\n" + - "\x06config\x18\x01 \x01(\v2\x1a.xtcp_config.v1.XtcpConfigR\x06config\"\xdb\x14\n" + + "\x06config\x18\x01 \x01(\v2\x1a.xtcp_config.v1.XtcpConfigR\x06config\"\xb8\x15\n" + "\n" + "XtcpConfig\x12F\n" + "\x17nl_timeout_milliseconds\x18\n" + @@ -983,7 +1006,9 @@ const file_xtcp_config_v1_xtcp_config_proto_rawDesc = "" + "\xbaH\a\xc8\x01\x00r\x02\x18(R\x03tag\x12(\n" + "\blocation\x18\xb5\x01 \x01(\tB\v\xbaH\b\xc8\x01\x00r\x03\x18\xfd\x01R\blocation\x12(\n" + "\bhostname\x18\xb6\x01 \x01(\tB\v\xbaH\b\xc8\x01\x00r\x03\x18\xfd\x01R\bhostname\x129\n" + - "\x14resolve_container_id\x18\xb7\x01 \x01(\bB\x06\xbaH\x03\xc8\x01\x00R\x12resolveContainerId\x12,\n" + + "\x14resolve_container_id\x18\xb7\x01 \x01(\bB\x06\xbaH\x03\xc8\x01\x00R\x12resolveContainerId\x12'\n" + + "\bipv4_ttl\x18\xb8\x01 \x01(\rB\v\xbaH\b\xc8\x01\x00*\x03\x18\xff\x01R\aipv4Ttl\x122\n" + + "\x0eipv6_hop_limit\x18\xb9\x01 \x01(\rB\v\xbaH\b\xc8\x01\x00*\x03\x18\xff\x01R\fipv6HopLimit\x12,\n" + "\tgrpc_port\x18\xbe\x01 \x01(\rB\x0e\xbaH\v\xc8\x01\x01*\x06\x18\xff\xff\x03(\x01R\bgrpcPort\x12b\n" + "\x15enabled_deserializers\x18\xc8\x01 \x01(\v2$.xtcp_config.v1.EnabledDeserializersB\x06\xbaH\x03\xc8\x01\x00R\x14enabledDeserializers\x12\"\n" + "\bio_uring\x18\xd2\x01 \x01(\bB\x06\xbaH\x03\xc8\x01\x00R\aioUring\x12F\n" + diff --git a/pkg/xtcp_config/xtcp_config_vtproto.pb.go b/pkg/xtcp_config/xtcp_config_vtproto.pb.go index 261465cb..8154b0e9 100644 --- a/pkg/xtcp_config/xtcp_config_vtproto.pb.go +++ b/pkg/xtcp_config/xtcp_config_vtproto.pb.go @@ -366,6 +366,20 @@ func (m *XtcpConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i-- dAtA[i] = 0xf0 } + if m.Ipv6HopLimit != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ipv6HopLimit)) + i-- + dAtA[i] = 0xb + i-- + dAtA[i] = 0xc8 + } + if m.Ipv4Ttl != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ipv4Ttl)) + i-- + dAtA[i] = 0xb + i-- + dAtA[i] = 0xc0 + } if m.ResolveContainerId { i-- if m.ResolveContainerId { @@ -997,6 +1011,12 @@ func (m *XtcpConfig) SizeVT() (n int) { if m.ResolveContainerId { n += 3 } + if m.Ipv4Ttl != 0 { + n += 2 + protohelpers.SizeOfVarint(uint64(m.Ipv4Ttl)) + } + if m.Ipv6HopLimit != 0 { + n += 2 + protohelpers.SizeOfVarint(uint64(m.Ipv6HopLimit)) + } if m.GrpcPort != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.GrpcPort)) } @@ -2650,6 +2670,44 @@ func (m *XtcpConfig) UnmarshalVT(dAtA []byte) error { } } m.ResolveContainerId = bool(v != 0) + case 184: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ipv4Ttl", wireType) + } + m.Ipv4Ttl = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ipv4Ttl |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 185: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ipv6HopLimit", wireType) + } + m.Ipv6HopLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ipv6HopLimit |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } case 190: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field GrpcPort", wireType) diff --git a/proto/xtcp_config/v1/xtcp_config.proto b/proto/xtcp_config/v1/xtcp_config.proto index 1f5f215a..a32b437c 100644 --- a/proto/xtcp_config/v1/xtcp_config.proto +++ b/proto/xtcp_config/v1/xtcp_config.proto @@ -510,6 +510,25 @@ message XtcpConfig { (buf.validate.field).required = false ]; + // Outgoing IPv4 TTL for xtcp2's own TCP listeners (Prometheus + gRPC). + // 0 = kernel default. A low value (e.g. 3) keeps replies from travelling + // far if the host is unexpectedly internet-exposed — the per-listener + // analogue of the host nftables TTL clamp. Set via -ipv4Ttl / IPV4_TTL. + // (cf. prometheus/exporter-toolkit#396.) + uint32 ipv4_ttl = 184 [ + (buf.validate.field).required = false, + (buf.validate.field).uint32 = { + lte: 255 + }]; + + // Outgoing IPv6 unicast hop limit for xtcp2's own TCP listeners. 0 = kernel + // default. Same intent as ipv4_ttl. Set via -ipv6HopLimit / IPV6_HOP_LIMIT. + uint32 ipv6_hop_limit = 185 [ + (buf.validate.field).required = false, + (buf.validate.field).uint32 = { + lte: 255 + }]; + // GRPC listening port uint32 grpc_port = 190 [ (buf.validate.field).required = true, diff --git a/python/xtcp_config/v1/xtcp_config_pb2.py b/python/xtcp_config/v1/xtcp_config_pb2.py index 584fdf0c..a8b700b8 100644 --- a/python/xtcp_config/v1/xtcp_config_pb2.py +++ b/python/xtcp_config/v1/xtcp_config_pb2.py @@ -27,7 +27,7 @@ from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n xtcp_config/v1/xtcp_config.proto\x12\x0extcp_config.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1b\x62uf/validate/validate.proto\"\x0c\n\nGetRequest\"A\n\x0bGetResponse\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1a.xtcp_config.v1.XtcpConfigR\x06\x63onfig\"@\n\nSetRequest\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1a.xtcp_config.v1.XtcpConfigR\x06\x63onfig\"A\n\x0bSetResponse\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1a.xtcp_config.v1.XtcpConfigR\x06\x63onfig\"\xb4\x02\n\x17SetPollFrequencyRequest\x12S\n\x0epoll_frequency\x18\x14 \x01(\x0b\x32\x19.google.protobuf.DurationB\x11\xbaH\x0e\xaa\x01\x08\"\x04\x08\x80\xf5$2\x00\xc8\x01\x01R\rpollFrequency\x12O\n\x0cpoll_timeout\x18\x1e \x01(\x0b\x32\x19.google.protobuf.DurationB\x11\xbaH\x0e\xaa\x01\x08\"\x04\x08\x80\xf5$2\x00\xc8\x01\x01R\x0bpollTimeout:s\xbaHp\x1an\n\x0fXtcpConfig.poll\x12\x32Poll timeout must be less than poll poll_frequency\x1a\'this.poll_timeout < this.poll_frequency\"N\n\x18SetPollFrequencyResponse\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1a.xtcp_config.v1.XtcpConfigR\x06\x63onfig\"\xdb\x14\n\nXtcpConfig\x12\x46\n\x17nl_timeout_milliseconds\x18\n \x01(\x04\x42\x0e\xbaH\x0b\x32\x06\x18\xa0\x8d\x06(\x00\xc8\x01\x01R\x15nlTimeoutMilliseconds\x12S\n\x0epoll_frequency\x18\x14 \x01(\x0b\x32\x19.google.protobuf.DurationB\x11\xbaH\x0e\xaa\x01\x08\"\x04\x08\x80\xf5$*\x00\xc8\x01\x01R\rpollFrequency\x12O\n\x0cpoll_timeout\x18\x1e \x01(\x0b\x32\x19.google.protobuf.DurationB\x11\xbaH\x0e\xaa\x01\x08\"\x04\x08\x80\xf5$*\x00\xc8\x01\x01R\x0bpollTimeout\x12+\n\tmax_loops\x18( \x01(\x04\x42\x0e\xbaH\x0b\x32\x06\x18\xa0\x8d\x06(\x00\xc8\x01\x00R\x08maxLoops\x12,\n\nnetlinkers\x18\x32 \x01(\rB\x0c\xbaH\t*\x04\x18\x64(\x01\xc8\x01\x01R\nnetlinkers\x12H\n\x19netlinkers_done_chan_size\x18\x33 \x01(\rB\r\xbaH\n*\x05\x18\xe8\x07(\x01\xc8\x01\x01R\x16netlinkersDoneChanSize\x12*\n\tnlmsg_seq\x18< \x01(\rB\r\xbaH\n*\x05\x18\x90N(\x00\xc8\x01\x01R\x08nlmsgSeq\x12/\n\x0bpacket_size\x18\x46 \x01(\x04\x42\x0e\xbaH\x0b\x32\x06\x18\xc0\x84=(\x00\xc8\x01\x00R\npacketSize\x12\x36\n\x10packet_size_mply\x18P \x01(\rB\x0c\xbaH\t*\x04\x18\x64(\x00\xc8\x01\x00R\x0epacketSizeMply\x12.\n\x0bwrite_files\x18Z \x01(\rB\r\xbaH\n*\x05\x18\xe8\x07(\x00\xc8\x01\x00R\nwriteFiles\x12/\n\x0c\x63\x61pture_path\x18\x64 \x01(\tB\x0c\xbaH\tr\x04\x10\x01\x18P\xc8\x01\x00R\x0b\x63\x61pturePath\x12(\n\x07modulus\x18n \x01(\x04\x42\x0e\xbaH\x0b\x32\x06\x18\xc0\x84=(\x01\xc8\x01\x01R\x07modulus\x12+\n\nmarshal_to\x18x \x01(\tB\x0c\xbaH\tr\x04\x10\x03\x18(\xc8\x01\x01R\tmarshalTo\x12K\n\x1e\x65nvelope_flush_threshold_bytes\x18z \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x1b\x65nvelopeFlushThresholdBytes\x12I\n\x1d\x65nvelope_flush_threshold_rows\x18{ \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x1a\x65nvelopeFlushThresholdRows\x12\x33\n\x11kafka_compression\x18| \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x10kafkaCompression\x12\'\n\x0bs3_endpoint\x18} \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\ns3Endpoint\x12#\n\ts3_bucket\x18~ \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x08s3Bucket\x12#\n\ts3_prefix\x18\x7f \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x08s3Prefix\x12+\n\rs3_access_key\x18\x80\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x0bs3AccessKey\x12+\n\rs3_secret_key\x18\x81\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x0bs3SecretKey\x12O\n s3_parquet_flush_threshold_bytes\x18\x84\x01 \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x1cs3ParquetFlushThresholdBytes\x12$\n\ts3_region\x18\x85\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x08s3Region\x12\x38\n\x14s3_skip_bucket_probe\x18\x86\x01 \x01(\x08\x42\x06\xbaH\x03\xc8\x01\x00R\x11s3SkipBucketProbe\x12,\n\rpyroscope_url\x18\x88\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x0cpyroscopeUrl\x12\x35\n\x12pyroscope_app_name\x18\x89\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x10pyroscopeAppName\x12\x37\n\x13pyroscope_sample_hz\x18\x8a\x01 \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x11pyroscopeSampleHz\x12J\n\x1dpyroscope_upload_interval_sec\x18\x8b\x01 \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x1apyroscopeUploadIntervalSec\x12\"\n\x04\x64\x65st\x18\x82\x01 \x01(\tB\r\xbaH\nr\x05\x10\x04\x18\x80\x01\xc8\x01\x01R\x04\x64\x65st\x12\x38\n\x10\x64\x65st_write_files\x18\x87\x01 \x01(\rB\r\xbaH\n*\x05\x18\xe8\x07(\x00\xc8\x01\x00R\x0e\x64\x65stWriteFiles\x12#\n\x05topic\x18\x8c\x01 \x01(\tB\x0c\xbaH\tr\x04\x10\x01\x18(\xc8\x01\x00R\x05topic\x12\x35\n\x0fxtcp_proto_file\x18\x8f\x01 \x01(\tB\x0c\xbaH\tr\x04\x10\x01\x18P\xc8\x01\x00R\rxtcpProtoFile\x12\x37\n\x10kafka_schema_url\x18\x91\x01 \x01(\tB\x0c\xbaH\tr\x04\x10\x01\x18<\xc8\x01\x00R\x0ekafkaSchemaUrl\x12`\n\x15kafka_produce_timeout\x18\x96\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x10\xbaH\r\xaa\x01\x07\"\x03\x08\xd8\x04\x32\x00\xc8\x01\x00R\x13kafkaProduceTimeout\x12/\n\x0b\x64\x65\x62ug_level\x18\xa0\x01 \x01(\rB\r\xbaH\n*\x05\x18\xe8\x07(\x00\xc8\x01\x01R\ndebugLevel\x12!\n\x05label\x18\xaa\x01 \x01(\tB\n\xbaH\x07r\x02\x18(\xc8\x01\x00R\x05label\x12\x1d\n\x03tag\x18\xb4\x01 \x01(\tB\n\xbaH\x07r\x02\x18(\xc8\x01\x00R\x03tag\x12(\n\x08location\x18\xb5\x01 \x01(\tB\x0b\xbaH\x08r\x03\x18\xfd\x01\xc8\x01\x00R\x08location\x12(\n\x08hostname\x18\xb6\x01 \x01(\tB\x0b\xbaH\x08r\x03\x18\xfd\x01\xc8\x01\x00R\x08hostname\x12\x39\n\x14resolve_container_id\x18\xb7\x01 \x01(\x08\x42\x06\xbaH\x03\xc8\x01\x00R\x12resolveContainerId\x12,\n\tgrpc_port\x18\xbe\x01 \x01(\rB\x0e\xbaH\x0b*\x06\x18\xff\xff\x03(\x01\xc8\x01\x01R\x08grpcPort\x12\x62\n\x15\x65nabled_deserializers\x18\xc8\x01 \x01(\x0b\x32$.xtcp_config.v1.EnabledDeserializersB\x06\xbaH\x03\xc8\x01\x00R\x14\x65nabledDeserializers\x12\"\n\x08io_uring\x18\xd2\x01 \x01(\x08\x42\x06\xbaH\x03\xc8\x01\x00R\x07ioUring\x12\x46\n\x18io_uring_recv_batch_size\x18\xd3\x01 \x01(\rB\r\xbaH\n*\x05\x18\x80 (\x01\xc8\x01\x00R\x14ioUringRecvBatchSize\x12\x44\n\x17io_uring_cqe_batch_size\x18\xd4\x01 \x01(\rB\r\xbaH\n*\x05\x18\x80 (\x01\xc8\x01\x00R\x13ioUringCqeBatchSize\x12(\n\x0b\x63sv_columns\x18\xdc\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\ncsvColumns:s\xbaHp\x1an\n\x0fXtcpConfig.poll\x12\x32Poll timeout must be less than poll poll_frequency\x1a\'this.poll_frequency > this.poll_timeout\"\x9f\x01\n\x14\x45nabledDeserializers\x12K\n\x07\x65nabled\x18\x01 \x03(\x0b\x32\x31.xtcp_config.v1.EnabledDeserializers.EnabledEntryR\x07\x65nabled\x1a:\n\x0c\x45nabledEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x08R\x05value:\x02\x38\x01\x32\xe1\x02\n\rConfigService\x12]\n\x03Get\x12\x1a.xtcp_config.v1.GetRequest\x1a\x1b.xtcp_config.v1.GetResponse\"\x1d\x82\xd3\xe4\x93\x02\x17\x1a\x12/ConfigService/Get:\x01*\x12]\n\x03Set\x12\x1a.xtcp_config.v1.SetRequest\x1a\x1b.xtcp_config.v1.SetResponse\"\x1d\x82\xd3\xe4\x93\x02\x17\x1a\x12/ConfigService/Set:\x01*\x12\x91\x01\n\x10SetPollFrequency\x12\'.xtcp_config.v1.SetPollFrequencyRequest\x1a(.xtcp_config.v1.SetPollFrequencyResponse\"*\x82\xd3\xe4\x93\x02$\x1a\x1f/ConfigService/SetPollFrequency:\x01*B\x8d\x01\n\x12\x63om.xtcp_config.v1B\x0fXtcpConfigProtoP\x01Z\x11./pkg/xtcp_config\xa2\x02\x03XXX\xaa\x02\rXtcpConfig.V1\xca\x02\rXtcpConfig\\V1\xe2\x02\x19XtcpConfig\\V1\\GPBMetadata\xea\x02\x0eXtcpConfig::V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n xtcp_config/v1/xtcp_config.proto\x12\x0extcp_config.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1b\x62uf/validate/validate.proto\"\x0c\n\nGetRequest\"A\n\x0bGetResponse\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1a.xtcp_config.v1.XtcpConfigR\x06\x63onfig\"@\n\nSetRequest\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1a.xtcp_config.v1.XtcpConfigR\x06\x63onfig\"A\n\x0bSetResponse\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1a.xtcp_config.v1.XtcpConfigR\x06\x63onfig\"\xb4\x02\n\x17SetPollFrequencyRequest\x12S\n\x0epoll_frequency\x18\x14 \x01(\x0b\x32\x19.google.protobuf.DurationB\x11\xbaH\x0e\xaa\x01\x08\"\x04\x08\x80\xf5$2\x00\xc8\x01\x01R\rpollFrequency\x12O\n\x0cpoll_timeout\x18\x1e \x01(\x0b\x32\x19.google.protobuf.DurationB\x11\xbaH\x0e\xaa\x01\x08\"\x04\x08\x80\xf5$2\x00\xc8\x01\x01R\x0bpollTimeout:s\xbaHp\x1an\n\x0fXtcpConfig.poll\x12\x32Poll timeout must be less than poll poll_frequency\x1a\'this.poll_timeout < this.poll_frequency\"N\n\x18SetPollFrequencyResponse\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1a.xtcp_config.v1.XtcpConfigR\x06\x63onfig\"\xb8\x15\n\nXtcpConfig\x12\x46\n\x17nl_timeout_milliseconds\x18\n \x01(\x04\x42\x0e\xbaH\x0b\x32\x06\x18\xa0\x8d\x06(\x00\xc8\x01\x01R\x15nlTimeoutMilliseconds\x12S\n\x0epoll_frequency\x18\x14 \x01(\x0b\x32\x19.google.protobuf.DurationB\x11\xbaH\x0e\xaa\x01\x08\"\x04\x08\x80\xf5$*\x00\xc8\x01\x01R\rpollFrequency\x12O\n\x0cpoll_timeout\x18\x1e \x01(\x0b\x32\x19.google.protobuf.DurationB\x11\xbaH\x0e\xaa\x01\x08\"\x04\x08\x80\xf5$*\x00\xc8\x01\x01R\x0bpollTimeout\x12+\n\tmax_loops\x18( \x01(\x04\x42\x0e\xbaH\x0b\x32\x06\x18\xa0\x8d\x06(\x00\xc8\x01\x00R\x08maxLoops\x12,\n\nnetlinkers\x18\x32 \x01(\rB\x0c\xbaH\t*\x04\x18\x64(\x01\xc8\x01\x01R\nnetlinkers\x12H\n\x19netlinkers_done_chan_size\x18\x33 \x01(\rB\r\xbaH\n*\x05\x18\xe8\x07(\x01\xc8\x01\x01R\x16netlinkersDoneChanSize\x12*\n\tnlmsg_seq\x18< \x01(\rB\r\xbaH\n*\x05\x18\x90N(\x00\xc8\x01\x01R\x08nlmsgSeq\x12/\n\x0bpacket_size\x18\x46 \x01(\x04\x42\x0e\xbaH\x0b\x32\x06\x18\xc0\x84=(\x00\xc8\x01\x00R\npacketSize\x12\x36\n\x10packet_size_mply\x18P \x01(\rB\x0c\xbaH\t*\x04\x18\x64(\x00\xc8\x01\x00R\x0epacketSizeMply\x12.\n\x0bwrite_files\x18Z \x01(\rB\r\xbaH\n*\x05\x18\xe8\x07(\x00\xc8\x01\x00R\nwriteFiles\x12/\n\x0c\x63\x61pture_path\x18\x64 \x01(\tB\x0c\xbaH\tr\x04\x10\x01\x18P\xc8\x01\x00R\x0b\x63\x61pturePath\x12(\n\x07modulus\x18n \x01(\x04\x42\x0e\xbaH\x0b\x32\x06\x18\xc0\x84=(\x01\xc8\x01\x01R\x07modulus\x12+\n\nmarshal_to\x18x \x01(\tB\x0c\xbaH\tr\x04\x10\x03\x18(\xc8\x01\x01R\tmarshalTo\x12K\n\x1e\x65nvelope_flush_threshold_bytes\x18z \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x1b\x65nvelopeFlushThresholdBytes\x12I\n\x1d\x65nvelope_flush_threshold_rows\x18{ \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x1a\x65nvelopeFlushThresholdRows\x12\x33\n\x11kafka_compression\x18| \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x10kafkaCompression\x12\'\n\x0bs3_endpoint\x18} \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\ns3Endpoint\x12#\n\ts3_bucket\x18~ \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x08s3Bucket\x12#\n\ts3_prefix\x18\x7f \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x08s3Prefix\x12+\n\rs3_access_key\x18\x80\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x0bs3AccessKey\x12+\n\rs3_secret_key\x18\x81\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x0bs3SecretKey\x12O\n s3_parquet_flush_threshold_bytes\x18\x84\x01 \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x1cs3ParquetFlushThresholdBytes\x12$\n\ts3_region\x18\x85\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x08s3Region\x12\x38\n\x14s3_skip_bucket_probe\x18\x86\x01 \x01(\x08\x42\x06\xbaH\x03\xc8\x01\x00R\x11s3SkipBucketProbe\x12,\n\rpyroscope_url\x18\x88\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x0cpyroscopeUrl\x12\x35\n\x12pyroscope_app_name\x18\x89\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\x10pyroscopeAppName\x12\x37\n\x13pyroscope_sample_hz\x18\x8a\x01 \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x11pyroscopeSampleHz\x12J\n\x1dpyroscope_upload_interval_sec\x18\x8b\x01 \x01(\rB\x06\xbaH\x03\xc8\x01\x00R\x1apyroscopeUploadIntervalSec\x12\"\n\x04\x64\x65st\x18\x82\x01 \x01(\tB\r\xbaH\nr\x05\x10\x04\x18\x80\x01\xc8\x01\x01R\x04\x64\x65st\x12\x38\n\x10\x64\x65st_write_files\x18\x87\x01 \x01(\rB\r\xbaH\n*\x05\x18\xe8\x07(\x00\xc8\x01\x00R\x0e\x64\x65stWriteFiles\x12#\n\x05topic\x18\x8c\x01 \x01(\tB\x0c\xbaH\tr\x04\x10\x01\x18(\xc8\x01\x00R\x05topic\x12\x35\n\x0fxtcp_proto_file\x18\x8f\x01 \x01(\tB\x0c\xbaH\tr\x04\x10\x01\x18P\xc8\x01\x00R\rxtcpProtoFile\x12\x37\n\x10kafka_schema_url\x18\x91\x01 \x01(\tB\x0c\xbaH\tr\x04\x10\x01\x18<\xc8\x01\x00R\x0ekafkaSchemaUrl\x12`\n\x15kafka_produce_timeout\x18\x96\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x10\xbaH\r\xaa\x01\x07\"\x03\x08\xd8\x04\x32\x00\xc8\x01\x00R\x13kafkaProduceTimeout\x12/\n\x0b\x64\x65\x62ug_level\x18\xa0\x01 \x01(\rB\r\xbaH\n*\x05\x18\xe8\x07(\x00\xc8\x01\x01R\ndebugLevel\x12!\n\x05label\x18\xaa\x01 \x01(\tB\n\xbaH\x07r\x02\x18(\xc8\x01\x00R\x05label\x12\x1d\n\x03tag\x18\xb4\x01 \x01(\tB\n\xbaH\x07r\x02\x18(\xc8\x01\x00R\x03tag\x12(\n\x08location\x18\xb5\x01 \x01(\tB\x0b\xbaH\x08r\x03\x18\xfd\x01\xc8\x01\x00R\x08location\x12(\n\x08hostname\x18\xb6\x01 \x01(\tB\x0b\xbaH\x08r\x03\x18\xfd\x01\xc8\x01\x00R\x08hostname\x12\x39\n\x14resolve_container_id\x18\xb7\x01 \x01(\x08\x42\x06\xbaH\x03\xc8\x01\x00R\x12resolveContainerId\x12\'\n\x08ipv4_ttl\x18\xb8\x01 \x01(\rB\x0b\xbaH\x08*\x03\x18\xff\x01\xc8\x01\x00R\x07ipv4Ttl\x12\x32\n\x0eipv6_hop_limit\x18\xb9\x01 \x01(\rB\x0b\xbaH\x08*\x03\x18\xff\x01\xc8\x01\x00R\x0cipv6HopLimit\x12,\n\tgrpc_port\x18\xbe\x01 \x01(\rB\x0e\xbaH\x0b*\x06\x18\xff\xff\x03(\x01\xc8\x01\x01R\x08grpcPort\x12\x62\n\x15\x65nabled_deserializers\x18\xc8\x01 \x01(\x0b\x32$.xtcp_config.v1.EnabledDeserializersB\x06\xbaH\x03\xc8\x01\x00R\x14\x65nabledDeserializers\x12\"\n\x08io_uring\x18\xd2\x01 \x01(\x08\x42\x06\xbaH\x03\xc8\x01\x00R\x07ioUring\x12\x46\n\x18io_uring_recv_batch_size\x18\xd3\x01 \x01(\rB\r\xbaH\n*\x05\x18\x80 (\x01\xc8\x01\x00R\x14ioUringRecvBatchSize\x12\x44\n\x17io_uring_cqe_batch_size\x18\xd4\x01 \x01(\rB\r\xbaH\n*\x05\x18\x80 (\x01\xc8\x01\x00R\x13ioUringCqeBatchSize\x12(\n\x0b\x63sv_columns\x18\xdc\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x00R\ncsvColumns:s\xbaHp\x1an\n\x0fXtcpConfig.poll\x12\x32Poll timeout must be less than poll poll_frequency\x1a\'this.poll_frequency > this.poll_timeout\"\x9f\x01\n\x14\x45nabledDeserializers\x12K\n\x07\x65nabled\x18\x01 \x03(\x0b\x32\x31.xtcp_config.v1.EnabledDeserializers.EnabledEntryR\x07\x65nabled\x1a:\n\x0c\x45nabledEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x08R\x05value:\x02\x38\x01\x32\xe1\x02\n\rConfigService\x12]\n\x03Get\x12\x1a.xtcp_config.v1.GetRequest\x1a\x1b.xtcp_config.v1.GetResponse\"\x1d\x82\xd3\xe4\x93\x02\x17\x1a\x12/ConfigService/Get:\x01*\x12]\n\x03Set\x12\x1a.xtcp_config.v1.SetRequest\x1a\x1b.xtcp_config.v1.SetResponse\"\x1d\x82\xd3\xe4\x93\x02\x17\x1a\x12/ConfigService/Set:\x01*\x12\x91\x01\n\x10SetPollFrequency\x12\'.xtcp_config.v1.SetPollFrequencyRequest\x1a(.xtcp_config.v1.SetPollFrequencyResponse\"*\x82\xd3\xe4\x93\x02$\x1a\x1f/ConfigService/SetPollFrequency:\x01*B\x8d\x01\n\x12\x63om.xtcp_config.v1B\x0fXtcpConfigProtoP\x01Z\x11./pkg/xtcp_config\xa2\x02\x03XXX\xaa\x02\rXtcpConfig.V1\xca\x02\rXtcpConfig\\V1\xe2\x02\x19XtcpConfig\\V1\\GPBMetadata\xea\x02\x0eXtcpConfig::V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -121,6 +121,10 @@ _globals['_XTCPCONFIG'].fields_by_name['hostname']._serialized_options = b'\272H\010r\003\030\375\001\310\001\000' _globals['_XTCPCONFIG'].fields_by_name['resolve_container_id']._loaded_options = None _globals['_XTCPCONFIG'].fields_by_name['resolve_container_id']._serialized_options = b'\272H\003\310\001\000' + _globals['_XTCPCONFIG'].fields_by_name['ipv4_ttl']._loaded_options = None + _globals['_XTCPCONFIG'].fields_by_name['ipv4_ttl']._serialized_options = b'\272H\010*\003\030\377\001\310\001\000' + _globals['_XTCPCONFIG'].fields_by_name['ipv6_hop_limit']._loaded_options = None + _globals['_XTCPCONFIG'].fields_by_name['ipv6_hop_limit']._serialized_options = b'\272H\010*\003\030\377\001\310\001\000' _globals['_XTCPCONFIG'].fields_by_name['grpc_port']._loaded_options = None _globals['_XTCPCONFIG'].fields_by_name['grpc_port']._serialized_options = b'\272H\013*\006\030\377\377\003(\001\310\001\001' _globals['_XTCPCONFIG'].fields_by_name['enabled_deserializers']._loaded_options = None @@ -156,11 +160,11 @@ _globals['_SETPOLLFREQUENCYRESPONSE']._serialized_start=668 _globals['_SETPOLLFREQUENCYRESPONSE']._serialized_end=746 _globals['_XTCPCONFIG']._serialized_start=749 - _globals['_XTCPCONFIG']._serialized_end=3400 - _globals['_ENABLEDDESERIALIZERS']._serialized_start=3403 - _globals['_ENABLEDDESERIALIZERS']._serialized_end=3562 - _globals['_ENABLEDDESERIALIZERS_ENABLEDENTRY']._serialized_start=3504 - _globals['_ENABLEDDESERIALIZERS_ENABLEDENTRY']._serialized_end=3562 - _globals['_CONFIGSERVICE']._serialized_start=3565 - _globals['_CONFIGSERVICE']._serialized_end=3918 + _globals['_XTCPCONFIG']._serialized_end=3493 + _globals['_ENABLEDDESERIALIZERS']._serialized_start=3496 + _globals['_ENABLEDDESERIALIZERS']._serialized_end=3655 + _globals['_ENABLEDDESERIALIZERS_ENABLEDENTRY']._serialized_start=3597 + _globals['_ENABLEDDESERIALIZERS_ENABLEDENTRY']._serialized_end=3655 + _globals['_CONFIGSERVICE']._serialized_start=3658 + _globals['_CONFIGSERVICE']._serialized_end=4011 # @@protoc_insertion_point(module_scope) diff --git a/python/xtcp_config/v1/xtcp_config_pb2.pyi b/python/xtcp_config/v1/xtcp_config_pb2.pyi index c8e33898..436a6bdb 100644 --- a/python/xtcp_config/v1/xtcp_config_pb2.pyi +++ b/python/xtcp_config/v1/xtcp_config_pb2.pyi @@ -46,7 +46,7 @@ class SetPollFrequencyResponse(_message.Message): def __init__(self, config: _Optional[_Union[XtcpConfig, _Mapping]] = ...) -> None: ... class XtcpConfig(_message.Message): - __slots__ = ("nl_timeout_milliseconds", "poll_frequency", "poll_timeout", "max_loops", "netlinkers", "netlinkers_done_chan_size", "nlmsg_seq", "packet_size", "packet_size_mply", "write_files", "capture_path", "modulus", "marshal_to", "envelope_flush_threshold_bytes", "envelope_flush_threshold_rows", "kafka_compression", "s3_endpoint", "s3_bucket", "s3_prefix", "s3_access_key", "s3_secret_key", "s3_parquet_flush_threshold_bytes", "s3_region", "s3_skip_bucket_probe", "pyroscope_url", "pyroscope_app_name", "pyroscope_sample_hz", "pyroscope_upload_interval_sec", "dest", "dest_write_files", "topic", "xtcp_proto_file", "kafka_schema_url", "kafka_produce_timeout", "debug_level", "label", "tag", "location", "hostname", "resolve_container_id", "grpc_port", "enabled_deserializers", "io_uring", "io_uring_recv_batch_size", "io_uring_cqe_batch_size", "csv_columns") + __slots__ = ("nl_timeout_milliseconds", "poll_frequency", "poll_timeout", "max_loops", "netlinkers", "netlinkers_done_chan_size", "nlmsg_seq", "packet_size", "packet_size_mply", "write_files", "capture_path", "modulus", "marshal_to", "envelope_flush_threshold_bytes", "envelope_flush_threshold_rows", "kafka_compression", "s3_endpoint", "s3_bucket", "s3_prefix", "s3_access_key", "s3_secret_key", "s3_parquet_flush_threshold_bytes", "s3_region", "s3_skip_bucket_probe", "pyroscope_url", "pyroscope_app_name", "pyroscope_sample_hz", "pyroscope_upload_interval_sec", "dest", "dest_write_files", "topic", "xtcp_proto_file", "kafka_schema_url", "kafka_produce_timeout", "debug_level", "label", "tag", "location", "hostname", "resolve_container_id", "ipv4_ttl", "ipv6_hop_limit", "grpc_port", "enabled_deserializers", "io_uring", "io_uring_recv_batch_size", "io_uring_cqe_batch_size", "csv_columns") NL_TIMEOUT_MILLISECONDS_FIELD_NUMBER: _ClassVar[int] POLL_FREQUENCY_FIELD_NUMBER: _ClassVar[int] POLL_TIMEOUT_FIELD_NUMBER: _ClassVar[int] @@ -87,6 +87,8 @@ class XtcpConfig(_message.Message): LOCATION_FIELD_NUMBER: _ClassVar[int] HOSTNAME_FIELD_NUMBER: _ClassVar[int] RESOLVE_CONTAINER_ID_FIELD_NUMBER: _ClassVar[int] + IPV4_TTL_FIELD_NUMBER: _ClassVar[int] + IPV6_HOP_LIMIT_FIELD_NUMBER: _ClassVar[int] GRPC_PORT_FIELD_NUMBER: _ClassVar[int] ENABLED_DESERIALIZERS_FIELD_NUMBER: _ClassVar[int] IO_URING_FIELD_NUMBER: _ClassVar[int] @@ -133,13 +135,15 @@ class XtcpConfig(_message.Message): location: str hostname: str resolve_container_id: bool + ipv4_ttl: int + ipv6_hop_limit: int grpc_port: int enabled_deserializers: EnabledDeserializers io_uring: bool io_uring_recv_batch_size: int io_uring_cqe_batch_size: int csv_columns: str - def __init__(self, nl_timeout_milliseconds: _Optional[int] = ..., poll_frequency: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., poll_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., max_loops: _Optional[int] = ..., netlinkers: _Optional[int] = ..., netlinkers_done_chan_size: _Optional[int] = ..., nlmsg_seq: _Optional[int] = ..., packet_size: _Optional[int] = ..., packet_size_mply: _Optional[int] = ..., write_files: _Optional[int] = ..., capture_path: _Optional[str] = ..., modulus: _Optional[int] = ..., marshal_to: _Optional[str] = ..., envelope_flush_threshold_bytes: _Optional[int] = ..., envelope_flush_threshold_rows: _Optional[int] = ..., kafka_compression: _Optional[str] = ..., s3_endpoint: _Optional[str] = ..., s3_bucket: _Optional[str] = ..., s3_prefix: _Optional[str] = ..., s3_access_key: _Optional[str] = ..., s3_secret_key: _Optional[str] = ..., s3_parquet_flush_threshold_bytes: _Optional[int] = ..., s3_region: _Optional[str] = ..., s3_skip_bucket_probe: bool = ..., pyroscope_url: _Optional[str] = ..., pyroscope_app_name: _Optional[str] = ..., pyroscope_sample_hz: _Optional[int] = ..., pyroscope_upload_interval_sec: _Optional[int] = ..., dest: _Optional[str] = ..., dest_write_files: _Optional[int] = ..., topic: _Optional[str] = ..., xtcp_proto_file: _Optional[str] = ..., kafka_schema_url: _Optional[str] = ..., kafka_produce_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., debug_level: _Optional[int] = ..., label: _Optional[str] = ..., tag: _Optional[str] = ..., location: _Optional[str] = ..., hostname: _Optional[str] = ..., resolve_container_id: bool = ..., grpc_port: _Optional[int] = ..., enabled_deserializers: _Optional[_Union[EnabledDeserializers, _Mapping]] = ..., io_uring: bool = ..., io_uring_recv_batch_size: _Optional[int] = ..., io_uring_cqe_batch_size: _Optional[int] = ..., csv_columns: _Optional[str] = ...) -> None: ... + def __init__(self, nl_timeout_milliseconds: _Optional[int] = ..., poll_frequency: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., poll_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., max_loops: _Optional[int] = ..., netlinkers: _Optional[int] = ..., netlinkers_done_chan_size: _Optional[int] = ..., nlmsg_seq: _Optional[int] = ..., packet_size: _Optional[int] = ..., packet_size_mply: _Optional[int] = ..., write_files: _Optional[int] = ..., capture_path: _Optional[str] = ..., modulus: _Optional[int] = ..., marshal_to: _Optional[str] = ..., envelope_flush_threshold_bytes: _Optional[int] = ..., envelope_flush_threshold_rows: _Optional[int] = ..., kafka_compression: _Optional[str] = ..., s3_endpoint: _Optional[str] = ..., s3_bucket: _Optional[str] = ..., s3_prefix: _Optional[str] = ..., s3_access_key: _Optional[str] = ..., s3_secret_key: _Optional[str] = ..., s3_parquet_flush_threshold_bytes: _Optional[int] = ..., s3_region: _Optional[str] = ..., s3_skip_bucket_probe: bool = ..., pyroscope_url: _Optional[str] = ..., pyroscope_app_name: _Optional[str] = ..., pyroscope_sample_hz: _Optional[int] = ..., pyroscope_upload_interval_sec: _Optional[int] = ..., dest: _Optional[str] = ..., dest_write_files: _Optional[int] = ..., topic: _Optional[str] = ..., xtcp_proto_file: _Optional[str] = ..., kafka_schema_url: _Optional[str] = ..., kafka_produce_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., debug_level: _Optional[int] = ..., label: _Optional[str] = ..., tag: _Optional[str] = ..., location: _Optional[str] = ..., hostname: _Optional[str] = ..., resolve_container_id: bool = ..., ipv4_ttl: _Optional[int] = ..., ipv6_hop_limit: _Optional[int] = ..., grpc_port: _Optional[int] = ..., enabled_deserializers: _Optional[_Union[EnabledDeserializers, _Mapping]] = ..., io_uring: bool = ..., io_uring_recv_batch_size: _Optional[int] = ..., io_uring_cqe_batch_size: _Optional[int] = ..., csv_columns: _Optional[str] = ...) -> None: ... class EnabledDeserializers(_message.Message): __slots__ = ("enabled",) diff --git a/xtcp_config/v1/xtcp_config.swagger.json b/xtcp_config/v1/xtcp_config.swagger.json index 4b3afdb6..a1860610 100644 --- a/xtcp_config/v1/xtcp_config.swagger.json +++ b/xtcp_config/v1/xtcp_config.swagger.json @@ -381,6 +381,16 @@ "type": "boolean", "description": "Resolve each socket's owning container id from its cgroup (sets the\nrecord's container_id / container_runtime). Set via -resolveContainerId\nflag or CONTAINER_ID_RESOLVE env. Needs /sys/fs/cgroup readable (mount it\nand run --cgroupns=host in a container)." }, + "ipv4Ttl": { + "type": "integer", + "format": "int64", + "title": "Outgoing IPv4 TTL for xtcp2's own TCP listeners (Prometheus + gRPC).\n0 = kernel default. A low value (e.g. 3) keeps replies from travelling\nfar if the host is unexpectedly internet-exposed — the per-listener\nanalogue of the host nftables TTL clamp. Set via -ipv4Ttl / IPV4_TTL.\n(cf. prometheus/exporter-toolkit#396.)" + }, + "ipv6HopLimit": { + "type": "integer", + "format": "int64", + "description": "Outgoing IPv6 unicast hop limit for xtcp2's own TCP listeners. 0 = kernel\ndefault. Same intent as ipv4_ttl. Set via -ipv6HopLimit / IPV6_HOP_LIMIT." + }, "grpcPort": { "type": "integer", "format": "int64",