package main import ( "flag" "strings" "fmt" "io/ioutil" "net" "path/filepath" "github.com/charmbracelet/lipgloss" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) type stringSlice []string func (s *stringSlice) String() string { return strings.Join(*s, ",") } func (s *stringSlice) Set(value string) error { *s = append(*s, value) return nil } func main() { var interfaces stringSlice flag.Var(&interfaces, "iface", "Interface(s) to filter on (can be specified multiple times)") flag.Parse() // Build lookup map filterSet := make(map[string]struct{}) for _, name := range interfaces { filterSet[name] = struct{}{} } // Determine if we are filtering at all filtering := len(filterSet) > 0 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 } if filtering { if _, ok := filterSet[attrs.Name]; !ok { 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() }