github.com/elastic/gosigar@v0.14.3/examples/ss/ss.go (about) 1 // +build linux 2 3 package main 4 5 import ( 6 "flag" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "log" 11 "os" 12 "strings" 13 "syscall" 14 "text/tabwriter" 15 16 "github.com/elastic/gosigar/sys/linux" 17 ) 18 19 var ( 20 fs = flag.NewFlagSet("ss", flag.ExitOnError) 21 debug = fs.Bool("d", false, "enable debug output to stderr") 22 ipv6 = fs.Bool("6", false, "display only IP version 6 sockets") 23 v1 = fs.Bool("v1", false, "send inet_diag_msg v1 instead of v2") 24 diag = fs.String("diag", "", "dump raw information about TCP sockets to FILE") 25 ) 26 27 func main() { 28 log.SetFlags(0) 29 fs.Parse(os.Args[1:]) 30 31 if !*debug { 32 log.SetOutput(ioutil.Discard) 33 } 34 35 if err := sockets(); err != nil { 36 fmt.Fprintf(os.Stderr, "error: %v\n", err) 37 os.Exit(1) 38 } 39 } 40 41 func sockets() error { 42 // Set address family based on flags. The requested address family only 43 // works with inet_diag_req_v2. v1 returns all tcp sockets. 44 af := linux.AF_INET 45 if *ipv6 { 46 af = linux.AF_INET6 47 } 48 49 // For debug purposes allow for sending either inet_diag_req and inet_diag_req_v2. 50 var req syscall.NetlinkMessage 51 if *v1 { 52 req = linux.NewInetDiagReq() 53 } else { 54 req = linux.NewInetDiagReqV2(af) 55 } 56 57 // Write netlink response to a file for further analysis or for writing 58 // tests cases. 59 var diagWriter io.Writer 60 if *diag != "" { 61 f, err := os.OpenFile(*diag, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600) 62 if err != nil { 63 return err 64 } 65 defer f.Close() 66 diagWriter = f 67 } 68 69 log.Println("sending netlink request") 70 msgs, err := linux.NetlinkInetDiagWithBuf(req, nil, diagWriter) 71 if err != nil { 72 return err 73 } 74 log.Printf("received %d inet_diag_msg responses", len(msgs)) 75 76 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 77 fmt.Fprintln(w, strings.Join([]string{ 78 "State", 79 "Recv-Q", 80 "Send-Q", 81 "Local Address:Port", 82 "Remote Address:Port", 83 "UID", 84 "Inode", 85 "PID/Program", 86 }, "\t")) 87 defer w.Flush() 88 89 for _, diag := range msgs { 90 // XXX: A real implementation of ss would find the process holding 91 // inode of the socket. It would read /proc/<pid>/fd and find all sockets. 92 pidProgram := "not implemented" 93 94 fmt.Fprintf(w, "%v\t%v\t%v\t%v:%v\t%v:%v\t%v\t%v\t%v\n", 95 linux.TCPState(diag.State), diag.RQueue, diag.WQueue, 96 diag.SrcIP().String(), diag.SrcPort(), 97 diag.DstIP().String(), diag.DstPort(), 98 diag.UID, diag.Inode, pidProgram) 99 } 100 101 return nil 102 }