Building system utilities, container orchestrators, or network monitoring daemons in Go often requires querying the host machine's hardware and virtual network interfaces. Developers need to programmatically determine hardware MAC addresses, IP subnets, MTU sizes, and interface operational states.

Go's standard library package net provides cross-platform abstractions (net.Interfaces() and net.InterfaceAddrs()) to query low-level network adapter properties across Linux, macOS, and Windows. In this article, we write a complete Go program to inspect and display interface details.

Quick Reference: Enumerating Network Adapters

The standard idiom for iterating active network interfaces and parsing associated IP subnets in Go:

quick_reference.gogo
package main
 
import (
	"fmt"
	"log"
	"net"
)
 
func main() {
	ifaces, err := net.Interfaces()
	if err != nil {
		log.Fatalf("Failed to retrieve interfaces: %v", err)
	}
 
	for _, iface := range ifaces {
		fmt.Printf("Name: %s | MAC: %s | MTU: %d | Flags: %v
",
			iface.Name, iface.HardwareAddr, iface.MTU, iface.Flags)
	}
}

1. Complete Go Program to Inspect Interface Details (main.go)

This Go program iterates over every physical and virtual network interface, displaying index IDs, MAC addresses, MTU limits, operational status flags, and assigned IPv4/IPv6 CIDR subnets:

main.gogo
package main
 
import (
	"fmt"
	"log"
	"net"
)
 
func main() {
	// 1. Fetch slice of all network interfaces on the host system
	interfaces, err := net.Interfaces()
	if err != nil {
		log.Fatalf("Error retrieving network interfaces: %v", err)
	}
 
	fmt.Println("=========================================================")
	fmt.Println("       HOST NETWORK INTERFACE DISCOVERY DETAILS          ")
	fmt.Println("=========================================================")
 
	for _, iface := range interfaces {
		fmt.Printf("
[ Interface #%d: %s ]
", iface.Index, iface.Name)
		
		// Display MAC Hardware Address
		mac := iface.HardwareAddr.String()
		if mac == "" {
			mac = "None (Virtual / Loopback)"
		}
		fmt.Printf("  Hardware MAC Address : %s
", mac)
		fmt.Printf("  Maximum Transmission : %d bytes (MTU)
", iface.MTU)
		fmt.Printf("  Status Flags         : %v
", iface.Flags)
 
		// Check boolean interface flags
		isUp := (iface.Flags & net.FlagUp) != 0
		isLoopback := (iface.Flags & net.FlagLoopback) != 0
		fmt.Printf("  Operational State    : Up=%t, Loopback=%t
", isUp, isLoopback)
 
		// 2. Fetch unicast and broadcast IP addresses bound to this interface
		addrs, err := iface.Addrs()
		if err != nil {
			fmt.Printf("  Error fetching addresses: %v
", err)
			continue
		}
 
		fmt.Println("  Bound IP Addresses   :")
		if len(addrs) == 0 {
			fmt.Println("    - No IP address assigned")
		}
 
		for _, addr := range addrs {
			// Type-assert address to *net.IPNet to extract IP and Subnet Mask
			if ipNet, ok := addr.(*net.IPNet); ok {
				if ip4 := ipNet.IP.To4(); ip4 != nil {
					fmt.Printf("    - IPv4 : %-15s (Mask: %s, CIDR: %s)
",
						ip4.String(), ipNet.Mask.String(), ipNet.String())
				} else if ip6 := ipNet.IP.To16(); ip6 != nil {
					fmt.Printf("    - IPv6 : %s
", ip6.String())
				}
			}
		}
	}
}

2. Running the Go Utility

Compile and run the Go program from your terminal:

Terminal Command & Sample Outputbash
$ go run main.go
 
=========================================================
       HOST NETWORK INTERFACE DISCOVERY DETAILS          
=========================================================
 
[ Interface #1: lo ]
  Hardware MAC Address : None (Virtual / Loopback)
  Maximum Transmission : 65536 bytes (MTU)
  Status Flags         : up|loopback
  Operational State    : Up=true, Loopback=true
  Bound IP Addresses   :
    - IPv4 : 127.0.0.1       (Mask: ffffff00, CIDR: 127.0.0.1/8)
    - IPv6 : ::1
 
[ Interface #2: wlan0 ]
  Hardware MAC Address : ac:fd:ce:81:49:b2
  Maximum Transmission : 1500 bytes (MTU)
  Status Flags         : up|broadcast|multicast
  Operational State    : Up=true, Loopback=false
  Bound IP Addresses   :
    - IPv4 : 192.168.1.105   (Mask: ffffff00, CIDR: 192.168.1.105/24)
    - IPv6 : fe80::8802:11fb:12a8:442c

Under the Hood: OS-Specific Kernel Syscalls

  • Linux (`AF_NETLINK` Sockets): On Linux, net.Interfaces() opens a Netlink socket (socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) and queries RTM_GETLINK and RTM_GETADDR kernel messages.

  • macOS / BSD (Routing Sockets): On BSD and macOS systems, Go reads system routing tables via sysctl with NET_RT_IFLIST.

  • Windows (`GetAdaptersAddresses`): On Windows, Go invokes the Win32 IP Helper API function GetAdaptersAddresses().