Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(crit): add crit x sk for sockets #138

Merged
merged 4 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions crit/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,9 @@

// The `crit x` command
var xCmd = &cobra.Command{
Use: "x DIR {ps|fds|mems|rss}",
Use: "x DIR {ps|fd|mem|rss|sk}",
Short: "Explore the image directory",
Long: "Explore the image directory with one of (ps, fds, mems, rss) options",
Long: "Explore the image directory with one of (ps, fd, mem, rss, sk) options",
// Exactly two arguments are required:
// * Path of the input directory
// * Explore type
Expand All @@ -242,14 +242,16 @@
switch args[1] {
case "ps":
xData, err = c.ExplorePs()
case "fds":
case "fd", "fds":
xData, err = c.ExploreFds()
case "mems":
case "mem", "mems":
xData, err = c.ExploreMems()
case "rss":
xData, err = c.ExploreRss()
case "sk":
xData, err = c.ExploreSk()
default:
err = errors.New("error exploring directory: invalid explore type")
err = errors.New("invalid explore type (supported: {ps|fd|mem|rss|sk})")

Check warning on line 254 in crit/cli/cli.go

View check run for this annotation

Codecov / codecov/patch

crit/cli/cli.go#L254

Added line #L254 was not covered by tests
}
if err != nil {
log.Fatal(fmt.Errorf("error exploring directory: %w", err))
Expand Down
1 change: 1 addition & 0 deletions crit/crit.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Critter interface {
ExploreFds() ([]*Fd, error)
ExploreMems() ([]*MemMap, error)
ExploreRss() ([]*RssMap, error)
ExploreSk() ([]*Sk, error)
}

// crit implements the Critter interface. It contains:
Expand Down
118 changes: 117 additions & 1 deletion crit/explore.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
// Fd represents the file descriptors opened in a single process
type Fd struct {
PId uint32 `json:"pId"`
Files []*File `json:"files,omitempty"`
Files []*File `json:"files"`
}

// File represents a single opened file
Expand Down Expand Up @@ -145,6 +145,11 @@
Path: filePath,
})

// Omit if the process has no file descriptors
if len(fdEntry.Files) == 0 {
continue

Check warning on line 150 in crit/explore.go

View check run for this annotation

Codecov / codecov/patch

crit/explore.go#L150

Added line #L150 was not covered by tests
}
rst0git marked this conversation as resolved.
Show resolved Hide resolved

fds = append(fds, &fdEntry)
}

Expand Down Expand Up @@ -368,3 +373,114 @@

return rssMaps, nil
}

// Sk represents the sockets associated with a single process
type Sk struct {
PId uint32 `json:"pId"`
Sockets []*Socket `json:"sockets"`
}

// Socket represents a single socket
type Socket struct {
Fd uint32 `json:"fd"`
FdType string `json:"fdType"`
Family string `json:"family,omitempty"`
rst0git marked this conversation as resolved.
Show resolved Hide resolved
Protocol string `json:"protocol,omitempty"`
Type string `json:"type,omitempty"`
State string `json:"state,omitempty"`
SrcAddr string `json:"srcAddr,omitempty"`
SrcPort uint32 `json:"srcPort,omitempty"`
DestAddr string `json:"destAddr,omitempty"`
DestPort uint32 `json:"destPort,omitempty"`
SendBuf string `json:"sendBuf,omitempty"`
RecvBuf string `json:"recvBuf,omitempty"`
}

// ExploreSk searches the process tree for sockets
// and returns a list of PIDs with the associated sockets
func (c *crit) ExploreSk() ([]*Sk, error) {
psTreeImg, err := getImg(filepath.Join(c.inputDirPath, "pstree.img"), &pstree.PstreeEntry{})
if err != nil {
return nil, err
}

Check warning on line 405 in crit/explore.go

View check run for this annotation

Codecov / codecov/patch

crit/explore.go#L404-L405

Added lines #L404 - L405 were not covered by tests

sks := make([]*Sk, 0)
for _, entry := range psTreeImg.Entries {
process := entry.Message.(*pstree.PstreeEntry)
pID := process.GetPid()
// Get file with object IDs
idsImg, err := getImg(filepath.Join(c.inputDirPath, fmt.Sprintf("ids-%d.img", pID)), &criu_core.TaskKobjIdsEntry{})
if err != nil {
return nil, err
}

Check warning on line 415 in crit/explore.go

View check run for this annotation

Codecov / codecov/patch

crit/explore.go#L414-L415

Added lines #L414 - L415 were not covered by tests
filesID := idsImg.Entries[0].Message.(*criu_core.TaskKobjIdsEntry).GetFilesId()
// Get open file descriptors
fdInfoImg, err := getImg(filepath.Join(c.inputDirPath, fmt.Sprintf("fdinfo-%d.img", filesID)), &fdinfo.FdinfoEntry{})
if err != nil {
return nil, err
}

Check warning on line 421 in crit/explore.go

View check run for this annotation

Codecov / codecov/patch

crit/explore.go#L420-L421

Added lines #L420 - L421 were not covered by tests
skEntry := Sk{PId: pID}
for _, fdInfoEntry := range fdInfoImg.Entries {
fdInfo := fdInfoEntry.Message.(*fdinfo.FdinfoEntry)
file, err := getFile(c.inputDirPath, fdInfo.GetId())
if err != nil {
return nil, err
}

Check warning on line 428 in crit/explore.go

View check run for this annotation

Codecov / codecov/patch

crit/explore.go#L427-L428

Added lines #L427 - L428 were not covered by tests
socket := Socket{
Fd: fdInfo.GetFd(),
FdType: fdInfo.GetType().String(),
}
switch fdInfo.GetType() {
case fdinfo.FdTypes_INETSK:
if isk := file.GetIsk(); isk != nil {
socket.State = getSkState(tcpState(isk.GetState()))
socket.Family = getAddressFamily(isk.GetFamily())
socket.Protocol = getSkProtocol(isk.GetProto())
socket.Type = getSkType(isk.GetType())
socket.SrcAddr = processIP(isk.GetSrcAddr())
socket.SrcPort = isk.GetSrcPort()
socket.DestAddr = processIP(isk.GetDstAddr())
socket.DestPort = isk.GetDstPort()
socket.SendBuf = countBytes(int64(isk.GetOpts().GetSoSndbuf()))
socket.RecvBuf = countBytes(int64(isk.GetOpts().GetSoRcvbuf()))
}
case fdinfo.FdTypes_UNIXSK:
if usk := file.GetUsk(); usk != nil {
socket.State = getSkState(tcpState(usk.GetState()))
socket.Type = getSkType(usk.GetType())
socket.SrcAddr = string(usk.GetName())
socket.SendBuf = countBytes(int64(usk.GetOpts().GetSoSndbuf()))
socket.RecvBuf = countBytes(int64(usk.GetOpts().GetSoRcvbuf()))
}
case fdinfo.FdTypes_PACKETSK:
if psk := file.GetPsk(); psk != nil {
socket.Type = getSkType(psk.GetProtocol())
socket.Protocol = getSkProtocol(psk.GetProtocol())
socket.SendBuf = countBytes(int64(psk.GetOpts().GetSoSndbuf()))
socket.RecvBuf = countBytes(int64(psk.GetOpts().GetSoRcvbuf()))
}
case fdinfo.FdTypes_NETLINKSK:
if nlsk := file.GetNlsk(); nlsk != nil {
socket.State = getSkState(tcpState(nlsk.GetState()))
socket.Protocol = getSkProtocol(nlsk.GetProtocol())
socket.Type = getSkType(nlsk.GetProtocol())
socket.SendBuf = countBytes(int64(nlsk.GetOpts().GetSoSndbuf()))
socket.RecvBuf = countBytes(int64(nlsk.GetOpts().GetSoRcvbuf()))
}

Check warning on line 469 in crit/explore.go

View check run for this annotation

Codecov / codecov/patch

crit/explore.go#L434-L469

Added lines #L434 - L469 were not covered by tests
default:
continue
}

skEntry.Sockets = append(skEntry.Sockets, &socket)

Check warning on line 474 in crit/explore.go

View check run for this annotation

Codecov / codecov/patch

crit/explore.go#L474

Added line #L474 was not covered by tests
}

// Omit if the process has no associated sockets
if len(skEntry.Sockets) == 0 {
continue
}

sks = append(sks, &skEntry)

Check warning on line 482 in crit/explore.go

View check run for this annotation

Codecov / codecov/patch

crit/explore.go#L482

Added line #L482 was not covered by tests
}

return sks, nil
}
166 changes: 152 additions & 14 deletions crit/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
"encoding/binary"
"fmt"
"io"
"net"
"os"
"path/filepath"
"strconv"
"syscall"

"github.com/checkpoint-restore/go-criu/v6/crit/images/fdinfo"
"github.com/checkpoint-restore/go-criu/v6/crit/images/pipe"
Expand Down Expand Up @@ -44,13 +46,13 @@

// Helper to convert bytes into a more readable unit
func countBytes(n int64) string {
const UNIT int64 = 1024
if n < UNIT {
const unit int64 = 1024
if n < unit {
return fmt.Sprint(n, " B")
}
div, exp := UNIT, 0
for i := n / UNIT; i >= UNIT; i /= UNIT {
div *= UNIT
div, exp := unit, 0
for i := n / unit; i >= unit; i /= unit {
div *= unit

Check warning on line 55 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L55

Added line #L55 was not covered by tests
exp++
}
return fmt.Sprintf("%.1f %cB", float64(n)/float64(div), "KMGTPE"[exp])
Expand Down Expand Up @@ -110,27 +112,37 @@
filesImg, regImg, pipeImg, unixSkImg *CriuImage
)

// Helper to get file path for exploring file descriptors
func getFilePath(dir string, fID uint32, fType fdinfo.FdTypes) (string, error) {
var filePath string
// Helper to fetch a file if it exists in files.img
func getFile(dir string, fID uint32) (*fdinfo.FileEntry, error) {
var err error
// Get open files
if filesImg == nil {
filesImg, err = getImg(filepath.Join(dir, "files.img"), &fdinfo.FileEntry{})
if err != nil {
return "", err
return nil, err

Check warning on line 121 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L121

Added line #L121 was not covered by tests
}
}

// Check if file entry is present
var file *fdinfo.FileEntry
for _, entry := range filesImg.Entries {
file = entry.Message.(*fdinfo.FileEntry)
if file.GetId() == fID {
break
return file, nil
}
}

return nil, nil

Check warning on line 133 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L133

Added line #L133 was not covered by tests
}

// Helper to get file path for exploring file descriptors
func getFilePath(dir string, fID uint32, fType fdinfo.FdTypes) (string, error) {
var filePath string
var err error
// Fetch the file, if it exists in file.img
file, err := getFile(dir, fID)
if err != nil {
return "", err
}

Check warning on line 144 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L143-L144

Added lines #L143 - L144 were not covered by tests

switch fType {
case fdinfo.FdTypes_REG:
filePath, err = getRegFilePath(dir, file, fID)
Expand All @@ -152,7 +164,7 @@
if file.GetReg() != nil {
return file.GetReg().GetName(), nil
}
return "Unknown path", nil
return "unknown", nil

Check warning on line 167 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L167

Added line #L167 was not covered by tests
}

if regImg == nil {
Expand All @@ -168,7 +180,7 @@
}
}

return "Unknown path", nil
return "unknown", nil

Check warning on line 183 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L183

Added line #L183 was not covered by tests
}

// Helper to get file path of pipe files
Expand Down Expand Up @@ -246,3 +258,129 @@
}
return nil
}

// Helper to convert slice of uint32 into IP address string
func processIP(parts []uint32) string {
rst0git marked this conversation as resolved.
Show resolved Hide resolved
// IPv4
if len(parts) == 1 {
ip := make(net.IP, net.IPv4len)
binary.LittleEndian.PutUint32(ip, parts[0])
return ip.String()
}
// IPv6
if len(parts) == 4 {
ip := make(net.IP, net.IPv6len)
for _, part := range parts {
binary.LittleEndian.PutUint32(ip, part)
}
return ip.String()
}
// Invalid
return ""
}

// tcpState represents the state of a TCP connection.
type tcpState uint8

// https://github.com/torvalds/linux/blob/999f6631/include/net/tcp_states.h#L12
const (
tcpEstablished tcpState = iota + 1
tcpSynSent
tcpSynReceived
tcpFinWait1
tcpFinWait2
tcpTimeWait
tcpClose
tcpCloseWait
tcpLastAck
tcpListen
tcpClosing
tcpNewSynRecv
)

var states = map[tcpState]string{
tcpEstablished: "ESTABLISHED",
tcpSynSent: "SYN_SENT",
tcpSynReceived: "SYN_RECEIVED",
tcpFinWait1: "FIN_WAIT_1",
tcpFinWait2: "FIN_WAIT_2",
tcpTimeWait: "TIME_WAIT",
tcpClose: "CLOSE",
tcpCloseWait: "CLOSE_WAIT",
tcpLastAck: "LAST_ACK",
tcpListen: "LISTEN",
tcpClosing: "CLOSING",
tcpNewSynRecv: "NEW_SYN_RECV",
}

// Helper to identify socket state
func getSkState(state tcpState) string {
if stateName, ok := states[state]; ok {
return stateName
}
return ""

Check warning on line 321 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L317-L321

Added lines #L317 - L321 were not covered by tests
}
rst0git marked this conversation as resolved.
Show resolved Hide resolved

// Helper to identify address family
func getAddressFamily(family uint32) string {
switch family {
case syscall.AF_UNIX:
return "UNIX"
case syscall.AF_NETLINK:
return "NETLINK"
case syscall.AF_BRIDGE:
return "BRIDGE"
case syscall.AF_KEY:
return "KEY"
case syscall.AF_PACKET:
return "PACKET"
case syscall.AF_INET:
return "IPV4"
case syscall.AF_INET6:
return "IPV6"
default:
return ""

Check warning on line 342 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L325-L342

Added lines #L325 - L342 were not covered by tests
}
}

// Helper to identify socket type
func getSkType(skType uint32) string {
switch skType {
case syscall.SOCK_STREAM:
return "STREAM"
case syscall.SOCK_DGRAM:
return "DGRAM"
case syscall.SOCK_SEQPACKET:
return "SEQPACKET"
case syscall.SOCK_RAW:
return "RAW"
case syscall.SOCK_RDM:
return "RDM"
case syscall.SOCK_PACKET:
return "PACKET"
default:
return ""

Check warning on line 362 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L347-L362

Added lines #L347 - L362 were not covered by tests
}
}

// Helper to identify socket protocol
func getSkProtocol(protocol uint32) string {
switch protocol {
case syscall.IPPROTO_ICMP:
return "ICMP"
case syscall.IPPROTO_ICMPV6:
return "ICMPV6"
case syscall.IPPROTO_IGMP:
return "IGMP"
case syscall.IPPROTO_RAW:
return "RAW"
case syscall.IPPROTO_TCP:
return "TCP"
case syscall.IPPROTO_UDP:
return "UDP"
case syscall.IPPROTO_UDPLITE:
return "UDPLITE"
default:
return ""

Check warning on line 384 in crit/utils.go

View check run for this annotation

Codecov / codecov/patch

crit/utils.go#L367-L384

Added lines #L367 - L384 were not covered by tests
}
}
Loading