Skip to content

Commit

Permalink
* different shape for different devices
Browse files Browse the repository at this point in the history
* fix: veth pair name not show for unused veth pair

* graph represented in ortho

* support '-t/--table' table format

* colorize plain text tree output

* show loopback

* rich text output

* table: group netns by color

Signed-off-by: tianyang ni <tianzong48@gmail.com>
  • Loading branch information
t1anz0ng committed Aug 1, 2022
1 parent 612e8f2 commit 9325fe7
Show file tree
Hide file tree
Showing 16 changed files with 587 additions and 158 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/
105 changes: 65 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,82 @@


<div align="center">

# ☘️ iftree

`iftree` command visulize local network interfaces.

intent for better understanding container networks :D

[![golangci-lint](https://github.com/TianZong48/iftree/actions/workflows/golangci-lint.yml/badge.svg?branch=main)](https://github.com/TianZong48/iftree/actions/workflows/golangci-lint.yml)
[![CodeQL](https://github.com/TianZong48/iftree/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/TianZong48/iftree/actions/workflows/codeql-analysis.yml)
[![Go Report](https://goreportcard.com/badge/github.com/TianZong48/iftree)](https://goreportcard.com/badge/github.com/TianZong48/iftree)
</div>

---

# iftree
<img
src="./asset/sample.jpg"
alt="iftree --graph"
width="60%"
align="right"
/>

CLI, easy way to illustrate local network interface.
<img
src="./asset/sample-term.png"
alt="iftree"
width="60%"
align="right"
/>

The intent is for understanding container networks :D
**Features**

- [x] visualize Veth/bridge connections
- [x] support graphviz
- [x] table output
- [x] rich text
- [ ] ascii graph
- [ ] support more networking device

![networ-devices](./sample.jpg)
## usage

## example
```
# sudo go run main.go
----------------------------------------------------
BRIDGE: cni_br up
netnsName veth peer peerInNetns netnsID
|____321
|----veth6328d76d enp5s0 eth1 3
|____123
|----veth5e41415a enp5s0 eth1 2
|----veth90c9f5fa wlp4s0 eth2 2
|----veth385ac3bb docker0 eth3 2
----------------------------------------------------
unused veth pairs
veth peer netnsID
veth31bc095b enp5s0 1
veth12d98148 wlp4s0 1
veth-tt1 veth-tt -1
veth-tt veth-tt1 -1
Usage:
iftree [options]
-d, --debug print debug message
-g, --graph output in graphviz dot language(https://graphviz.org/doc/info/lang.html
-t, --table output in table
--no-color disable color output
Help Options:
-h, --help Show this help message
```

## graph output
### text

```shell
sudo go run cmd/iftree/main.go
```
# sudo go run cmd/iftree/main.go --graph | dot -Tpng > output.png
or
# sudo go run cmd/iftree/main.go --graph > xx.dot
# dot -Tpng xx.dot > output.png

### graph

Create an ouput image with [graphviz](https://www.graphviz.org/) compatible renderer.
e.g: online editor: https://dreampuf.github.io/GraphvizOnline

```shell
sudo go run cmd/iftree/main.go --graph
```
example:
![networ-devices](./sample.jpg)

### prequisite
Create an ouput image with any [graphviz](http://www.graphviz.org/download) compatible renderer
generate image using `dot`(http://www.graphviz.org/download/#executable-packages)

---
```shell
sudo go run cmd/iftree/main.go --graph | dot -Tpng > output.png
```

### roadmap
### table

```shell
sudo iftree --table
```

![table](./asset/sample-table.png)

- [x] show peer name in container
- [x] graphviz
- [ ] rich text
- [ ] topo relation in ascii graph
- [ ] support more networking device
Binary file added asset/sample-table.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added asset/sample-term.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added asset/sample.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 85 additions & 23 deletions cmd/iftree/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,58 @@ import (
"os"
"runtime"
"syscall"
"text/tabwriter"

"github.com/containerd/nerdctl/pkg/rootlessutil"
"github.com/fatih/color"
log "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"

"github.com/TianZong48/iftree/pkg"
"github.com/TianZong48/iftree/pkg/formatter"
"github.com/TianZong48/iftree/pkg/graph"
"github.com/TianZong48/iftree/pkg/netutil"
)

var (
debug = pflag.BoolP("debug", "d", false, "print debug message")
isGraph = pflag.BoolP("graph", "g", false, "output in graphviz dot language(https://graphviz.org/doc/info/lang.html")
debug = pflag.BoolP("debug", "d", false, "print debug message")
oGraph = pflag.BoolP("graph", "g", false, "output in graphviz dot language(https://graphviz.org/doc/info/lang.html")
oTable = pflag.BoolP("table", "t", false, "output in table")
flagNoColor = pflag.Bool("no-color", false, "Disable color output")
help = pflag.BoolP("help", "h", false, "")
)

func init() {
pflag.Usage = func() {
fmt.Println(`Usage:
iftree [options]
-d, --debug print debug message
-g, --graph output in graphviz dot language(https://graphviz.org/doc/info/lang.html
-t, --table output in table
--no-color disable color output
Help Options:
-h, --help Show this help message`)
}
if *flagNoColor {
color.NoColor = true // disables colorized output
}
}

func helper() error {
if *oGraph && *oTable {
return fmt.Errorf(`only one of "graph", or "table" can be set`)
}
if *help {
pflag.Usage()
os.Exit(0)
}
return nil
}
func main() {
pflag.Parse()
if err := helper(); err != nil {
log.Fatal(err)
}
if rootlessutil.IsRootless() {
log.Error("iftree must be run as root to enter ns")
os.Exit(1)
Expand All @@ -38,6 +69,7 @@ func main() {
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()

netNsMap, err := netutil.NetNsMap()
if err != nil {
log.Fatal(err)
Expand All @@ -47,10 +79,11 @@ func main() {
if err != nil {
log.Fatal(err)
}
// master link
vm := make(map[string][]pkg.Pair)
vpairs := []pkg.Pair{}
bm := make(map[string]*net.IP)

vm := make(map[string][]pkg.Node) // map bridge <-> veth paris
vpairs := []pkg.Node{} // unused veth paris
bm := make(map[string]*net.IP) // bridge ip
los := []pkg.Node{} // loopback

for _, link := range ll {
veth, ok := link.(*netlink.Veth)
Expand All @@ -64,12 +97,21 @@ func main() {
log.Fatal(err)
}
if link.Attrs().MasterIndex == -1 || veth.MasterIndex == 0 {
p := pkg.Pair{
Veth: veth.Name,
Peer: veth.PeerName,
PeerId: peerIdx,
NetNsID: veth.NetNsID}
vpairs = append(vpairs, p)
if veth.PeerName == "" {
p, err := netlink.LinkByIndex(peerIdx)
if err != nil {
log.Fatal(err)
}
veth.PeerName = p.Attrs().Name
}

vpairs = append(vpairs,
pkg.Node{
Type: pkg.VethType,
Veth: veth.Name,
Peer: veth.PeerName,
PeerId: peerIdx,
NetNsID: veth.NetNsID})
continue
}

Expand All @@ -85,20 +127,31 @@ func main() {
bridge := master.Attrs().Name
v, ok := vm[bridge]
if !ok {
vm[bridge] = []pkg.Pair{}
vm[bridge] = []pkg.Node{}
}
pair := pkg.Pair{
pair := pkg.Node{
Type: pkg.VethType,
Veth: veth.Name,
PeerId: peerIdx,
NetNsID: veth.NetNsID,
}
if peerNetNs, ok := netNsMap[veth.NetNsID]; ok {
peerInNs, err := netutil.GetPeerInNs(peerNetNs, peerIdx, origin)
peerInNs, err := netutil.GetPeerInNs(peerNetNs, origin, peerIdx)
if err != nil {
log.Fatal(err)
}
pair.NetNsName = peerNetNs
pair.PeerInNetns = peerInNs
pair.PeerNameInNetns = peerInNs.Attrs().Name
pair.Status = peerInNs.Attrs().OperState.String()

lo, err := netutil.GetLoInNs(peerNetNs, origin)
if err == nil && lo != nil {
los = append(los, pkg.Node{
Type: pkg.LoType,
NetNsName: peerNetNs,
Status: lo.Attrs().OperState.String(),
})
}
} else {
pair.Orphaned = true
}
Expand All @@ -118,18 +171,27 @@ func main() {
}

}
if *isGraph {
output, err := graph.GenerateGraph(vm, vpairs, bm)
if *oGraph {
output, err := formatter.Graph(vm, vpairs, los, bm)
if err != nil {
log.Fatal(err)
}
fmt.Fprintln(os.Stdout, output)
return
}
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
if err := formatter.Print(w, vm, netNsMap, vpairs); err != nil {
if *oTable {
err := formatter.Table(os.Stdout, vm)
if err != nil {
log.Fatal(err)
}
if err := formatter.TableParis(os.Stdout, vpairs); err != nil {
log.Fatal(err)
}
return
}

if err := formatter.Print(os.Stdout, vm, netNsMap, vpairs); err != nil {
log.Fatal(err)
}
w.Flush()

}
12 changes: 11 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ go 1.18

require (
github.com/awalterschulze/gographviz v2.0.3+incompatible
github.com/charmbracelet/lipgloss v0.5.0
github.com/containerd/nerdctl v0.22.0
github.com/fatih/color v1.13.0
github.com/jedib0t/go-pretty/v6 v6.3.5
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.0
github.com/spf13/pflag v1.0.5
github.com/vishvananda/netlink v1.2.1-beta.2
Expand All @@ -21,7 +25,13 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 // indirect
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rootless-containers/rootlesskit v1.0.1 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/sys v0.0.0-20220727055044-e65921a090b8 // indirect
Expand Down

0 comments on commit 9325fe7

Please sign in to comment.