Skip to content

Commit

Permalink
text: optimize output for not bridged veths
Browse files Browse the repository at this point in the history
  • Loading branch information
t1anz0ng committed Aug 22, 2022
1 parent 0ec98d1 commit abbc193
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 113 deletions.
176 changes: 92 additions & 84 deletions cmd/iftree/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import (
var (
debug = pflag.BoolP("debug", "d", false, "print debug message")

oUnusedVeths = pflag.BoolP("all", "a", false, "show all veths, including unused.")
oGraph = pflag.BoolP("graph", "g", false, "output in png by defaul")
oGraphType = pflag.StringP("gtype", "T", "png", `graph output type, "jpg", "png", "svg", "dot"(graphviz dot language(https://graphviz.org/doc/info/lang.html)`)
oGraphName = pflag.StringP("output", "O", "output", "graph output name/path")
oNotBridgedVeths = pflag.BoolP("all", "a", true, "show all veths, including not bridged.")
oGraph = pflag.BoolP("graph", "g", false, "output in png by defaul")
oGraphType = pflag.StringP("gtype", "T", "png", `graph output type, "jpg", "png", "svg", "dot"(graphviz dot language(https://graphviz.org/doc/info/lang.html)`)
oGraphName = pflag.StringP("output", "O", "output", "graph output name/path")

oTable = pflag.BoolP("table", "t", false, "output in table")

Expand Down Expand Up @@ -100,106 +100,114 @@ func main() {
os.Exit(0)
}
log.Debugf("net namespace id <-> name map:\n%+v\n", netNsMap)

ll, err := netlink.LinkList()
if err != nil {
log.Fatal(err)
}
log.Debugf("net link list:\n%+v\n", ll)

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
bridgeVethM := make(map[string][]pkg.Node) // map bridge <-> veth paris
unBridgedVpairs := []pkg.Node{}
bridgeIps := make(map[string]*net.IP) // bridge ip
loS := []pkg.Node{} // loopback

origin, err := netns.Get()
if err != nil {
log.Fatalf("failed get current netne, %v", err)
}
// FIXME: use undirected graph insted of array/map to store relations
for _, link := range ll {
veth, ok := link.(*netlink.Veth)
if ok { // skip device not enslaved to any bridge

origin, _ := netns.Get()
defer origin.Close()

peerIdx, err := netlink.VethPeerIndex(veth)
if err != nil {
log.Fatal(err)
}
if link.Attrs().MasterIndex == -1 || veth.MasterIndex == 0 {
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
}

master, err := netlink.LinkByIndex(veth.Attrs().MasterIndex)
if err != nil {
log.Fatal(err)
}
if !ok {
// skip device not enslaved to any bridge
log.Debugf("skip %s, type: %s", link.Attrs().Name, link.Type())
continue
}
log.Debugf("found veth device: %s", veth.Name)

// if master is not bridge
if _, ok := master.(*netlink.Bridge); !ok {
log.Debug("todo: not bridge")
}
bridge := master.Attrs().Name
v, ok := vm[bridge]
if !ok {
vm[bridge] = []pkg.Node{}
}
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, origin, peerIdx)
peerIdx, err := netlink.VethPeerIndex(veth)
if err != nil {
log.Fatal(err)
}
if link.Attrs().MasterIndex == -1 || veth.MasterIndex == 0 {
log.Debugf("%s not has a bridge as master, MasterIndex: %d", veth.Name, link.Attrs().MasterIndex)
if veth.PeerName == "" {
p, err := netlink.LinkByIndex(peerIdx)
if err != nil {
log.Fatal(err)
}
pair.NetNsName = peerNetNs
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
veth.PeerName = p.Attrs().Name
}
unBridgedVpairs = append(unBridgedVpairs,
pkg.Node{
Type: pkg.VethType,
Veth: veth.Name,
Peer: veth.PeerName,
PeerId: peerIdx,
NetNsID: veth.NetNsID})
continue
}

addrs, err := netlink.AddrList(master, syscall.AF_INET)
master, err := netlink.LinkByIndex(veth.Attrs().MasterIndex)
if err != nil {
log.Fatal(err)
}

// if master is not bridge
if _, ok := master.(*netlink.Bridge); !ok {
log.Debug("todo: not bridge")
}
bridge := master.Attrs().Name
v, ok := bridgeVethM[bridge]
if !ok {
bridgeVethM[bridge] = []pkg.Node{}
}
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, origin, peerIdx)
if err != nil {
log.Fatal(err)
}
if len(addrs) > 0 {
pair.Master = &pkg.Bridge{
Name: bridge,
IP: &addrs[0].IP,
}
bm[bridge] = &addrs[0].IP
pair.NetNsName = peerNetNs
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(),
})
}
vm[bridge] = append(v, pair)
} else {
pair.Orphaned = true
}

addrs, err := netlink.AddrList(master, syscall.AF_INET)
if err != nil {
log.Fatal(err)
}
if len(addrs) > 0 {
pair.Master = &pkg.Bridge{
Name: bridge,
IP: &addrs[0].IP,
}
bridgeIps[bridge] = &addrs[0].IP
}
bridgeVethM[bridge] = append(v, pair)
}
log.Debugf("bridgeVethMap: %+v", bridgeVethM)

if *oGraph {
buf := bytes.Buffer{}
output, err := formatter.Graph(vm, vpairs, los, bm)
output, err := formatter.Graph(bridgeVethM, unBridgedVpairs, loS, bridgeIps)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -241,15 +249,15 @@ func main() {
return
}
if *oTable {
err := formatter.Table(os.Stdout, vm)
err := formatter.Table(os.Stdout, bridgeVethM)
if err != nil {
log.Fatal(err)
}
if *oUnusedVeths {
formatter.TableParis(os.Stdout, vpairs)
if *oNotBridgedVeths {
formatter.TableParis(os.Stdout, unBridgedVpairs)
}
return
}

formatter.Print(os.Stdout, vm, netNsMap, vpairs, *oUnusedVeths)
formatter.Print(os.Stdout, bridgeVethM, netNsMap, unBridgedVpairs, *oNotBridgedVeths)
}
14 changes: 3 additions & 11 deletions pkg/formatter/color.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ var (
Padding(0, 1).
Italic(true).
Foreground(lipgloss.Color("#FFFFFF"))
TitleHighlight = lipgloss.NewStyle().
titleHighlight = lipgloss.NewStyle().
Background(lipgloss.Color("#F25D94")).
MarginTop(1).
MarginLeft(10).
MarginRight(10).
MarginLeft(5).
MarginRight(5).
Padding(0, 1).
Italic(true).
Foreground(lipgloss.Color("#FFF7DB"))
Expand Down Expand Up @@ -54,14 +54,6 @@ var (
Padding(0, 1).
Italic(false).
Foreground(lipgloss.Color("#FFFFFF"))

unusedVethStyle = lipgloss.NewStyle().
Background(lipgloss.Color("#F25D94")).
MarginLeft(5).
MarginRight(5).
Padding(0, 1).
Italic(true).
Foreground(lipgloss.Color("#FFF7DB"))
)

func colorGrid(xSteps, ySteps int) [][]string {
Expand Down
11 changes: 7 additions & 4 deletions pkg/formatter/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func Print(w io.Writer, vm map[string][]pkg.Node, netNsMap map[int]string, vpair

lw := list.NewWriter()
lw.SetOutputMirror(&content)
fmt.Fprintln(&content, TitleHighlight.SetString("Bridge <----> veth pair"))
fmt.Fprintln(&content, titleHighlight.SetString("Bridge <----> veth pair"))
for k, v := range vm {
master, err := netlink.LinkByName(k)
if err != nil {
Expand All @@ -39,6 +39,7 @@ func Print(w io.Writer, vm map[string][]pkg.Node, netNsMap map[int]string, vpair
f = true
lw.Indent()
}

lw.AppendItem(
vethStyle.SetString(
fmt.Sprintf("%s\t%s",
Expand All @@ -61,7 +62,7 @@ func Print(w io.Writer, vm map[string][]pkg.Node, netNsMap map[int]string, vpair
if all {
var vpair strings.Builder

fmt.Fprintln(&vpair, unusedVethStyle.SetString("unused veth pairs"))
fmt.Fprintln(&vpair, titleHighlight.SetString("not bridged veth pairs"))

visited := make(map[string]struct{})

Expand All @@ -71,10 +72,12 @@ func Print(w io.Writer, vm map[string][]pkg.Node, netNsMap map[int]string, vpair
continue
}

fmt.Fprintf(&vpair, "%s%s%s",
fmt.Fprintf(&vpair, "%s%s%s\t%s\n",
basicTextStyle.SetString(veth.Veth),
textHighlight.SetString("<----->"),
basicTextStyle.SetString(veth.Peer))
basicTextStyle.SetString(veth.Peer),
netNsStyle.SetString(netNsMap[veth.NetNsID]),
)
visited[h] = struct{}{}
}

Expand Down
32 changes: 18 additions & 14 deletions pkg/netutil/netutil_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import (
"github.com/vishvananda/netns"
)

const (
// https://man7.org/linux/man-pages/man8/ip-netns.8.html
netNsPath = "/var/run/netns"
// default docker dir
docNetNSkerPath = "/var/run/docker/netns"
)

// https://github.com/shemminger/iproute2/blob/main/ip/ipnetns.c#L432
// https://github.com/shemminger/iproute2/blob/main/ip/ipnetns.c#L106
func NetNsMap() (map[int]string, error) {
Expand Down Expand Up @@ -46,26 +53,23 @@ func NsidFromPath(path string) (int, error) {
func listNetNsPath() ([]string, error) {
var ns []string

// https://man7.org/linux/man-pages/man8/ip-netns.8.html
path := "/var/run/netns"
es, err := os.ReadDir(path)
es, err := os.ReadDir(netNsPath)
if err != nil && !os.IsNotExist(err) {
return nil, err
} else {
for _, e := range es {
ns = append(ns, filepath.Join(path, e.Name()))
ns = append(ns, filepath.Join(netNsPath, e.Name()))
}
}

// default docker dir
dockerPath := "/var/run/docker/netns"
dEs, err := os.ReadDir(dockerPath)
dEs, err := os.ReadDir(docNetNSkerPath)
if err != nil && !os.IsNotExist(err) {
return nil, err
} else {
for _, e := range dEs {
ns = append(ns, filepath.Join(docNetNSkerPath, e.Name()))
}
}
for _, e := range dEs {
ns = append(ns, filepath.Join(dockerPath, e.Name()))
}

return ns, nil
}

Expand All @@ -84,15 +88,15 @@ func GetLoInNs(ns string, origin netns.NsHandle) (netlink.Link, error) {
}

func netnsGetName(ns string, origin netns.NsHandle, fn func() (netlink.Link, error)) (link netlink.Link, err error) {
// Switch back to the original namespace
defer netns.Set(origin) //nolint: errcheck

hd, err := netns.GetFromPath(ns)
if err != nil {
return nil, err
}
if err := netns.Set(hd); err != nil {
return nil, err
}
// Switch back to the original namespace

defer netns.Set(origin) //nolint: errcheck
return fn()
}

0 comments on commit abbc193

Please sign in to comment.