feat: netstat pod support

talosctl netstat -k show all host and non-hostnetwork pods sockets/connections.
talosctl netstat namespace/pod shows sockets/connections of a specific pod +
autocompletes in the shell.

Signed-off-by: Nico Berlee <nico.berlee@on2it.net>
Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Nico Berlee 2023-03-24 08:36:19 +01:00 committed by Andrey Smirnov
parent 52e857f55e
commit 0af8fe2fb5
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
13 changed files with 2014 additions and 1326 deletions

View File

@ -608,6 +608,7 @@ message ContainerInfo {
string status = 5;
string pod_id = 6;
string name = 7;
string network_namespace = 8;
}
// The messages message containing the requested containers.
@ -1223,6 +1224,12 @@ message NetstatRequest {
bool raw6 = 8;
}
L4proto l4proto = 3;
message NetNS {
bool hostnetwork = 1;
repeated string netns = 2;
bool allnetns = 3;
}
NetNS netns = 4;
}
message ConnectRecord {
@ -1268,6 +1275,7 @@ message ConnectRecord {
string name = 2;
}
Process process = 17;
string netns = 18;
}
message Netstat {

View File

@ -12,9 +12,11 @@ import (
"strings"
"text/tabwriter"
criconstants "github.com/containerd/containerd/pkg/cri/constants"
"github.com/spf13/cobra"
"github.com/siderolabs/talos/pkg/cli"
"github.com/siderolabs/talos/pkg/machinery/api/common"
"github.com/siderolabs/talos/pkg/machinery/api/machine"
"github.com/siderolabs/talos/pkg/machinery/client"
)
@ -26,6 +28,7 @@ var netstatCmdFlags struct {
timers bool
listening bool
all bool
pods bool
tcp bool
udp bool
udplite bool
@ -34,26 +37,102 @@ var netstatCmdFlags struct {
ipv6 bool
}
// netstatCmd represents the ls command.
type netstat struct {
client *client.Client
NodeNetNSPods map[string]map[string]string
}
// netstatCmd represents the netstat command.
var netstatCmd = &cobra.Command{
Use: "netstat",
Aliases: []string{"ss"},
Short: "Retrieve a socket listing of connections",
Long: ``,
Args: cobra.NoArgs,
Short: "Show network connections and sockets",
Long: `Show network connections and sockets.
You can pass an optional argument to view a specific pod's connections.
To do this, format the argument as "namespace/pod".
Note that only pods with a pod network namespace are allowed.
If you don't pass an argument, the command will show host connections.`,
Args: cobra.MaximumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp
}
var podList []string
if WithClient(func(ctx context.Context, c *client.Client) error {
n := netstat{
NodeNetNSPods: make(map[string]map[string]string),
client: c,
}
err := n.getPodNetNsFromNode(ctx)
if err != nil {
return err
}
for _, netNsPods := range n.NodeNetNSPods {
for _, podName := range netNsPods {
podList = append(podList, podName)
}
}
return nil
}) != nil {
return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp
}
return podList, cobra.ShellCompDirectiveNoFileComp
},
RunE: func(cmd *cobra.Command, args []string) error {
return WithClient(func(ctx context.Context, c *client.Client) error {
req := netstatFlagsToRequest()
req := netstatFlagsToRequest()
return WithClient(func(ctx context.Context, c *client.Client) (err error) {
if netstatCmdFlags.pods && len(args) > 0 {
return fmt.Errorf("cannot use --pods and specify a pod")
}
findThePod := len(args) > 0
n := netstat{
client: c,
}
n.NodeNetNSPods = make(map[string]map[string]string)
if findThePod || netstatCmdFlags.pods {
err = n.getPodNetNsFromNode(ctx)
if err != nil {
return err
}
}
if findThePod {
var foundNode, foundNetNs string
foundNode, foundNetNs = n.findPodNetNs(args[0])
if foundNetNs == "" {
cli.Fatalf("pod %s not found", args[0])
}
ctx = client.WithNode(ctx, foundNode)
req.Netns.Netns = []string{foundNetNs}
req.Netns.Hostnetwork = false
}
response, err := c.Netstat(ctx, req)
if err != nil {
if response == nil {
return fmt.Errorf("error getting netstat: %w", err)
return err
}
cli.Warning("%s", err)
}
err = printNetstat(response)
err = n.printNetstat(response)
return err
})
@ -76,6 +155,10 @@ func netstatFlagsToRequest() *machine.NetstatRequest {
Raw: netstatCmdFlags.raw,
Raw6: netstatCmdFlags.raw,
},
Netns: &machine.NetstatRequest_NetNS{
Allnetns: netstatCmdFlags.pods,
Hostnetwork: true,
},
}
switch {
@ -122,37 +205,61 @@ func netstatFlagsToRequest() *machine.NetstatRequest {
return &req
}
func (n *netstat) getPodNetNsFromNode(ctx context.Context) (err error) {
resp, err := n.client.Containers(ctx, criconstants.K8sContainerdNamespace, common.ContainerDriver_CRI)
if err != nil {
cli.Warning("error getting containers: %v", err)
return err
}
for _, msg := range resp.Messages {
for _, p := range msg.Containers {
if p.NetworkNamespace == "" {
continue
}
if p.Pid == 0 {
continue
}
if p.Id != p.PodId {
continue
}
if n.NodeNetNSPods[msg.Metadata.Hostname] == nil {
n.NodeNetNSPods[msg.Metadata.Hostname] = make(map[string]string)
}
n.NodeNetNSPods[msg.Metadata.Hostname][p.NetworkNamespace] = p.Id
}
}
return nil
}
func (n *netstat) findPodNetNs(findNamespaceAndPod string) (string, string) {
var foundNetNs, foundNode string
for node, netNSPods := range n.NodeNetNSPods {
for NetNs, podName := range netNSPods {
if podName == strings.ToLower(findNamespaceAndPod) {
foundNetNs = NetNs
foundNode = node
break
}
}
}
return foundNode, foundNetNs
}
//nolint:gocyclo
func printNetstat(response *machine.NetstatResponse) error {
func (n *netstat) printNetstat(response *machine.NetstatResponse) error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
node := ""
labels := strings.Join(
[]string{
"Proto",
"Recv-Q",
"Send-Q",
"Local Address",
"Foreign Address",
"State",
}, "\t")
if netstatCmdFlags.extend {
labels += "\t" + strings.Join(
[]string{
"Uid",
"Inode",
}, "\t")
}
if netstatCmdFlags.pid {
labels += "\t" + "PID/Program name"
}
if netstatCmdFlags.timers {
labels += "\t" + "Timer"
}
for i, message := range response.Messages {
if message.Metadata != nil && message.Metadata.Hostname != "" {
node = message.Metadata.Hostname
@ -164,6 +271,8 @@ func printNetstat(response *machine.NetstatResponse) error {
for j, record := range message.Connectrecord {
if i == 0 && j == 0 {
labels := netstatSummaryLabels()
if node != "" {
fmt.Fprintln(w, "NODE\t"+labels)
} else {
@ -210,6 +319,18 @@ func printNetstat(response *machine.NetstatResponse) error {
}
}
if netstatCmdFlags.pods {
if record.Netns == "" || node == "" || n.NodeNetNSPods[node] == nil {
args = append(args, []interface{}{
"-",
}...)
} else {
args = append(args, []interface{}{
n.NodeNetNSPods[node][record.Netns],
}...)
}
}
if netstatCmdFlags.timers {
timerwhen := strconv.FormatFloat(float64(record.Timerwhen)/100, 'f', 2, 64)
@ -228,6 +349,40 @@ func printNetstat(response *machine.NetstatResponse) error {
return w.Flush()
}
func netstatSummaryLabels() (labels string) {
labels = strings.Join(
[]string{
"Proto",
"Recv-Q",
"Send-Q",
"Local Address",
"Foreign Address",
"State",
}, "\t")
if netstatCmdFlags.extend {
labels += "\t" + strings.Join(
[]string{
"Uid",
"Inode",
}, "\t")
}
if netstatCmdFlags.pid {
labels += "\t" + "PID/Program name"
}
if netstatCmdFlags.pods {
labels += "\t" + "Pod"
}
if netstatCmdFlags.timers {
labels += "\t" + "Timer"
}
return labels
}
func wildcardIfZero(num uint32) string {
if num == 0 {
return "*"
@ -244,12 +399,13 @@ func init() {
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.timers, "timers", "o", false, "display timers")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.listening, "listening", "l", false, "display listening server sockets")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.all, "all", "a", false, "display all sockets states (default: connected)")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.pods, "pods", "k", false, "show sockets used by Kubernetes pods")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.tcp, "tcp", "t", false, "display only TCP sockets")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.udp, "udp", "u", false, "display only UDP sockets")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.udplite, "udplite", "U", false, "display only UDPLite sockets")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.raw, "raw", "w", false, "display only RAW sockets")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.ipv4, "ipv4", "4", false, "display only ipv4 sockets")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.ipv4, "ipv6", "6", false, "display only ipv6 sockets")
netstatCmd.Flags().BoolVarP(&netstatCmdFlags.ipv6, "ipv6", "6", false, "display only ipv6 sockets")
addCommand(netstatCmd)
}

2
go.mod
View File

@ -75,7 +75,7 @@ require (
github.com/mdlayher/genetlink v1.3.1
github.com/mdlayher/netlink v1.7.1
github.com/mdlayher/netx v0.0.0-20220422152302-c711c2f8512f
github.com/nberlee/go-netstat v0.0.0-20230319161348-19cc338ee40a
github.com/nberlee/go-netstat v0.1.1
github.com/opencontainers/image-spec v1.1.0-rc2
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/packethost/packngo v0.29.0

4
go.sum
View File

@ -975,8 +975,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nberlee/go-netstat v0.0.0-20230319161348-19cc338ee40a h1:1p1sNvJOlkfWnZ01BuPF+SVHaVe/VXlMxLgnGTgPH+I=
github.com/nberlee/go-netstat v0.0.0-20230319161348-19cc338ee40a/go.mod h1:GvDCRLsUKMRN1wULkt7tpnDmjSIE6YGf5zeVq+mBO64=
github.com/nberlee/go-netstat v0.1.1 h1:8QlXkUT8pr5wJKVRgn2186+lPxbum0BkuEwbSOtHrSE=
github.com/nberlee/go-netstat v0.1.1/go.mod h1:GvDCRLsUKMRN1wULkt7tpnDmjSIE6YGf5zeVq+mBO64=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=

View File

@ -129,7 +129,7 @@ This allows to distinguish between containers with the same name.
[notes.netstat]
title = "talosctl netstat"
description="""\
Talos API was extended to support retrieving a list of network connections (sockets) from the node.
Talos API was extended to support retrieving a list of network connections (sockets) from the node and pods.
`talosctl netstat` command was added to retrieve the list of network connections.
"""

View File

@ -1438,13 +1438,14 @@ func (s *Server) Containers(ctx context.Context, in *machine.ContainersRequest)
for _, pod := range pods {
for _, container := range pod.Containers {
container := &machine.ContainerInfo{
Namespace: in.Namespace,
Id: container.Display,
PodId: pod.Name,
Name: container.Name,
Image: container.Image,
Pid: container.Pid,
Status: container.Status,
Namespace: in.Namespace,
Id: container.Display,
PodId: pod.Name,
Name: container.Name,
Image: container.Image,
Pid: container.Pid,
Status: container.Status,
NetworkNamespace: container.NetworkNamespace,
}
containers = append(containers, container)
}
@ -2345,15 +2346,18 @@ func (s *Server) Netstat(ctx context.Context, req *machine.NetstatRequest) (*mac
}
features := netstat.EnableFeatures{
TCP: req.L4Proto.Tcp,
TCP6: req.L4Proto.Tcp6,
UDP: req.L4Proto.Udp,
UDP6: req.L4Proto.Udp6,
UDPLite: req.L4Proto.Udplite,
UDPLite6: req.L4Proto.Udplite6,
Raw: req.L4Proto.Raw,
Raw6: req.L4Proto.Raw6,
PID: req.Feature.Pid,
TCP: req.L4Proto.Tcp,
TCP6: req.L4Proto.Tcp6,
UDP: req.L4Proto.Udp,
UDP6: req.L4Proto.Udp6,
UDPLite: req.L4Proto.Udplite,
UDPLite6: req.L4Proto.Udplite6,
Raw: req.L4Proto.Raw,
Raw6: req.L4Proto.Raw6,
PID: req.Feature.Pid,
NoHostNetwork: !req.Netns.Hostnetwork,
AllNetNs: req.Netns.Allnetns,
NetNsName: req.Netns.Netns,
}
var fn netstat.AcceptFn
@ -2363,11 +2367,11 @@ func (s *Server) Netstat(ctx context.Context, req *machine.NetstatRequest) (*mac
fn = func(*netstat.SockTabEntry) bool { return true }
case machine.NetstatRequest_LISTENING:
fn = func(s *netstat.SockTabEntry) bool {
return s.State == netstat.Listen
return s.RemoteEndpoint.IP.IsUnspecified() && s.RemoteEndpoint.Port == 0
}
case machine.NetstatRequest_CONNECTED:
fn = func(s *netstat.SockTabEntry) bool {
return s.State != netstat.Listen
return !s.RemoteEndpoint.IP.IsUnspecified() && s.RemoteEndpoint.Port != 0
}
}
@ -2397,6 +2401,7 @@ func (s *Server) Netstat(ctx context.Context, req *machine.NetstatRequest) (*mac
Ref: entry.Ref,
Pointer: entry.Pointer,
Process: &machine.ConnectRecord_Process{},
Netns: entry.NetNS,
}
if entry.Process != nil {
records[i].Process = &machine.ConnectRecord_Process{

View File

@ -7,12 +7,14 @@
package cli
import (
"context"
"regexp"
"strings"
"github.com/siderolabs/talos/internal/integration/base"
)
// NetstatSuite verifies etcd command.
// NetstatSuite verifies etcd command and coredns container.
type NetstatSuite struct {
base.CLISuite
}
@ -28,6 +30,14 @@ func (suite *NetstatSuite) TestListening() {
base.StdoutShouldMatch(regexp.MustCompile(`:::50000.+LISTEN.+/apid`)))
}
// TestContainers verifies that containers are listed.
func (suite *NetstatSuite) TestContainers() {
nodes := suite.DiscoverNodeInternalIPs(context.TODO())
suite.RunCLI([]string{"netstat", "--listening", "--programs", "--udp", "--ipv6", "--pods", "--nodes", strings.Join(nodes, ",")},
base.StdoutShouldMatch(regexp.MustCompile(`:::53\s+:::\*.+/coredns\s+kube-system/coredns-`)))
}
func init() {
allSuites = append(allSuites, new(NetstatSuite))
}

View File

@ -25,19 +25,20 @@ import (
type Container struct {
Inspector Inspector
Display string // Friendly Name
Name string // container name
ID string // container sha/id
Digest string // Container Digest
Image string
PodName string
Sandbox string
Status string // Running state of container
RestartCount string
LogPath string
Metrics *ContainerMetrics
Pid uint32
IsPodSandbox bool // real container or just pod sandbox
Display string // Friendly Name
Name string // container name
ID string // container sha/id
Digest string // Container Digest
Image string
PodName string
Sandbox string
Status string // Running state of container
RestartCount string
LogPath string
Metrics *ContainerMetrics
Pid uint32
IsPodSandbox bool // real container or just pod sandbox
NetworkNamespace string
}
// ContainerMetrics represents container cgroup stats.

View File

@ -9,6 +9,7 @@ import (
"context"
"encoding/json"
"fmt"
"path"
"strings"
"syscall"
"time"
@ -184,6 +185,7 @@ func findContainer(cntID string, containers []*runtimeapi.Container) (*runtimeap
return nil, false
}
// nolint: gocyclo
func (i *inspector) buildPod(sandbox *runtimeapi.PodSandbox) (*ctrs.Pod, error) {
sandboxStatus, sandboxInfo, err := i.client.PodSandboxStatus(i.ctx, sandbox.Id)
if err != nil {
@ -210,7 +212,7 @@ func (i *inspector) buildPod(sandbox *runtimeapi.PodSandbox) (*ctrs.Pod, error)
}
if info, ok := sandboxInfo["info"]; ok {
var verboseInfo map[string]interface{}
var verboseInfo map[string]any
if err := json.Unmarshal([]byte(info), &verboseInfo); err == nil {
if pid, ok := verboseInfo["pid"]; ok {
@ -225,6 +227,22 @@ func (i *inspector) buildPod(sandbox *runtimeapi.PodSandbox) (*ctrs.Pod, error)
pod.Containers[0].Digest = digest
}
}
if runtimeSpec, ok := verboseInfo["runtimeSpec"].(map[string]any); ok {
if linuxSpec, ok := runtimeSpec["linux"].(map[string]any); ok {
if namespaces, ok := linuxSpec["namespaces"].([]any); ok {
for _, n := range namespaces {
if nt, ok := n.(map[string]any); ok && nt["type"] == "network" {
if netnsPath, ok := nt["path"].(string); ok {
pod.Containers[0].NetworkNamespace = path.Base(netnsPath)
break
}
}
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3749,6 +3749,13 @@ func (m *ContainerInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.NetworkNamespace) > 0 {
i -= len(m.NetworkNamespace)
copy(dAtA[i:], m.NetworkNamespace)
i = encodeVarint(dAtA, i, uint64(len(m.NetworkNamespace)))
i--
dAtA[i] = 0x42
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
@ -9124,6 +9131,68 @@ func (m *NetstatRequest_L4Proto) MarshalToSizedBufferVT(dAtA []byte) (int, error
return len(dAtA) - i, nil
}
func (m *NetstatRequest_NetNS) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *NetstatRequest_NetNS) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *NetstatRequest_NetNS) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.Allnetns {
i--
if m.Allnetns {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x18
}
if len(m.Netns) > 0 {
for iNdEx := len(m.Netns) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Netns[iNdEx])
copy(dAtA[i:], m.Netns[iNdEx])
i = encodeVarint(dAtA, i, uint64(len(m.Netns[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if m.Hostnetwork {
i--
if m.Hostnetwork {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *NetstatRequest) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@ -9154,6 +9223,16 @@ func (m *NetstatRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.Netns != nil {
size, err := m.Netns.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarint(dAtA, i, uint64(size))
i--
dAtA[i] = 0x22
}
if m.L4Proto != nil {
size, err := m.L4Proto.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
@ -9257,6 +9336,15 @@ func (m *ConnectRecord) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.Netns) > 0 {
i -= len(m.Netns)
copy(dAtA[i:], m.Netns)
i = encodeVarint(dAtA, i, uint64(len(m.Netns)))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x92
}
if m.Process != nil {
size, err := m.Process.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
@ -11196,6 +11284,10 @@ func (m *ContainerInfo) SizeVT() (n int) {
if l > 0 {
n += 1 + l + sov(uint64(l))
}
l = len(m.NetworkNamespace)
if l > 0 {
n += 1 + l + sov(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -13310,6 +13402,28 @@ func (m *NetstatRequest_L4Proto) SizeVT() (n int) {
return n
}
func (m *NetstatRequest_NetNS) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Hostnetwork {
n += 2
}
if len(m.Netns) > 0 {
for _, s := range m.Netns {
l = len(s)
n += 1 + l + sov(uint64(l))
}
}
if m.Allnetns {
n += 2
}
n += len(m.unknownFields)
return n
}
func (m *NetstatRequest) SizeVT() (n int) {
if m == nil {
return 0
@ -13327,6 +13441,10 @@ func (m *NetstatRequest) SizeVT() (n int) {
l = m.L4Proto.SizeVT()
n += 1 + l + sov(uint64(l))
}
if m.Netns != nil {
l = m.Netns.SizeVT()
n += 1 + l + sov(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -13409,6 +13527,10 @@ func (m *ConnectRecord) SizeVT() (n int) {
l = m.Process.SizeVT()
n += 2 + l + sov(uint64(l))
}
l = len(m.Netns)
if l > 0 {
n += 2 + l + sov(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -21834,6 +21956,38 @@ func (m *ContainerInfo) UnmarshalVT(dAtA []byte) error {
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field NetworkNamespace", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.NetworkNamespace = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
@ -33961,6 +34115,129 @@ func (m *NetstatRequest_L4Proto) UnmarshalVT(dAtA []byte) error {
}
return nil
}
func (m *NetstatRequest_NetNS) UnmarshalVT(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 ErrIntOverflow
}
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: NetstatRequest_NetNS: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: NetstatRequest_NetNS: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Hostnetwork", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Hostnetwork = bool(v != 0)
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Netns", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Netns = append(m.Netns, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Allnetns", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Allnetns = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *NetstatRequest) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@ -34081,6 +34358,42 @@ func (m *NetstatRequest) UnmarshalVT(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Netns", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Netns == nil {
m.Netns = &NetstatRequest_NetNS{}
}
if err := m.Netns.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
@ -34613,6 +34926,38 @@ func (m *ConnectRecord) UnmarshalVT(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 18:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Netns", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Netns = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])

View File

@ -308,6 +308,7 @@ description: Talos gRPC API reference.
- [NetstatRequest](#machine.NetstatRequest)
- [NetstatRequest.Feature](#machine.NetstatRequest.Feature)
- [NetstatRequest.L4proto](#machine.NetstatRequest.L4proto)
- [NetstatRequest.NetNS](#machine.NetstatRequest.NetNS)
- [NetstatResponse](#machine.NetstatResponse)
- [NetworkConfig](#machine.NetworkConfig)
- [NetworkDeviceConfig](#machine.NetworkDeviceConfig)
@ -4012,6 +4013,7 @@ ConfigValidationErrorEvent is reported when config validation has failed.
| ref | [uint64](#uint64) | | |
| pointer | [uint64](#uint64) | | |
| process | [ConnectRecord.Process](#machine.ConnectRecord.Process) | | |
| netns | [string](#string) | | |
@ -4065,6 +4067,7 @@ The messages message containing the requested containers.
| status | [string](#string) | | |
| pod_id | [string](#string) | | |
| name | [string](#string) | | |
| network_namespace | [string](#string) | | |
@ -5356,6 +5359,7 @@ The messages message containing the requested df stats.
| filter | [NetstatRequest.Filter](#machine.NetstatRequest.Filter) | | |
| feature | [NetstatRequest.Feature](#machine.NetstatRequest.Feature) | | |
| l4proto | [NetstatRequest.L4proto](#machine.NetstatRequest.L4proto) | | |
| netns | [NetstatRequest.NetNS](#machine.NetstatRequest.NetNS) | | |
@ -5399,6 +5403,23 @@ The messages message containing the requested df stats.
<a name="machine.NetstatRequest.NetNS"></a>
### NetstatRequest.NetNS
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| hostnetwork | [bool](#bool) | | |
| netns | [string](#string) | repeated | |
| allnetns | [bool](#bool) | | |
<a name="machine.NetstatResponse"></a>
### NetstatResponse

View File

@ -2103,7 +2103,16 @@ talosctl mounts [flags]
## talosctl netstat
Retrieve a socket listing of connections
Show network connections and sockets
### Synopsis
Show network connections and sockets.
You can pass an optional argument to view a specific pod's connections.
To do this, format the argument as "namespace/pod".
Note that only pods with a pod network namespace are allowed.
If you don't pass an argument, the command will show host connections.
```
talosctl netstat [flags]
@ -2118,6 +2127,7 @@ talosctl netstat [flags]
-4, --ipv4 display only ipv4 sockets
-6, --ipv6 display only ipv6 sockets
-l, --listening display listening server sockets
-k, --pods show sockets used by Kubernetes pods
-p, --programs show process using socket
-w, --raw display only RAW sockets
-t, --tcp display only TCP sockets
@ -2809,7 +2819,7 @@ A CLI for out-of-band management of Kubernetes nodes created by Talos
* [talosctl memory](#talosctl-memory) - Show memory usage
* [talosctl meta](#talosctl-meta) - Write and delete keys in the META partition
* [talosctl mounts](#talosctl-mounts) - List mounts
* [talosctl netstat](#talosctl-netstat) - Retrieve a socket listing of connections
* [talosctl netstat](#talosctl-netstat) - Show network connections and sockets
* [talosctl patch](#talosctl-patch) - Update field(s) of a resource using a JSON patch.
* [talosctl pcap](#talosctl-pcap) - Capture the network packets from the node.
* [talosctl processes](#talosctl-processes) - List running processes