Skip to content

Commit

Permalink
Added utility code for interfacing with linux NETLINK_INET_DIAG. (#60)
Browse files Browse the repository at this point in the history
The NETLINK_INET_DIAG feature was introduced in Linux 2.6.14. It provides a dump of all sockets.
  • Loading branch information
andrewkroh authored and ruflin committed Dec 22, 2016
1 parent 7790408 commit 171a3c9
Show file tree
Hide file tree
Showing 12 changed files with 700 additions and 4 deletions.
6 changes: 3 additions & 3 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ os: Windows Server 2012 R2

# Environment variables
environment:
GOVERSION: 1.7.3
GOROOT: c:\go1.7.3
GOVERSION: 1.7.4
GOROOT: c:\go1.7.4
GOPATH: c:\gopath

# Custom clone folder (variables are not expanded here).
Expand All @@ -17,7 +17,7 @@ clone_folder: c:\gopath\src\github.com\elastic\gosigar
cache:
- C:\ProgramData\chocolatey\bin -> .appveyor.yml
- C:\ProgramData\chocolatey\lib -> .appveyor.yml
- C:\go1.7.3 -> .appveyor.yml
- C:\go1.7.4 -> .appveyor.yml
- C:\tools\mingw64 -> .appveyor.yml

# Scripts that run after cloning repository
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ examples/free/free
examples/free/free.exe
examples/ps/ps
examples/ps/ps.exe
examples/ss/ss
examples/ss/ss.exe
examples/uptime/uptime
examples/uptime/uptime.exe

Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ os:
- osx

go:
- 1.7.3
- 1.7.4

env:
global:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added `Swap` implementation for Windows based on page file metrics. #55
- Added support to `github.com/gosigar/sys/windows` for querying and enabling
privileges in a process token.
- Added utility code for interfacing with linux NETLINK_INET_DIAG. #60

### Changed
- Changed several `OpenProcess` calls on Windows to request the lowest possible
Expand Down
110 changes: 110 additions & 0 deletions examples/ss/ss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// +build linux

package main

import (
"flag"
"fmt"
"io"
"os"
"strings"
"syscall"
"text/tabwriter"
"time"

log "github.com/Sirupsen/logrus"
"github.com/elastic/gosigar/sys/linux"
)

var (
fs = flag.NewFlagSet("ss", flag.ExitOnError)
debug = fs.Bool("d", false, "enable debug output to stderr")
ipv6 = fs.Bool("6", false, "display only IP version 6 sockets")
v1 = fs.Bool("v1", false, "send inet_diag_msg v1 instead of v2")
diag = fs.String("diag", "", "dump raw information about TCP sockets to FILE")
)

func enableLogger() {
log.SetOutput(os.Stderr)
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.TextFormatter{
FullTimestamp: true,
TimestampFormat: time.RFC3339Nano,
})
}

func main() {
fs.Parse(os.Args[1:])

if *debug {
enableLogger()
}

if err := sockets(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}

func sockets() error {
// Set address family based on flags. The requested address family only
// works with inet_diag_req_v2. v1 returns all tcp sockets.
af := linux.AF_INET
if *ipv6 {
af = linux.AF_INET6
}

// For debug purposes allow for sending either inet_diag_req and inet_diag_req_v2.
var req syscall.NetlinkMessage
if *v1 {
req = linux.NewInetDiagReq()
} else {
req = linux.NewInetDiagReqV2(af)
}

// Write netlink response to a file for further analysis or for writing
// tests cases.
var diagWriter io.Writer
if *diag != "" {
f, err := os.OpenFile(*diag, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer f.Close()
diagWriter = f
}

log.Debugln("sending netlink request")
msgs, err := linux.NetlinkInetDiagWithBuf(req, nil, diagWriter)
if err != nil {
return err
}
log.Debugf("received %d inet_diag_msg responses", len(msgs))

w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, strings.Join([]string{
"State",
"Recv-Q",
"Send-Q",
"Local Address:Port",
"Remote Address:Port",
"UID",
"Inode",
"PID/Program",
}, "\t"))
defer w.Flush()

for _, diag := range msgs {
// XXX: A real implementation of ss would find the process holding
// inode of the socket. It would read /proc/<pid>/fd and find all sockets.
pidProgram := "not implemented"

fmt.Fprintf(w, "%v\t%v\t%v\t%v:%v\t%v:%v\t%v\t%v\t%v\n",
linux.TCPState(diag.State), diag.RQueue, diag.WQueue,
diag.SrcIP().String(), diag.SrcPort(),
diag.DstIP().String(), diag.DstPort(),
diag.UID, diag.Inode, pidProgram)
}

return nil
}
Loading

0 comments on commit 171a3c9

Please sign in to comment.