Repository: pixie-io/pixie (language: cpp)

Issue:
`px/tcp_drops` pxl script broken due to missing tcp_drop kprobe
Running the `px/tcp_drops` pxl script results in the following errors:

```
$ kubectl -n pl logs vizier-pem-qrn9l

[ .. ]

stdin:26-28: WARNING: tcp_drop is not traceable (either non-existing, inlined, or marked as "notrace"); attaching to it will likely fail
I20230627 17:32:19.979256 1894345 stirling.cc:549] DynamicTraceConnector [DT_e2a6ba86-df73-4975-a291-fd8728296097] created in 5570.15 ms.
I20230627 17:32:19.979339 1894345 source_connector.cc:35] Initializing source connector: DT_e2a6ba86-df73-4975-a291-fd8728296097
cannot attach kprobe, probe entry may not exist
ERROR: Error attaching probe: 'kprobe:tcp_drop'
I20230627 17:32:20.119061 1894345 stirling.cc:566] Internal : Failed to run BPF code.
I20230627 17:32:22.009603 3168351 tracepoint_manager.cc:180] Tracepoint[e2a6ba86-df73-4975-a291-fd8728296097]::tcp_drop_tracer has transitioned PENDING_STATE -> FAILED_STATE
```

It appears that the `tcp_drop` function was removed in v5.19 kernel and that removal may have been backported to other v5.x releases. For these newer kernels there is a tracepoint available that should serve a similar purpose (`tracepoint:skb:kfree_skb`). BCC's `tcpdrop` tool uses this as a [fallback](https://github.com/iovisor/bcc/blob/6ef0f0dd2c8d315e2b6dda26e63d014a49ca9ec1/tools/tcpdrop.py#L228-L232) if the kprobe doesn't exist, so we should implement a similar fallback mechanism.



Discussion / hints:

Fix patch:
diff --git a/src/common/system/kernel_version.h b/src/common/system/kernel_version.h
index 610d6478e5a..7b0588d5657 100644
--- a/src/common/system/kernel_version.h
+++ b/src/common/system/kernel_version.h
@@ -38,6 +38,11 @@ struct KernelVersion {
   std::string ToString() const {
     return absl::Substitute("$0.$1.$2", version, major_rev, minor_rev);
   }
+
+  bool operator==(const KernelVersion& other) const {
+    return (version == other.version) && (major_rev == other.major_rev) &&
+           (minor_rev == other.minor_rev);
+  }
 };
 
 enum class KernelVersionSource {
diff --git a/src/vizier/services/agent/kelvin/kelvin_main.cc b/src/vizier/services/agent/kelvin/kelvin_main.cc
index 06a727dda97..fdcacad7f64 100644
--- a/src/vizier/services/agent/kelvin/kelvin_main.cc
+++ b/src/vizier/services/agent/kelvin/kelvin_main.cc
@@ -27,6 +27,7 @@
 #include "src/common/base/base.h"
 #include "src/common/event/nats.h"
 #include "src/common/signal/signal.h"
+#include "src/common/system/kernel_version.h"
 #include "src/shared/version/version.h"
 
 DEFINE_string(nats_url, gflags::StringFromEnv("PL_NATS_URL", "pl-nats"),
@@ -70,8 +71,6 @@ int main(int argc, char** argv) {
   TerminationHandler::InstallSignalHandlers();
 
   sole::uuid agent_id = sole::uuid4();
-  LOG(INFO) << absl::Substitute("Pixie Kelvin. Version: $0, id: $1",
-                                px::VersionInfo::VersionString(), agent_id.str());
   if (FLAGS_pod_ip.length() == 0) {
     LOG(FATAL) << "The POD_IP must be specified";
   }
@@ -88,8 +87,12 @@ int main(int argc, char** argv) {
   std::string mds_addr =
       absl::Substitute("$0.$1.svc:$2", FLAGS_mds_addr, FLAGS_namespace, FLAGS_mds_port);
 
+  px::system::KernelVersion kernel_version = px::system::GetCachedKernelVersion();
+  LOG(INFO) << absl::Substitute("Pixie Kelvin. Version: $0, id: $1, kernel: $2",
+                                px::VersionInfo::VersionString(), agent_id.str(),
+                                kernel_version.ToString());
   auto manager = KelvinManager::Create(agent_id, FLAGS_pod_name, FLAGS_host_ip, addr,
-                                       FLAGS_rpc_port, FLAGS_nats_url, mds_addr)
+                                       FLAGS_rpc_port, FLAGS_nats_url, mds_addr, kernel_version)
                      .ConsumeValueOrDie();
 
   TerminationHandler::set_manager(manager.get());
diff --git a/src/vizier/services/agent/kelvin/kelvin_manager.h b/src/vizier/services/agent/kelvin/kelvin_manager.h
index 81336488ddc..e5d2460fd88 100644
--- a/src/vizier/services/agent/kelvin/kelvin_manager.h
+++ b/src/vizier/services/agent/kelvin/kelvin_manager.h
@@ -22,6 +22,7 @@
 #include <string>
 #include <utility>
 
+#include "src/common/system/kernel_version.h"
 #include "src/vizier/services/agent/shared/manager/manager.h"
 
 namespace px {
@@ -43,9 +44,9 @@ class KelvinManager : public Manager {
   KelvinManager() = delete;
   KelvinManager(sole::uuid agent_id, std::string_view pod_name, std::string_view host_ip,
                 std::string_view addr, int grpc_server_port, std::string_view nats_url,
-                std::string_view mds_url)
+                std::string_view mds_url, system::KernelVersion kernel_version)
       : Manager(agent_id, pod_name, host_ip, grpc_server_port, KelvinManager::Capabilities(),
-                KelvinManager::Parameters(), nats_url, mds_url) {
+                KelvinManager::Parameters(), nats_url, mds_url, kernel_version) {
     info()->address = std::string(addr);
   }
 
diff --git a/src/vizier/services/agent/pem/BUILD.bazel b/src/vizier/services/agent/pem/BUILD.bazel
index 29ff6d32e63..ae3d53eafbc 100644
--- a/src/vizier/services/agent/pem/BUILD.bazel
+++ b/src/vizier/services/agent/pem/BUILD.bazel
@@ -58,6 +58,7 @@ pl_cc_binary(
     deps = [
         ":cc_library",
         "//src/common/signal:cc_library",
+        "//src/common/system:cc_library",
         "//src/shared/version:cc_library",
         "//src/shared/version:version_linkstamp",
         "//src/vizier/services/agent/shared/base:cc_library",
diff --git a/src/vizier/services/agent/pem/pem_main.cc b/src/vizier/services/agent/pem/pem_main.cc
index ae8b8a5893d..c9a2e81884c 100644
--- a/src/vizier/services/agent/pem/pem_main.cc
+++ b/src/vizier/services/agent/pem/pem_main.cc
@@ -24,6 +24,7 @@
 
 #include "src/common/base/base.h"
 #include "src/common/signal/signal.h"
+#include "src/common/system/kernel_version.h"
 #include "src/shared/version/version.h"
 
 DEFINE_string(nats_url, gflags::StringFromEnv("PL_NATS_URL", "pl-nats"),
@@ -54,8 +55,6 @@ int main(int argc, char** argv) {
   TerminationHandler::InstallSignalHandlers();
 
   sole::uuid agent_id = sole::uuid4();
-  LOG(INFO) << absl::Substitute("Pixie PEM. Version: $0, id: $1", px::VersionInfo::VersionString(),
-                                agent_id.str());
 
   if (FLAGS_clock_converter == "grpc") {
     px::system::Config::ResetInstance(
@@ -65,8 +64,13 @@ int main(int argc, char** argv) {
   if (FLAGS_host_ip.length() == 0) {
     LOG(FATAL) << "The HOST_IP must be specified";
   }
-  auto manager = PEMManager::Create(agent_id, FLAGS_pod_name, FLAGS_host_ip, FLAGS_nats_url)
-                     .ConsumeValueOrDie();
+  px::system::KernelVersion kernel_version = px::system::GetCachedKernelVersion();
+  LOG(INFO) << absl::Substitute("Pixie PEM. Version: $0, id: $1, kernel version: $2",
+                                px::VersionInfo::VersionString(), agent_id.str(),
+                                kernel_version.ToString());
+  auto manager =
+      PEMManager::Create(agent_id, FLAGS_pod_name, FLAGS_host_ip, FLAGS_nats_url, kernel_version)
+          .ConsumeValueOrDie();
 
   TerminationHandler::set_manager(manager.get());
 
diff --git a/src/vizier/services/agent/pem/pem_manager.h b/src/vizier/services/agent/pem/pem_manager.h
index 08919286f4c..442c7a34bce 100644
--- a/src/vizier/services/agent/pem/pem_manager.h
+++ b/src/vizier/services/agent/pem/pem_manager.h
@@ -26,6 +26,7 @@
 
 #include <prometheus/gauge.h>
 
+#include "src/common/system/kernel_version.h"
 #include "src/stirling/stirling.h"
 #include "src/vizier/services/agent/pem/tracepoint_manager.h"
 #include "src/vizier/services/agent/shared/manager/manager.h"
@@ -52,15 +53,18 @@ class PEMManager : public Manager {
  protected:
   PEMManager() = delete;
   PEMManager(sole::uuid agent_id, std::string_view pod_name, std::string_view host_ip,
-             std::string_view nats_url)
+             std::string_view nats_url, px::system::KernelVersion kernel_version)
       : PEMManager(agent_id, pod_name, host_ip, nats_url,
-                   px::stirling::Stirling::Create(px::stirling::CreateSourceRegistryFromFlag())) {}
+                   px::stirling::Stirling::Create(px::stirling::CreateSourceRegistryFromFlag()),
+                   kernel_version) {}
 
+  // Constructor which creates the HostInfo for an agent (runs once per node).
   PEMManager(sole::uuid agent_id, std::string_view pod_name, std::string_view host_ip,
-             std::string_view nats_url, std::unique_ptr<stirling::Stirling> stirling)
+             std::string_view nats_url, std::unique_ptr<stirling::Stirling> stirling,
+             px::system::KernelVersion kernel_version)
       : Manager(agent_id, pod_name, host_ip, /*grpc_server_port*/ 0, PEMManager::Capabilities(),
                 PEMManager::Parameters(), nats_url,
-                /*mds_url*/ ""),
+                /*mds_url*/ "", kernel_version),
         stirling_(std::move(stirling)),
         node_available_memory_(prometheus::BuildGauge()
                                    .Name("node_available_memory")
diff --git a/src/vizier/services/agent/shared/base/info.h b/src/vizier/services/agent/shared/base/info.h
index 5ce279b6bb9..62ea8e5f1e3 100644
--- a/src/vizier/services/agent/shared/base/info.h
+++ b/src/vizier/services/agent/shared/base/info.h
@@ -21,6 +21,7 @@
 
 #include <sole.hpp>
 
+#include "src/common/system/kernel_version.h"
 #include "src/vizier/services/shared/agentpb/agent.pb.h"
 
 namespace px {
@@ -42,6 +43,7 @@ struct Info {
   std::string address;
   std::string pod_name;
   std::string host_ip;
+  system::KernelVersion kernel_version;
   services::shared::agent::AgentCapabilities capabilities;
   services::shared::agent::AgentParameters parameters;
 };
diff --git a/src/vizier/services/agent/shared/manager/BUILD.bazel b/src/vizier/services/agent/shared/manager/BUILD.bazel
index 6ea594a9b41..7ba7ff6b8cc 100644
--- a/src/vizier/services/agent/shared/manager/BUILD.bazel
+++ b/src/vizier/services/agent/shared/manager/BUILD.bazel
@@ -35,6 +35,7 @@ pl_cc_library(
         "//src/carnot",
         "//src/common/event:cc_library",
         "//src/common/metrics:cc_library",
+        "//src/common/system:cc_library",
         "//src/common/uuid:cc_library",
         "//src/shared/metadata:cc_library",
         "//src/shared/schema:cc_library",
diff --git a/src/vizier/services/agent/shared/manager/manager.cc b/src/vizier/services/agent/shared/manager/manager.cc
index 7d2458c105d..75fac1db7a0 100644
--- a/src/vizier/services/agent/shared/manager/manager.cc
+++ b/src/vizier/services/agent/shared/manager/manager.cc
@@ -98,7 +98,7 @@ std::shared_ptr<services::metadata::CronScriptStoreService::Stub> CreateCronScri
 Manager::Manager(sole::uuid agent_id, std::string_view pod_name, std::string_view host_ip,
                  int grpc_server_port, services::shared::agent::AgentCapabilities capabilities,
                  services::shared::agent::AgentParameters parameters, std::string_view nats_url,
-                 std::string_view mds_url)
+                 std::string_view mds_url, system::KernelVersion kernel_version)
     : grpc_channel_creds_(SSL::DefaultGRPCClientCreds()),
       time_system_(std::make_unique<px::event::RealTimeSystem>()),
       api_(std::make_unique<px::event::APIImpl>(time_system_.get())),
@@ -134,6 +134,7 @@ Manager::Manager(sole::uuid agent_id, std::string_view pod_name, std::string_vie
   info_.parameters = std::move(parameters);
   info_.pod_name = std::string(pod_name);
   info_.host_ip = std::string(host_ip);
+  info_.kernel_version = kernel_version;
 }
 
 Status Manager::Init() {
diff --git a/src/vizier/services/agent/shared/manager/manager.h b/src/vizier/services/agent/shared/manager/manager.h
index d3d1f0d50d7..67f8a161081 100644
--- a/src/vizier/services/agent/shared/manager/manager.h
+++ b/src/vizier/services/agent/shared/manager/manager.h
@@ -30,6 +30,7 @@
 #include "src/common/event/event.h"
 #include "src/common/event/nats.h"
 #include "src/common/metrics/memory_metrics.h"
+#include "src/common/system/kernel_version.h"
 #include "src/common/uuid/uuid.h"
 #include "src/shared/metadata/metadata.h"
 #include "src/vizier/funcs/context/vizier_context.h"
@@ -108,7 +109,7 @@ class Manager : public BaseManager {
   Manager(sole::uuid agent_id, std::string_view pod_name, std::string_view host_ip,
           int grpc_server_port, services::shared::agent::AgentCapabilities capabilities,
           services::shared::agent::AgentParameters parameters, std::string_view nats_url,
-          std::string_view mds_url);
+          std::string_view mds_url, system::KernelVersion kernel_version);
   Status Init();
 
   Status RegisterMessageHandler(MsgCase c, std::shared_ptr<MessageHandler> handler,
diff --git a/src/vizier/services/agent/shared/manager/registration.cc b/src/vizier/services/agent/shared/manager/registration.cc
index 538b6ab542d..fe9ad3587e1 100644
--- a/src/vizier/services/agent/shared/manager/registration.cc
+++ b/src/vizier/services/agent/shared/manager/registration.cc
@@ -20,6 +20,7 @@
 #include <utility>
 #include <vector>
 
+#include "src/vizier/services/agent/shared/base/info.h"
 #include "src/vizier/services/agent/shared/manager/manager.h"
 #include "src/vizier/services/agent/shared/manager/registration.h"
 
@@ -56,6 +57,16 @@ RegistrationHandler::RegistrationHandler(px::event::Dispatcher* dispatcher, Info
   });
 }
 
+// convert px::system::KernelVersion to px::vizier::services::shared::agent:KernelVersion
+::px::vizier::services::shared::agent::KernelVersion KernelToProto(
+    const system::KernelVersion& kv) {
+  ::px::vizier::services::shared::agent::KernelVersion kv_proto;
+  kv_proto.set_version(kv.version);
+  kv_proto.set_major_rev(kv.major_rev);
+  kv_proto.set_minor_rev(kv.minor_rev);
+  return kv_proto;
+}
+
 Status RegistrationHandler::DispatchRegistration() {
   // Send the registration request.
   messages::VizierMessage req;
@@ -79,6 +90,8 @@ Status RegistrationHandler::DispatchRegistration() {
   host_info->set_hostname(agent_info()->hostname);
   host_info->set_pod_name(agent_info()->pod_name);
   host_info->set_host_ip(agent_info()->host_ip);
+  auto kernel_version_proto = KernelToProto(agent_info()->kernel_version);
+  host_info->mutable_kernel()->CopyFrom(kernel_version_proto);
   *req_info->mutable_capabilities() = agent_info()->capabilities;
   *req_info->mutable_parameters() = agent_info()->parameters;
 
diff --git a/src/vizier/services/shared/agentpb/agent.pb.go b/src/vizier/services/shared/agentpb/agent.pb.go
index da7c654921c..04334195703 100755
--- a/src/vizier/services/shared/agentpb/agent.pb.go
+++ b/src/vizier/services/shared/agentpb/agent.pb.go
@@ -215,16 +215,76 @@ func (m *AgentInfo) GetParameters() *AgentParameters {
 	return nil
 }
 
+type KernelVersion struct {
+	Version  uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
+	MajorRev uint32 `protobuf:"varint,2,opt,name=major_rev,json=majorRev,proto3" json:"major_rev,omitempty"`
+	MinorRev uint32 `protobuf:"varint,3,opt,name=minor_rev,json=minorRev,proto3" json:"minor_rev,omitempty"`
+}
+
+func (m *KernelVersion) Reset()      { *m = KernelVersion{} }
+func (*KernelVersion) ProtoMessage() {}
+func (*KernelVersion) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fef0af3bd5248f34, []int{3}
+}
+func (m *KernelVersion) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *KernelVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_KernelVersion.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *KernelVersion) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_KernelVersion.Merge(m, src)
+}
+func (m *KernelVersion) XXX_Size() int {
+	return m.Size()
+}
+func (m *KernelVersion) XXX_DiscardUnknown() {
+	xxx_messageInfo_KernelVersion.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_KernelVersion proto.InternalMessageInfo
+
+func (m *KernelVersion) GetVersion() uint32 {
+	if m != nil {
+		return m.Version
+	}
+	return 0
+}
+
+func (m *KernelVersion) GetMajorRev() uint32 {
+	if m != nil {
+		return m.MajorRev
+	}
+	return 0
+}
+
+func (m *KernelVersion) GetMinorRev() uint32 {
+	if m != nil {
+		return m.MinorRev
+	}
+	return 0
+}
+
 type HostInfo struct {
-	Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
-	PodName  string `protobuf:"bytes,2,opt,name=pod_name,json=podName,proto3" json:"pod_name,omitempty"`
-	HostIP   string `protobuf:"bytes,3,opt,name=host_ip,json=hostIp,proto3" json:"host_ip,omitempty"`
+	Hostname string         `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
+	PodName  string         `protobuf:"bytes,2,opt,name=pod_name,json=podName,proto3" json:"pod_name,omitempty"`
+	HostIP   string         `protobuf:"bytes,3,opt,name=host_ip,json=hostIp,proto3" json:"host_ip,omitempty"`
+	Kernel   *KernelVersion `protobuf:"bytes,4,opt,name=kernel,proto3" json:"kernel,omitempty"`
 }
 
 func (m *HostInfo) Reset()      { *m = HostInfo{} }
 func (*HostInfo) ProtoMessage() {}
 func (*HostInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fef0af3bd5248f34, []int{3}
+	return fileDescriptor_fef0af3bd5248f34, []int{4}
 }
 func (m *HostInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -274,6 +334,13 @@ func (m *HostInfo) GetHostIP() string {
 	return ""
 }
 
+func (m *HostInfo) GetKernel() *KernelVersion {
+	if m != nil {
+		return m.Kernel
+	}
+	return nil
+}
+
 type Agent struct {
 	Info            *AgentInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"`
 	CreateTimeNS    int64      `protobuf:"varint,2,opt,name=create_time_ns,json=createTimeNs,proto3" json:"create_time_ns,omitempty"`
@@ -284,7 +351,7 @@ type Agent struct {
 func (m *Agent) Reset()      { *m = Agent{} }
 func (*Agent) ProtoMessage() {}
 func (*Agent) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fef0af3bd5248f34, []int{4}
+	return fileDescriptor_fef0af3bd5248f34, []int{5}
 }
 func (m *Agent) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -349,7 +416,7 @@ type AgentStatus struct {
 func (m *AgentStatus) Reset()      { *m = AgentStatus{} }
 func (*AgentStatus) ProtoMessage() {}
 func (*AgentStatus) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fef0af3bd5248f34, []int{5}
+	return fileDescriptor_fef0af3bd5248f34, []int{6}
 }
 func (m *AgentStatus) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -397,6 +464,7 @@ func init() {
 	proto.RegisterType((*AgentCapabilities)(nil), "px.vizier.services.shared.agent.AgentCapabilities")
 	proto.RegisterType((*AgentParameters)(nil), "px.vizier.services.shared.agent.AgentParameters")
 	proto.RegisterType((*AgentInfo)(nil), "px.vizier.services.shared.agent.AgentInfo")
+	proto.RegisterType((*KernelVersion)(nil), "px.vizier.services.shared.agent.KernelVersion")
 	proto.RegisterType((*HostInfo)(nil), "px.vizier.services.shared.agent.HostInfo")
 	proto.RegisterType((*Agent)(nil), "px.vizier.services.shared.agent.Agent")
 	proto.RegisterType((*AgentStatus)(nil), "px.vizier.services.shared.agent.AgentStatus")
@@ -407,56 +475,61 @@ func init() {
 }
 
 var fileDescriptor_fef0af3bd5248f34 = []byte{
-	// 778 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xcf, 0x6f, 0xe3, 0x44,
-	0x14, 0x8e, 0xf3, 0xa3, 0x49, 0xa6, 0xe9, 0x26, 0x3b, 0xbb, 0xd2, 0x86, 0x6a, 0x65, 0x47, 0x59,
-	0x90, 0x96, 0x05, 0x1c, 0x54, 0x24, 0xe0, 0x02, 0x28, 0xbf, 0x20, 0x11, 0x8b, 0x13, 0xd9, 0xe9,
-	0x22, 0xb8, 0x8c, 0x26, 0xf6, 0x24, 0x19, 0x91, 0xd8, 0x23, 0xcf, 0x64, 0xb5, 0xea, 0x89, 0x23,
-	0x47, 0xfe, 0x05, 0x6e, 0xfc, 0x29, 0x1c, 0x7b, 0xec, 0xc9, 0xa2, 0x2e, 0x07, 0x8e, 0xfd, 0x13,
-	0x90, 0x9f, 0x93, 0xb6, 0x69, 0x25, 0xda, 0x93, 0x67, 0xde, 0xfb, 0xbe, 0xef, 0xbd, 0xf9, 0x3e,
-	0xc9, 0xc8, 0x94, 0xa1, 0xdb, 0x7a, 0xcb, 0x4f, 0x38, 0x0b, 0x5b, 0x92, 0x85, 0x6f, 0xb9, 0xcb,
-	0x64, 0x4b, 0x2e, 0x68, 0xc8, 0xbc, 0x16, 0x9d, 0x33, 0x5f, 0x89, 0x69, 0xfa, 0x35, 0x45, 0x18,
-	0xa8, 0x00, 0x1b, 0xe2, 0x9d, 0x99, 0xc2, 0xcd, 0x2d, 0xdc, 0x4c, 0xe1, 0x26, 0xc0, 0x0e, 0x3f,
-	0x99, 0x73, 0xb5, 0x58, 0x4f, 0x4d, 0x37, 0x58, 0xb5, 0xe6, 0xc1, 0x3c, 0x68, 0x01, 0x6f, 0xba,
-	0x9e, 0xc1, 0x0d, 0x2e, 0x70, 0x4a, 0xf5, 0x0e, 0x8d, 0x64, 0x3e, 0x15, 0x3c, 0x85, 0xb5, 0xd6,
-	0x6b, 0xee, 0x89, 0x29, 0x7c, 0x52, 0x40, 0xf3, 0x4b, 0xf4, 0xb8, 0x9d, 0x08, 0x77, 0xa9, 0xa0,
-	0x53, 0xbe, 0xe4, 0x8a, 0x33, 0x89, 0x5f, 0xa0, 0x03, 0x37, 0x58, 0x2e, 0x99, 0xab, 0x24, 0xf1,
-	0xa8, 0xa2, 0x75, 0xad, 0xa1, 0xbd, 0x2c, 0xd9, 0x95, 0x6d, 0xb1, 0x47, 0x15, 0x6d, 0xfe, 0xa6,
-	0xa1, 0x2a, 0x50, 0xc7, 0x34, 0xa4, 0x2b, 0xa6, 0x58, 0x28, 0xf1, 0x1a, 0x7d, 0x20, 0xc2, 0x60,
-	0xc6, 0x97, 0x2c, 0x24, 0x52, 0x51, 0xf7, 0x17, 0xa2, 0x42, 0xea, 0x32, 0x22, 0xe9, 0x4a, 0x2c,
-	0x19, 0x11, 0x2c, 0xe4, 0x81, 0x47, 0x56, 0x12, 0x04, 0x0b, 0x9d, 0xf7, 0xe3, 0xc8, 0x68, 0x8c,
-	0x37, 0x04, 0x27, 0xc1, 0x4f, 0x12, 0xb8, 0x03, 0xe8, 0x31, 0x80, 0x7f, 0x70, 0xec, 0x86, 0xf8,
-	0x7f, 0x84, 0x6c, 0xfe, 0x93, 0x45, 0x65, 0x58, 0x65, 0xe8, 0xcf, 0x02, 0xfc, 0x05, 0x2a, 0x81,
-	0x57, 0x84, 0x7b, 0x30, 0x67, 0xff, 0xa8, 0x6a, 0x8a, 0x77, 0x66, 0xfa, 0x76, 0xf3, 0xf8, 0x78,
-	0xd8, 0xeb, 0xec, 0xc7, 0x91, 0x51, 0x4c, 0x19, 0x3d, 0xbb, 0x08, 0xe8, 0xa1, 0x87, 0xbf, 0x45,
-	0xe5, 0x45, 0x20, 0x15, 0xe1, 0xfe, 0x2c, 0xa8, 0x67, 0x81, 0xf9, 0xa1, 0x79, 0x4f, 0x20, 0xe6,
-	0x20, 0x90, 0x30, 0xd6, 0x2e, 0x2d, 0x36, 0x27, 0xfc, 0x31, 0x42, 0x5c, 0x10, 0xea, 0x79, 0x21,
-	0x93, 0xb2, 0x9e, 0x6b, 0x68, 0x2f, 0xcb, 0x9d, 0x83, 0x38, 0x32, 0xca, 0xc3, 0x71, 0x3b, 0x2d,
-	0xda, 0x65, 0x2e, 0x36, 0x47, 0xfc, 0x06, 0x55, 0xdc, 0x1b, 0xe6, 0xd7, 0xf3, 0x30, 0xf8, 0xe8,
-	0xde, 0xc1, 0x77, 0x62, 0xb3, 0x77, 0x74, 0xf0, 0x18, 0x21, 0x71, 0x95, 0x4c, 0xbd, 0x00, 0xaa,
-	0x9f, 0x3e, 0x4c, 0xf5, 0x3a, 0x51, 0xfb, 0x86, 0x46, 0x73, 0x86, 0x4a, 0xdb, 0xd7, 0xe2, 0x43,
-	0x04, 0xef, 0xf5, 0xe9, 0x8a, 0x81, 0xc9, 0x65, 0xfb, 0xea, 0x8e, 0xdf, 0x43, 0x25, 0x11, 0x78,
-	0x04, 0x7a, 0x59, 0xe8, 0x15, 0x45, 0xe0, 0x59, 0x49, 0xeb, 0x05, 0x2a, 0xa6, 0x16, 0x8b, 0x8d,
-	0x2f, 0x28, 0x8e, 0x8c, 0x3d, 0x50, 0x1d, 0xdb, 0x7b, 0xe0, 0xa0, 0x68, 0x46, 0x1a, 0x2a, 0xc0,
-	0x1e, 0xf8, 0x6b, 0x94, 0x87, 0x30, 0xd2, 0x18, 0x5f, 0x3d, 0x6c, 0x7b, 0x48, 0x03, 0x78, 0xf8,
-	0x73, 0xf4, 0xc8, 0x0d, 0x19, 0x55, 0x8c, 0x28, 0xbe, 0x62, 0xc4, 0x97, 0xb0, 0x4f, 0xae, 0x53,
-	0x8b, 0x23, 0xa3, 0xd2, 0x85, 0xce, 0x84, 0xaf, 0x98, 0xe5, 0xd8, 0x15, 0xf7, 0xfa, 0x26, 0xf1,
-	0x37, 0xe8, 0xf1, 0x92, 0x4a, 0x45, 0x16, 0x8c, 0x86, 0x6a, 0xca, 0xa8, 0x4a, 0xa8, 0x39, 0xa0,
-	0x3e, 0x89, 0x23, 0xa3, 0xfa, 0x9a, 0x4a, 0x35, 0xd8, 0xf6, 0x2c, 0xc7, 0xae, 0x2e, 0x77, 0x0a,
-	0x12, 0x3f, 0x47, 0x79, 0x2a, 0xb9, 0x07, 0x61, 0x1e, 0x74, 0x4a, 0x71, 0x64, 0xe4, 0xdb, 0xce,
-	0xb0, 0x67, 0x43, 0xb5, 0xf9, 0x87, 0x86, 0xf6, 0x61, 0x55, 0x47, 0x51, 0xb5, 0x96, 0x78, 0x84,
-	0x9e, 0xf9, 0x92, 0x48, 0xee, 0xbb, 0x8c, 0xec, 0xce, 0x85, 0x97, 0xe7, 0x3a, 0xf5, 0x38, 0x32,
-	0x9e, 0x5a, 0x8e, 0x93, 0x20, 0x76, 0x66, 0xdb, 0x4f, 0x7d, 0x79, 0xb7, 0x8a, 0xdb, 0xa8, 0x20,
-	0x15, 0x55, 0xa9, 0xfd, 0x8f, 0x8e, 0x3e, 0x7a, 0x98, 0x71, 0xc9, 0x36, 0xcc, 0x4e, 0x99, 0xaf,
-	0x4e, 0x10, 0xba, 0x2e, 0xe2, 0x67, 0xe8, 0x49, 0xfb, 0xbb, 0xbe, 0x35, 0x21, 0xce, 0xa4, 0x3d,
-	0xe9, 0x93, 0x63, 0xeb, 0x7b, 0x6b, 0xf4, 0xa3, 0x55, 0xcb, 0xdc, 0x6e, 0x0c, 0xfa, 0xed, 0xd7,
-	0x93, 0xc1, 0x4f, 0x35, 0x0d, 0x3f, 0x47, 0xf5, 0x5d, 0x86, 0xdd, 0x77, 0xc6, 0x23, 0xcb, 0x19,
-	0xbe, 0xe9, 0xd7, 0xb2, 0xb7, 0xbb, 0xbd, 0xa1, 0xd3, 0x1d, 0x59, 0x56, 0xbf, 0x3b, 0xe9, 0xf7,
-	0x6a, 0xb9, 0xce, 0x57, 0xa7, 0xe7, 0x7a, 0xe6, 0xec, 0x5c, 0xcf, 0x5c, 0x9e, 0xeb, 0xda, 0xaf,
-	0xb1, 0xae, 0xfd, 0x19, 0xeb, 0xda, 0x5f, 0xb1, 0xae, 0x9d, 0xc6, 0xba, 0xf6, 0x77, 0xac, 0x6b,
-	0xff, 0xc6, 0x7a, 0xe6, 0x32, 0xd6, 0xb5, 0xdf, 0x2f, 0xf4, 0xcc, 0xe9, 0x85, 0x9e, 0x39, 0xbb,
-	0xd0, 0x33, 0x3f, 0x17, 0x37, 0x3f, 0xd4, 0xe9, 0x1e, 0xfc, 0xda, 0x3e, 0xfb, 0x2f, 0x00, 0x00,
-	0xff, 0xff, 0xcd, 0x84, 0x5c, 0x18, 0x7d, 0x05, 0x00, 0x00,
+	// 852 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x41, 0x6f, 0x1b, 0x45,
+	0x14, 0xf6, 0xc6, 0x49, 0x6c, 0x4f, 0xe2, 0xc6, 0x9d, 0x56, 0xaa, 0x09, 0xd5, 0x6e, 0xe4, 0x82,
+	0x54, 0x0a, 0xac, 0x51, 0x90, 0x80, 0x0b, 0x20, 0x3b, 0x76, 0x89, 0xd5, 0xb2, 0xb1, 0x66, 0x9d,
+	0x20, 0xb8, 0x8c, 0xc6, 0xbb, 0x93, 0x78, 0xa8, 0xbd, 0x3b, 0x9a, 0x19, 0x5b, 0x55, 0x4f, 0x1c,
+	0x39, 0xf2, 0x17, 0xb8, 0x71, 0xe2, 0x77, 0x70, 0xcc, 0xb1, 0xa7, 0x15, 0xd9, 0x70, 0xe0, 0xd8,
+	0x9f, 0x80, 0xf6, 0xed, 0xba, 0xa9, 0x5b, 0x89, 0xe4, 0xb4, 0xf3, 0xde, 0xf7, 0x7d, 0xef, 0xcd,
+	0xfb, 0xde, 0x68, 0x91, 0xab, 0x55, 0xd0, 0x5e, 0x88, 0x17, 0x82, 0xab, 0xb6, 0xe6, 0x6a, 0x21,
+	0x02, 0xae, 0xdb, 0x7a, 0xc2, 0x14, 0x0f, 0xdb, 0xec, 0x8c, 0x47, 0x46, 0x8e, 0xf3, 0xaf, 0x2b,
+	0x55, 0x6c, 0x62, 0xec, 0xc8, 0xe7, 0x6e, 0x4e, 0x77, 0x97, 0x74, 0x37, 0xa7, 0xbb, 0x40, 0xdb,
+	0xfd, 0xf4, 0x4c, 0x98, 0xc9, 0x7c, 0xec, 0x06, 0xf1, 0xac, 0x7d, 0x16, 0x9f, 0xc5, 0x6d, 0xd0,
+	0x8d, 0xe7, 0xa7, 0x10, 0x41, 0x00, 0xa7, 0xbc, 0xde, 0xae, 0x93, 0xf5, 0x67, 0x52, 0xe4, 0xb4,
+	0xf6, 0x7c, 0x2e, 0x42, 0x39, 0x86, 0x4f, 0x4e, 0x68, 0x7d, 0x85, 0x6e, 0x77, 0xb2, 0xc2, 0x07,
+	0x4c, 0xb2, 0xb1, 0x98, 0x0a, 0x23, 0xb8, 0xc6, 0x0f, 0x50, 0x3d, 0x88, 0xa7, 0x53, 0x1e, 0x18,
+	0x4d, 0x43, 0x66, 0x58, 0xd3, 0xda, 0xb3, 0x1e, 0x56, 0xc9, 0xf6, 0x32, 0xd9, 0x63, 0x86, 0xb5,
+	0x7e, 0xb5, 0xd0, 0x0e, 0x48, 0x87, 0x4c, 0xb1, 0x19, 0x37, 0x5c, 0x69, 0x3c, 0x47, 0x1f, 0x4a,
+	0x15, 0x9f, 0x8a, 0x29, 0x57, 0x54, 0x1b, 0x16, 0x3c, 0xa3, 0x46, 0xb1, 0x80, 0x53, 0xcd, 0x66,
+	0x72, 0xca, 0xa9, 0xe4, 0x4a, 0xc4, 0x21, 0x9d, 0x69, 0x28, 0xb8, 0xd1, 0xfd, 0x20, 0x4d, 0x9c,
+	0xbd, 0x61, 0x21, 0xf0, 0x33, 0xfe, 0x28, 0xa3, 0xfb, 0xc0, 0x1e, 0x02, 0xf9, 0x7b, 0x9f, 0xec,
+	0xc9, 0xff, 0x67, 0xe8, 0xd6, 0x3f, 0x6b, 0xa8, 0x06, 0x57, 0x19, 0x44, 0xa7, 0x31, 0xfe, 0x12,
+	0x55, 0xc1, 0x2b, 0x2a, 0x42, 0xe8, 0xb3, 0xb5, 0xbf, 0xe3, 0xca, 0xe7, 0x6e, 0x3e, 0xbb, 0x7b,
+	0x7c, 0x3c, 0xe8, 0x75, 0xb7, 0xd2, 0xc4, 0xa9, 0xe4, 0x8a, 0x1e, 0xa9, 0x00, 0x7b, 0x10, 0xe2,
+	0xc7, 0xa8, 0x36, 0x89, 0xb5, 0xa1, 0x22, 0x3a, 0x8d, 0x9b, 0x6b, 0xa0, 0xfc, 0xc8, 0xbd, 0x66,
+	0x21, 0xee, 0x61, 0xac, 0xa1, 0x2d, 0xa9, 0x4e, 0x8a, 0x13, 0xfe, 0x04, 0x21, 0x21, 0x29, 0x0b,
+	0x43, 0xc5, 0xb5, 0x6e, 0x96, 0xf7, 0xac, 0x87, 0xb5, 0x6e, 0x3d, 0x4d, 0x9c, 0xda, 0x60, 0xd8,
+	0xc9, 0x93, 0xa4, 0x26, 0x64, 0x71, 0xc4, 0x27, 0x68, 0x3b, 0x78, 0xc3, 0xfc, 0xe6, 0x3a, 0x34,
+	0xde, 0xbf, 0xb6, 0xf1, 0x3b, 0x6b, 0x23, 0x2b, 0x75, 0xf0, 0x10, 0x21, 0xf9, 0x7a, 0x33, 0xcd,
+	0x0d, 0xa8, 0xfa, 0xd9, 0xcd, 0xaa, 0x5e, 0x6d, 0x94, 0xbc, 0x51, 0xa3, 0x15, 0xa0, 0xfa, 0x13,
+	0xae, 0x22, 0x3e, 0x3d, 0xe1, 0x4a, 0x8b, 0x38, 0xc2, 0x4d, 0x54, 0x59, 0xe4, 0x47, 0x30, 0xba,
+	0x4e, 0x96, 0x21, 0x7e, 0x1f, 0xd5, 0x66, 0xec, 0xe7, 0x58, 0x51, 0xc5, 0x17, 0x60, 0x65, 0x9d,
+	0x54, 0x21, 0x41, 0xf8, 0x02, 0x40, 0x11, 0x15, 0x60, 0xb9, 0x00, 0xb3, 0x04, 0xe1, 0x8b, 0xd6,
+	0x9f, 0x16, 0xaa, 0x2e, 0x3d, 0xc5, 0xbb, 0x08, 0x5c, 0x8d, 0xd8, 0x8c, 0x43, 0x87, 0x1a, 0x79,
+	0x1d, 0xe3, 0xf7, 0x50, 0x55, 0xc6, 0x21, 0x05, 0x6c, 0x0d, 0xb0, 0x8a, 0x8c, 0x43, 0x2f, 0x83,
+	0x1e, 0xa0, 0x4a, 0xbe, 0x48, 0x59, 0xb8, 0x8f, 0xd2, 0xc4, 0xd9, 0x84, 0xaa, 0x43, 0xb2, 0x09,
+	0x7b, 0x92, 0xf8, 0x31, 0xda, 0x7c, 0x06, 0xd3, 0x14, 0x8e, 0xbb, 0xd7, 0x7a, 0xb3, 0x32, 0x3c,
+	0x29, 0xd4, 0xad, 0xc4, 0x42, 0x1b, 0xe0, 0x1a, 0xfe, 0x06, 0xad, 0xc3, 0xd3, 0xc9, 0x1f, 0xdd,
+	0xa3, 0x9b, 0x79, 0x0d, 0x6f, 0x07, 0x74, 0xf8, 0x0b, 0x74, 0x2b, 0x50, 0x9c, 0x19, 0x4e, 0x8d,
+	0x98, 0x71, 0x1a, 0x69, 0x98, 0xab, 0xdc, 0x6d, 0xa4, 0x89, 0xb3, 0x7d, 0x00, 0xc8, 0x48, 0xcc,
+	0xb8, 0xe7, 0x93, 0xed, 0xe0, 0x2a, 0xd2, 0xf8, 0x5b, 0x74, 0x7b, 0xca, 0xb4, 0xa1, 0x13, 0xce,
+	0x94, 0x19, 0x73, 0x66, 0x32, 0x69, 0x19, 0xa4, 0x77, 0xd2, 0xc4, 0xd9, 0x79, 0xca, 0xb4, 0x39,
+	0x5c, 0x62, 0x9e, 0x4f, 0x76, 0xa6, 0x2b, 0x09, 0x8d, 0xef, 0xa3, 0x75, 0xa6, 0x45, 0x08, 0x46,
+	0xd4, 0xbb, 0xd5, 0x34, 0x71, 0xd6, 0x3b, 0xfe, 0xa0, 0x47, 0x20, 0xdb, 0xfa, 0xdd, 0x42, 0x5b,
+	0x70, 0x55, 0xdf, 0x30, 0x33, 0xd7, 0xf8, 0x08, 0xdd, 0x8b, 0x34, 0xd5, 0x22, 0x0a, 0x38, 0x5d,
+	0xed, 0x0b, 0x93, 0x97, 0xbb, 0xcd, 0x34, 0x71, 0xee, 0x7a, 0xbe, 0x9f, 0x31, 0x56, 0x7a, 0x93,
+	0xbb, 0x91, 0x7e, 0x37, 0x8b, 0x3b, 0x68, 0x43, 0x1b, 0x66, 0xf2, 0x35, 0xde, 0xda, 0xff, 0xf8,
+	0x66, 0xc6, 0x65, 0xb7, 0xe1, 0x24, 0x57, 0x3e, 0x7a, 0x81, 0xd0, 0x55, 0x12, 0xdf, 0x43, 0x77,
+	0x3a, 0xdf, 0xf5, 0xbd, 0x11, 0xf5, 0x47, 0x9d, 0x51, 0x9f, 0x1e, 0x7b, 0x4f, 0xbc, 0xa3, 0x1f,
+	0xbc, 0x46, 0xe9, 0x6d, 0xe0, 0xb0, 0xdf, 0x79, 0x3a, 0x3a, 0xfc, 0xb1, 0x61, 0xe1, 0xfb, 0xa8,
+	0xb9, 0xaa, 0x20, 0x7d, 0x7f, 0x78, 0xe4, 0xf9, 0x83, 0x93, 0x7e, 0x63, 0xed, 0x6d, 0xb4, 0x37,
+	0xf0, 0x0f, 0x8e, 0x3c, 0xaf, 0x7f, 0x30, 0xea, 0xf7, 0x1a, 0xe5, 0xee, 0xd7, 0xe7, 0x17, 0x76,
+	0xe9, 0xe5, 0x85, 0x5d, 0x7a, 0x75, 0x61, 0x5b, 0xbf, 0xa4, 0xb6, 0xf5, 0x47, 0x6a, 0x5b, 0x7f,
+	0xa5, 0xb6, 0x75, 0x9e, 0xda, 0xd6, 0xdf, 0xa9, 0x6d, 0xfd, 0x9b, 0xda, 0xa5, 0x57, 0xa9, 0x6d,
+	0xfd, 0x76, 0x69, 0x97, 0xce, 0x2f, 0xed, 0xd2, 0xcb, 0x4b, 0xbb, 0xf4, 0x53, 0xa5, 0xf8, 0xfd,
+	0x8f, 0x37, 0xe1, 0x47, 0xfc, 0xf9, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x6f, 0x91, 0x00, 0xa3,
+	0x2b, 0x06, 0x00, 0x00,
 }
 
 func (x AgentState) String() string {
@@ -550,6 +623,36 @@ func (this *AgentInfo) Equal(that interface{}) bool {
 	}
 	return true
 }
+func (this *KernelVersion) Equal(that interface{}) bool {
+	if that == nil {
+		return this == nil
+	}
+
+	that1, ok := that.(*KernelVersion)
+	if !ok {
+		that2, ok := that.(KernelVersion)
+		if ok {
+			that1 = &that2
+		} else {
+			return false
+		}
+	}
+	if that1 == nil {
+		return this == nil
+	} else if this == nil {
+		return false
+	}
+	if this.Version != that1.Version {
+		return false
+	}
+	if this.MajorRev != that1.MajorRev {
+		return false
+	}
+	if this.MinorRev != that1.MinorRev {
+		return false
+	}
+	return true
+}
 func (this *HostInfo) Equal(that interface{}) bool {
 	if that == nil {
 		return this == nil
@@ -578,6 +681,9 @@ func (this *HostInfo) Equal(that interface{}) bool {
 	if this.HostIP != that1.HostIP {
 		return false
 	}
+	if !this.Kernel.Equal(that1.Kernel) {
+		return false
+	}
 	return true
 }
 func (this *Agent) Equal(that interface{}) bool {
@@ -682,15 +788,30 @@ func (this *AgentInfo) GoString() string {
 	s = append(s, "}")
 	return strings.Join(s, "")
 }
-func (this *HostInfo) GoString() string {
+func (this *KernelVersion) GoString() string {
 	if this == nil {
 		return "nil"
 	}
 	s := make([]string, 0, 7)
+	s = append(s, "&agentpb.KernelVersion{")
+	s = append(s, "Version: "+fmt.Sprintf("%#v", this.Version)+",\n")
+	s = append(s, "MajorRev: "+fmt.Sprintf("%#v", this.MajorRev)+",\n")
+	s = append(s, "MinorRev: "+fmt.Sprintf("%#v", this.MinorRev)+",\n")
+	s = append(s, "}")
+	return strings.Join(s, "")
+}
+func (this *HostInfo) GoString() string {
+	if this == nil {
+		return "nil"
+	}
+	s := make([]string, 0, 8)
 	s = append(s, "&agentpb.HostInfo{")
 	s = append(s, "Hostname: "+fmt.Sprintf("%#v", this.Hostname)+",\n")
 	s = append(s, "PodName: "+fmt.Sprintf("%#v", this.PodName)+",\n")
 	s = append(s, "HostIP: "+fmt.Sprintf("%#v", this.HostIP)+",\n")
+	if this.Kernel != nil {
+		s = append(s, "Kernel: "+fmt.Sprintf("%#v", this.Kernel)+",\n")
+	}
 	s = append(s, "}")
 	return strings.Join(s, "")
 }
@@ -867,6 +988,44 @@ func (m *AgentInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *KernelVersion) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *KernelVersion) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *KernelVersion) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.MinorRev != 0 {
+		i = encodeVarintAgent(dAtA, i, uint64(m.MinorRev))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.MajorRev != 0 {
+		i = encodeVarintAgent(dAtA, i, uint64(m.MajorRev))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.Version != 0 {
+		i = encodeVarintAgent(dAtA, i, uint64(m.Version))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
 func (m *HostInfo) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -887,6 +1046,18 @@ func (m *HostInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.Kernel != nil {
+		{
+			size, err := m.Kernel.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintAgent(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x22
+	}
 	if len(m.HostIP) > 0 {
 		i -= len(m.HostIP)
 		copy(dAtA[i:], m.HostIP)
@@ -1058,6 +1229,24 @@ func (m *AgentInfo) Size() (n int) {
 	return n
 }
 
+func (m *KernelVersion) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Version != 0 {
+		n += 1 + sovAgent(uint64(m.Version))
+	}
+	if m.MajorRev != 0 {
+		n += 1 + sovAgent(uint64(m.MajorRev))
+	}
+	if m.MinorRev != 0 {
+		n += 1 + sovAgent(uint64(m.MinorRev))
+	}
+	return n
+}
+
 func (m *HostInfo) Size() (n int) {
 	if m == nil {
 		return 0
@@ -1076,6 +1265,10 @@ func (m *HostInfo) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovAgent(uint64(l))
 	}
+	if m.Kernel != nil {
+		l = m.Kernel.Size()
+		n += 1 + l + sovAgent(uint64(l))
+	}
 	return n
 }
 
@@ -1156,6 +1349,18 @@ func (this *AgentInfo) String() string {
 	}, "")
 	return s
 }
+func (this *KernelVersion) String() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings.Join([]string{`&KernelVersion{`,
+		`Version:` + fmt.Sprintf("%v", this.Version) + `,`,
+		`MajorRev:` + fmt.Sprintf("%v", this.MajorRev) + `,`,
+		`MinorRev:` + fmt.Sprintf("%v", this.MinorRev) + `,`,
+		`}`,
+	}, "")
+	return s
+}
 func (this *HostInfo) String() string {
 	if this == nil {
 		return "nil"
@@ -1164,6 +1369,7 @@ func (this *HostInfo) String() string {
 		`Hostname:` + fmt.Sprintf("%v", this.Hostname) + `,`,
 		`PodName:` + fmt.Sprintf("%v", this.PodName) + `,`,
 		`HostIP:` + fmt.Sprintf("%v", this.HostIP) + `,`,
+		`Kernel:` + strings.Replace(this.Kernel.String(), "KernelVersion", "KernelVersion", 1) + `,`,
 		`}`,
 	}, "")
 	return s
@@ -1565,6 +1771,113 @@ func (m *AgentInfo) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *KernelVersion) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowAgent
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: KernelVersion: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: KernelVersion: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
+			}
+			m.Version = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAgent
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Version |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MajorRev", wireType)
+			}
+			m.MajorRev = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAgent
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.MajorRev |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MinorRev", wireType)
+			}
+			m.MinorRev = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAgent
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.MinorRev |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipAgent(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthAgent
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *HostInfo) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
@@ -1690,6 +2003,42 @@ func (m *HostInfo) Unmarshal(dAtA []byte) error {
 			}
 			m.HostIP = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAgent
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthAgent
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthAgent
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Kernel == nil {
+				m.Kernel = &KernelVersion{}
+			}
+			if err := m.Kernel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipAgent(dAtA[iNdEx:])
diff --git a/src/vizier/services/shared/agentpb/agent.proto b/src/vizier/services/shared/agentpb/agent.proto
index 728eef61a2c..a0565641cf2 100644
--- a/src/vizier/services/shared/agentpb/agent.proto
+++ b/src/vizier/services/shared/agentpb/agent.proto
@@ -44,6 +44,12 @@ message AgentInfo {
   AgentParameters parameters = 5;
 }
 
+message KernelVersion {
+  uint32 version = 1;
+  uint32 major_rev = 2;
+  uint32 minor_rev = 3;
+}
+
 // HostInfo contains the details for the host (OS, kernel, CPU, etc).
 message HostInfo {
   // The Hostname as reported by pod. This will be the machine hostname for Daemonsets and assigned
@@ -54,6 +60,8 @@ message HostInfo {
   string pod_name = 2;
   // The IP of the host that pod is running on. This can used to avoid extra K8s lookups.
   string host_ip = 3 [ (gogoproto.customname) = "HostIP" ];
+  // Version of the kernel running on the host.
+  KernelVersion kernel = 4;
 }
 
 // Agent contains information about a specific agent instance.


Test patch:
diff --git a/src/common/system/kernel_version_test.cc b/src/common/system/kernel_version_test.cc
index f6fb0702d00..2cd9fdb7eaf 100644
--- a/src/common/system/kernel_version_test.cc
+++ b/src/common/system/kernel_version_test.cc
@@ -37,10 +37,6 @@ std::string GetPathToTestDataFile(const std::string_view fname) {
 using ::px::testing::TempDir;
 using ::testing::EndsWith;
 
-bool operator==(const KernelVersion& a, const KernelVersion& b) {
-  return (a.version == b.version) && (a.major_rev == b.major_rev) && (a.minor_rev == b.minor_rev);
-}
-
 // A list of kernel version identification methods that use /proc only.
 // Used in a number of tests below.
 const std::vector<KernelVersionSource> kProcFSKernelVersionSources = {
diff --git a/src/vizier/services/agent/shared/manager/registration_test.cc b/src/vizier/services/agent/shared/manager/registration_test.cc
index a419073c3f3..3811ed47e08 100644
--- a/src/vizier/services/agent/shared/manager/registration_test.cc
+++ b/src/vizier/services/agent/shared/manager/registration_test.cc
@@ -62,6 +62,8 @@ class RegistrationHandlerTest : public ::testing::Test {
     agent_info_.pod_name = "pod_name";
     agent_info_.host_ip = "host_ip";
     agent_info_.capabilities.set_collects_data(true);
+    agent_info_.kernel_version =
+        system::ParseKernelVersionString("5.15.0-106-generic").ValueOrDie();
 
     auto register_hook = [this](uint32_t asid) -> Status {
       called_register_++;
@@ -112,6 +114,9 @@ TEST_F(RegistrationHandlerTest, RegisterAgent) {
   EXPECT_EQ(agent_info_.hostname, req.info().host_info().hostname());
   EXPECT_EQ(agent_info_.pod_name, req.info().host_info().pod_name());
   EXPECT_EQ(agent_info_.host_ip, req.info().host_info().host_ip());
+  EXPECT_EQ(agent_info_.kernel_version.version, req.info().host_info().kernel().version());
+  EXPECT_EQ(agent_info_.kernel_version.major_rev, req.info().host_info().kernel().major_rev());
+  EXPECT_EQ(agent_info_.kernel_version.minor_rev, req.info().host_info().kernel().minor_rev());
 
   auto registration_ack = std::make_unique<messages::VizierMessage>();
   registration_ack->mutable_register_agent_response()->set_asid(10);
diff --git a/src/vizier/services/metadata/controllers/agent/agent_test.go b/src/vizier/services/metadata/controllers/agent/agent_test.go
index 2183f6cb682..18968f4d8b6 100644
--- a/src/vizier/services/metadata/controllers/agent/agent_test.go
+++ b/src/vizier/services/metadata/controllers/agent/agent_test.go
@@ -118,6 +118,11 @@ func TestRegisterAgent(t *testing.T) {
 			HostInfo: &agentpb.HostInfo{
 				Hostname: "localhost",
 				HostIP:   "127.0.0.4",
+				Kernel: &agentpb.KernelVersion{
+					Version:  5,
+					MajorRev: 19,
+					MinorRev: 0,
+				},
 			},
 			AgentID: upb,
 			Capabilities: &agentpb.AgentCapabilities{