pretty-ip/main.go

192 lines
4.6 KiB
Go

package main
import (
"fmt"
"io/ioutil"
"net"
"path/filepath"
"github.com/charmbracelet/lipgloss"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
func main() {
headerStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("63"))
ipStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("203"))
upStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("42"))
downStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("196"))
addrStyle := lipgloss.NewStyle().Faint(true)
infoStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("33"))
origNS, err := netns.Get()
if err != nil {
fmt.Println("Error getting current namespace:", err)
return
}
defer origNS.Close()
links, err := netlink.LinkList()
if err != nil {
fmt.Println("Error listing links:", err)
return
}
for _, link := range links {
attrs := link.Attrs()
if attrs == nil {
continue
}
state := downStyle.Render("DOWN")
if attrs.Flags&net.FlagUp != 0 {
state = upStyle.Render("UP")
}
fmt.Print(headerStyle.Render(attrs.Name), " ")
fmt.Printf(addrStyle.Render(" %s"), attrs.HardwareAddr)
fmt.Printf(addrStyle.Render(" %d"), attrs.MTU)
fmt.Printf(" %s\n", state)
addrs, err := netlink.AddrList(link, unix.AF_INET)
if err == nil {
for _, addr := range addrs {
fmt.Println(" ", ipStyle.Render(addr.IPNet.String()))
}
}
if link.Type() != "veth" {
fmt.Println()
continue
}
fmt.Println(" ", infoStyle.Render("Veth interface"))
peerFound := false
netnsDir := "/var/run/netns/"
entries, err := ioutil.ReadDir(netnsDir)
if err != nil {
fmt.Println(" ", infoStyle.Render("Could not scan netns dir"))
} else {
for _, entry := range entries {
nsPath := filepath.Join(netnsDir, entry.Name())
nsHandle, err := netns.GetFromPath(nsPath)
if err != nil {
continue
}
if err := netns.Set(nsHandle); err != nil {
nsHandle.Close()
continue
}
remoteLinks, err := netlink.LinkList()
if err == nil {
for _, rl := range remoteLinks {
if rl.Type() == "veth" && rl.Attrs().ParentIndex == attrs.Index {
fmt.Println(" ", infoStyle.Render("Peer in namespace:"), entry.Name(), "as", rl.Attrs().Name)
// Show IPs on peer
peerAddrs, err := netlink.AddrList(rl, unix.AF_INET)
if err == nil {
for _, pa := range peerAddrs {
fmt.Println(" ", ipStyle.Render(pa.IPNet.String()))
}
}
fmt.Println(" ", infoStyle.Render("Interfaces in namespace:"), entry.Name())
for _, intf := range remoteLinks {
intfState := downStyle.Render("DOWN")
if intf.Attrs().Flags&net.FlagUp != 0 {
intfState = upStyle.Render("UP")
}
fmt.Printf(" %s %s\n", headerStyle.Render(intf.Attrs().Name), intfState)
nsAddrs, err := netlink.AddrList(intf, unix.AF_INET)
if err == nil {
for _, addr := range nsAddrs {
fmt.Println(" ", ipStyle.Render(addr.IPNet.String()))
}
}
}
peerFound = true
routes, _ := netlink.RouteList(nil, unix.AF_INET)
if len(routes) > 0 {
fmt.Println(" ", infoStyle.Render("Routes in namespace:"), entry.Name())
for _, route := range routes {
dst := "default"
if route.Dst != nil {
dst = route.Dst.String()
}
via := ""
if route.Gw != nil {
via = "via " + route.Gw.String()
}
dev := ""
if link, err := netlink.LinkByIndex(route.LinkIndex); err == nil {
dev = "dev " + link.Attrs().Name
}
src := ""
if route.Src != nil {
src = "src " + route.Src.String()
}
fmt.Printf(" %s %s %s %s\n", dst, via, dev, src)
}
}
break
}
}
}
nsHandle.Close()
netns.Set(origNS)
if peerFound {
break
}
}
}
if !peerFound {
fmt.Println(" ", infoStyle.Render("Peer not found — may be deleted or hidden"))
}
fmt.Println()
}
fmt.Println(headerStyle.Render("Routes in main namespace:"))
mainRoutes, err := netlink.RouteList(nil, unix.AF_INET)
if err == nil {
for _, route := range mainRoutes {
var dst string
if route.Dst == nil {
dst = "default"
} else {
dst = route.Dst.String()
}
via := ""
if route.Gw != nil {
via = "via " + route.Gw.String()
}
dev := ""
if link, err := netlink.LinkByIndex(route.LinkIndex); err == nil {
dev = "dev " + link.Attrs().Name
}
src := ""
if route.Src != nil {
src = "src " + route.Src.String()
}
fmt.Printf(" %s %s %s %s\n", dst, via, dev, src)
}
}
fmt.Println()
}