@@ -18,12 +18,14 @@ func Diff() *cobra.Command {
1818 Long : `Compare two CRIU checkpoints and show differences in:
1919 - Process tree (new/removed/modified processes)
2020 - File descriptors (opened/closed files)
21+ - Sockets (new/removed network sockets)
2122 - Memory usage (size changes)
2223
2324Example:
2425 checkpointctl diff checkpoint1.tar checkpoint2.tar
2526 checkpointctl diff --format json checkpoint1.tar checkpoint2.tar
26- checkpointctl diff --files --ps-tree-cmd checkpoint1.tar checkpoint2.tar` ,
27+ checkpointctl diff --files --ps-tree-cmd checkpoint1.tar checkpoint2.tar
28+ checkpointctl diff --files --sockets checkpoint1.tar checkpoint2.tar` ,
2729 Args : cobra .ExactArgs (2 ),
2830 RunE : diff ,
2931 }
@@ -216,6 +218,7 @@ type CheckpointMetadata struct {
216218 CheckpointSize CheckpointSize `json:"checkpoint_size"`
217219 ProcessTree * ProcessTree `json:"process_tree,omitempty"`
218220 FileDescriptors []FileDescriptorEntry `json:"file_descriptors,omitempty"`
221+ Sockets []SkNode `json:"sockets,omitempty"`
219222}
220223
221224type CheckpointSize struct {
@@ -241,6 +244,29 @@ type OpenFile struct {
241244 Path string `json:"path"`
242245}
243246
247+ // Socket types (mirroring internal/json.go)
248+ type SkNode struct {
249+ PID uint32 `json:"pid"`
250+ OpenSockets []SocketNode `json:"open_sockets,omitempty"`
251+ }
252+
253+ type SocketNode struct {
254+ Protocol string `json:"protocol,omitempty"`
255+ Data SkData `json:"data,omitempty"`
256+ }
257+
258+ type SkData struct {
259+ Type string `json:"type,omitempty"`
260+ Address string `json:"address,omitempty"`
261+ State string `json:"state,omitempty"`
262+ Source string `json:"src,omitempty"`
263+ SourcePort uint32 `json:"src_port,omitempty"`
264+ Dest string `json:"dst,omitempty"`
265+ DestPort uint32 `json:"dst_port,omitempty"`
266+ SendBuf string `json:"send_buf,omitempty"`
267+ RecvBuf string `json:"recv_buf,omitempty"`
268+ }
269+
244270// Diff result structures
245271type DiffResult struct {
246272 ContainerID string `json:"container_id"`
@@ -251,6 +277,7 @@ type DiffResult struct {
251277 ProcessChanges * ProcessDiff `json:"process_changes,omitempty"`
252278 FileChanges * FileDiff `json:"file_changes,omitempty"`
253279 MemoryChanges * MemoryDiff `json:"memory_changes"`
280+ SocketChanges * SocketDiff `json:"socket_changes,omitempty"`
254281 Summary string `json:"summary"`
255282}
256283
@@ -290,6 +317,24 @@ type MemoryDiff struct {
290317 SizeChangeMB float64 `json:"size_change_mb"`
291318}
292319
320+ type SocketDiff struct {
321+ Added []SocketInfo `json:"added,omitempty"`
322+ Removed []SocketInfo `json:"removed,omitempty"`
323+ Unchanged int `json:"unchanged"`
324+ }
325+
326+ type SocketInfo struct {
327+ PID int `json:"pid"`
328+ Protocol string `json:"protocol"`
329+ Type string `json:"type"`
330+ Address string `json:"address,omitempty"`
331+ State string `json:"state,omitempty"`
332+ Source string `json:"source,omitempty"`
333+ SrcPort uint32 `json:"src_port,omitempty"`
334+ Dest string `json:"dest,omitempty"`
335+ DstPort uint32 `json:"dst_port,omitempty"`
336+ }
337+
293338func computeDiff (metadataA , metadataB CheckpointMetadata ) * DiffResult {
294339 result := & DiffResult {
295340 ContainerID : metadataA .ID ,
@@ -313,6 +358,11 @@ func computeDiff(metadataA, metadataB CheckpointMetadata) *DiffResult {
313358 result .FileChanges = compareFileDescriptors (metadataA .FileDescriptors , metadataB .FileDescriptors )
314359 }
315360
361+ // Compare sockets if requested
362+ if * sockets {
363+ result .SocketChanges = compareSockets (metadataA .Sockets , metadataB .Sockets )
364+ }
365+
316366 // Compare memory
317367 sizeChange := metadataB .CheckpointSize .MemoryPagesSize - metadataA .CheckpointSize .MemoryPagesSize
318368 result .MemoryChanges = & MemoryDiff {
@@ -444,6 +494,82 @@ func compareFileDescriptors(fdsA, fdsB []FileDescriptorEntry) *FileDiff {
444494 return diff
445495}
446496
497+ func compareSockets (sksA , sksB []SkNode ) * SocketDiff {
498+ diff := & SocketDiff {}
499+
500+ // Build socket maps
501+ // Key format: PID:Protocol:Type:Address:SrcPort:Dest:DstPort
502+ mapA := make (map [string ]SocketInfo )
503+ mapB := make (map [string ]SocketInfo )
504+
505+ for _ , entry := range sksA {
506+ for _ , socket := range entry .OpenSockets {
507+ key := fmt .Sprintf ("%d:%s:%s:%s:%d:%s:%d" ,
508+ entry .PID ,
509+ socket .Protocol ,
510+ socket .Data .Type ,
511+ socket .Data .Address ,
512+ socket .Data .SourcePort ,
513+ socket .Data .Dest ,
514+ socket .Data .DestPort ,
515+ )
516+ mapA [key ] = SocketInfo {
517+ PID : int (entry .PID ),
518+ Protocol : socket .Protocol ,
519+ Type : socket .Data .Type ,
520+ Address : socket .Data .Address ,
521+ State : socket .Data .State ,
522+ Source : socket .Data .Source ,
523+ SrcPort : socket .Data .SourcePort ,
524+ Dest : socket .Data .Dest ,
525+ DstPort : socket .Data .DestPort ,
526+ }
527+ }
528+ }
529+
530+ for _ , entry := range sksB {
531+ for _ , socket := range entry .OpenSockets {
532+ key := fmt .Sprintf ("%d:%s:%s:%s:%d:%s:%d" ,
533+ entry .PID ,
534+ socket .Protocol ,
535+ socket .Data .Type ,
536+ socket .Data .Address ,
537+ socket .Data .SourcePort ,
538+ socket .Data .Dest ,
539+ socket .Data .DestPort ,
540+ )
541+ mapB [key ] = SocketInfo {
542+ PID : int (entry .PID ),
543+ Protocol : socket .Protocol ,
544+ Type : socket .Data .Type ,
545+ Address : socket .Data .Address ,
546+ State : socket .Data .State ,
547+ Source : socket .Data .Source ,
548+ SrcPort : socket .Data .SourcePort ,
549+ Dest : socket .Data .Dest ,
550+ DstPort : socket .Data .DestPort ,
551+ }
552+ }
553+ }
554+
555+ // Find differences
556+ for key , socketB := range mapB {
557+ if _ , exists := mapA [key ]; ! exists {
558+ diff .Added = append (diff .Added , socketB )
559+ } else {
560+ diff .Unchanged ++
561+ }
562+ }
563+
564+ for key , socketA := range mapA {
565+ if _ , exists := mapB [key ]; ! exists {
566+ diff .Removed = append (diff .Removed , socketA )
567+ }
568+ }
569+
570+ return diff
571+ }
572+
447573func generateSummary (result * DiffResult ) string {
448574 summary := fmt .Sprintf ("Checkpoint comparison for container %s" , result .ContainerName )
449575
@@ -466,6 +592,15 @@ func generateSummary(result *DiffResult) string {
466592 }
467593 }
468594
595+ if result .SocketChanges != nil {
596+ added := len (result .SocketChanges .Added )
597+ removed := len (result .SocketChanges .Removed )
598+
599+ if added > 0 || removed > 0 {
600+ summary += fmt .Sprintf ("\n Sockets: +%d -%d" , added , removed )
601+ }
602+ }
603+
469604 if result .MemoryChanges != nil && result .MemoryChanges .SizeChangeBytes != 0 {
470605 summary += fmt .Sprintf ("\n Memory: %+.2f MB" , result .MemoryChanges .SizeChangeMB )
471606 }
@@ -564,6 +699,50 @@ func renderTreeDiff(result *DiffResult) {
564699 fmt .Println ("└──────────────────────────────────────────────────────────────┘" )
565700 }
566701
702+ // Socket changes
703+ if result .SocketChanges != nil {
704+ fmt .Println ("┌─ Socket Changes ─────────────────────────────────────────────┐" )
705+
706+ if len (result .SocketChanges .Added ) > 0 {
707+ fmt .Println ("│ Added:" )
708+ for _ , socket := range result .SocketChanges .Added {
709+ fmt .Printf ("│ + PID %-5d %-8s" , socket .PID , socket .Protocol )
710+ switch socket .Type {
711+ case "UNIX" :
712+ fmt .Printf ("%s\n " , truncate (socket .Address , 40 ))
713+ case "TCP" , "UDP" :
714+ fmt .Printf ("%s:%d -> %s:%d\n " ,
715+ socket .Source , socket .SrcPort ,
716+ socket .Dest , socket .DstPort )
717+ default :
718+ fmt .Printf ("%s\n " , truncate (socket .Address , 40 ))
719+ }
720+ }
721+ }
722+
723+ if len (result .SocketChanges .Removed ) > 0 {
724+ fmt .Println ("│ Removed:" )
725+ for _ , socket := range result .SocketChanges .Removed {
726+ fmt .Printf ("│ - PID %-5d %-8s" , socket .PID , socket .Protocol )
727+ switch socket .Type {
728+ case "UNIX" :
729+ fmt .Printf ("%s\n " , truncate (socket .Address , 40 ))
730+ case "TCP" , "UDP" :
731+ fmt .Printf ("%s:%d -> %s:%d\n " ,
732+ socket .Source , socket .SrcPort ,
733+ socket .Dest , socket .DstPort )
734+ default :
735+ fmt .Printf ("%s\n " , truncate (socket .Address , 40 ))
736+ }
737+ }
738+ }
739+
740+ if result .SocketChanges .Unchanged > 0 {
741+ fmt .Printf ("│ Unchanged: %d\n " , result .SocketChanges .Unchanged )
742+ }
743+ fmt .Println ("└──────────────────────────────────────────────────────────────┘" )
744+ }
745+
567746 // Summary
568747 fmt .Println ("Summary:" )
569748 fmt .Println (result .Summary )
0 commit comments