-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,257 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,77 @@ | ||
# wiredog | ||
a terminal-based monitor of local HTTP traffic | ||
|
||
Wiredog monitors the local HTTP traffic by scanning the | ||
network packets. All network interfaces are monitored. | ||
At least one active interface is needed. | ||
|
||
When the requests rate for a given period exceeds a | ||
threshold, an alert message is displayed. The top 10 | ||
website sections ordered by number of requests are displayed. | ||
|
||
Working and tested on Linux. OSX and Windows support is planned. | ||
|
||
<p align="center"> | ||
<img src="wiredog-demo.gif" width="800"> | ||
</p> | ||
|
||
``` | ||
$ wiredog -help | ||
Wiredog is a tool for monitoring network traffic. | ||
Only local HTTP requests are studied. | ||
Administrator permission is required to access network devices. | ||
Consider using sudo on Linux or get admin rights on Windows. | ||
Usage: sudo \path\to\wiredog [flags] | ||
-assembly_debug_log | ||
If true, the github.com/google/gopacket/tcpassembly library will log verbose debugging information (at least one line per packet) | ||
-assembly_memuse_log | ||
If true, the github.com/google/gopacket/tcpassembly library will log information regarding its memory use every once in a while. | ||
-d string | ||
network device name (default "wlp2s0") | ||
-h int | ||
threshold of HTTP request hits (default 2) | ||
-log string | ||
log directory (default ".") | ||
-p duration | ||
time period between traffic checks (default 2m0s) | ||
-t generates test HTTP requests; sets h=2 and p=2s | ||
``` | ||
|
||
## Built With (from go.mod file) | ||
``` | ||
Go 1.12 | ||
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c | ||
github.com/gizak/termui v2.3.0+incompatible // indirect | ||
github.com/gizak/termui/v3 v3.0.0 | ||
github.com/google/gopacket v1.1.16 | ||
github.com/maruel/panicparse v1.1.1 // indirect | ||
github.com/mattn/go-runewidth v0.0.4 // indirect | ||
github.com/mdlayher/raw v0.0.0-20190419142535-64193704e472 // indirect | ||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect | ||
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e // indirect | ||
github.com/onsi/ginkgo v1.8.0 // indirect | ||
github.com/onsi/gomega v1.5.0 // indirect | ||
github.com/pkg/errors v0.8.1 | ||
github.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc // indirect | ||
``` | ||
|
||
## Thanks | ||
|
||
gopacket and termui are cool Go packages. And of course, the Go programming language is beautiful. | ||
|
||
## Useful links | ||
|
||
https://www.devdungeon.com/content/packet-capture-injection-and-analysis-gopacket | ||
|
||
https://medium.com/@cjoudrey/capturing-http-packets-the-hard-way-b9c799bfb6 | ||
|
||
https://stackoverflow.com/questions/21145781/how-do-you-use-the-tcp-assembly-package-in-gopacket | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
// Wiredog is a command for network monitoring. | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"time" | ||
|
||
"code.cloudfoundry.org/clock" | ||
ui "github.com/gizak/termui/v3" | ||
"github.com/raypereda/wiredog/pkg/alerts" | ||
asm "github.com/raypereda/wiredog/pkg/httpassembler" | ||
"github.com/raypereda/wiredog/pkg/screens" | ||
"github.com/raypereda/wiredog/pkg/stats" | ||
) | ||
|
||
var ( | ||
device string | ||
hitThreshold int | ||
logDirectory string | ||
alertInterval time.Duration | ||
testRequests bool | ||
) | ||
|
||
func settings() string { | ||
var sb strings.Builder | ||
fmt.Fprintf(&sb, "Alert Interval : %s\n", alertInterval) | ||
fmt.Fprintf(&sb, "Hit Threshold : %d\n", hitThreshold) | ||
fmt.Fprintf(&sb, "Network Interface : %s\n", device) | ||
absDirectory, err := filepath.Abs(logDirectory) | ||
if err != nil { | ||
log.Fatalf("error getting absolute path for %s: %v", logDirectory, err) | ||
} | ||
logDirectory = absDirectory | ||
fmt.Fprintf(&sb, "Log Directory : %8s\n", logDirectory) | ||
return sb.String() | ||
} | ||
|
||
// Usage prints usage, overwrites default | ||
var Usage = func() { | ||
fmt.Fprintln(flag.CommandLine.Output(), "Wiredog is a tool for monitoring network traffic.") | ||
fmt.Fprintln(flag.CommandLine.Output()) | ||
fmt.Fprintln(flag.CommandLine.Output(), "Only local HTTP requests are studied.") | ||
fmt.Fprintln(flag.CommandLine.Output(), "Administrator permission is required to access network devices.") | ||
fmt.Fprintln(flag.CommandLine.Output(), "Consider using sudo on Linux or get admin rights on Windows.") | ||
fmt.Fprintln(flag.CommandLine.Output()) | ||
fmt.Fprintln(flag.CommandLine.Output(), "Usage: sudo \\path\\to\\wiredog [flags]") | ||
fmt.Fprintln(flag.CommandLine.Output()) | ||
flag.PrintDefaults() | ||
} | ||
|
||
func main() { | ||
// other network devices on my laptop: lo, enp0s31f6, but they don't work in promiscuous mode | ||
flag.StringVar(&device, "d", "wlp2s0", "network device name") | ||
flag.IntVar(&hitThreshold, "h", 2, "threshold of HTTP request hits") | ||
flag.StringVar(&logDirectory, "log", ".", "log directory") | ||
flag.DurationVar(&alertInterval, "p", 2*time.Minute, "time period between traffic checks") | ||
flag.BoolVar(&testRequests, "t", false, "generates test HTTP requests; sets h=2 and p=2s") | ||
flag.Usage = Usage | ||
flag.Parse() | ||
|
||
logFile := logDirectory + "/wiredog.log" | ||
f, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) | ||
if err != nil { | ||
log.Fatalf("error opening file: %v", err) | ||
} | ||
defer f.Close() | ||
log.SetOutput(f) | ||
|
||
if err := ui.Init(); err != nil { | ||
log.Fatalf("failed to initialize termui: %v", err) | ||
} | ||
defer ui.Close() | ||
|
||
if testRequests { | ||
hitThreshold = 5 | ||
alertInterval = 2 * time.Second | ||
go makeTestRequests() | ||
} | ||
|
||
requests := make(chan *http.Request) // receiver of requests | ||
asm, err := asm.NewHTTPAssembler(device, requests) | ||
if err != nil { | ||
log.Fatalln("error connecting to network devices", err) | ||
} | ||
go asm.Run() | ||
|
||
s := screens.New(settings()) | ||
|
||
clock := clock.NewClock() | ||
hist := alerts.NewHistory(clock, alertInterval) | ||
// BUGFIX: avoid sharing screen state with alert, need to rethink | ||
alert := alerts.New(clock, hist, alertInterval, hitThreshold, s) | ||
go alert.Run() | ||
|
||
metrics := stats.New() | ||
go func() { | ||
for { | ||
r := <-requests | ||
metrics.Tally(r) // collects statistics | ||
hist.Record(r) // keep history for alerter | ||
} | ||
}() | ||
|
||
go func() { | ||
ticker := time.NewTicker(time.Second) | ||
for { | ||
<-ticker.C | ||
s.UpdateTopN(metrics.GetTopSections()) | ||
s.Render() | ||
} | ||
}() | ||
|
||
go func() { | ||
ticker := time.NewTicker(alertInterval) | ||
for { | ||
<-ticker.C | ||
count := float64(hist.RecentRequestCount()) | ||
s.AddRequestCount(count) | ||
s.Render() | ||
} | ||
}() | ||
|
||
s.Start() | ||
log.Println("Stopped monitoring.") | ||
} | ||
|
||
func get(url string) { | ||
_, _ = http.Get(url) | ||
} | ||
|
||
func makeTestRequests() { | ||
time.Sleep(1 * time.Second) // allow alerts to setup | ||
get("http://example1.com") | ||
get("http://example2.com/sectionA/") | ||
get("http://example2.com/sectionA/") | ||
get("http://example3.com") | ||
get("http://example3.com") | ||
get("http://example3.com") | ||
|
||
for { | ||
time.Sleep(time.Second) | ||
get("http://example4.com") | ||
get("http://example4.com") | ||
get("http://example4.com") | ||
get("http://example4.com") | ||
time.Sleep(time.Second) | ||
get("http://example5.com") | ||
get("http://example5.com") | ||
get("http://example5.com") | ||
get("http://example5.com") | ||
get("http://example5.com") | ||
} | ||
} |
Binary file not shown.
Oops, something went wrong.