DEV Community

Rez Moss
Rez Moss

Posted on

Deep Dive into net/netip Addr Methods 5/7

Hey! We've covered a lot of ground in our previous articles about net/netip. Today, we're going to do a deep dive into all the methods available on the Addr type. While we've touched on some of these before, now we'll explore each one in detail with real-world examples and use cases.

Core Methods Overview

The Addr type has quite a few methods, and understanding when to use each one is crucial for effective network programming. Let's break them down by category.

Address Creation and Validation

package main import ( "fmt" "net/netip" ) func demoAddressCreation() { // From string addr1, _ := netip.ParseAddr("192.168.1.1") // From 4-byte array addr2 := netip.AddrFrom4([4]byte{192, 168, 1, 1}) // From 16-byte array addr3 := netip.AddrFrom16([16]byte{ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }) fmt.Printf("From string: %v\n", addr1) fmt.Printf("From bytes4: %v\n", addr2) fmt.Printf("From bytes16: %v\n", addr3) } 
Enter fullscreen mode Exit fullscreen mode

Byte Array Conversions

func demoByteConversions(addr netip.Addr) { if addr.Is4() { bytes4 := addr.As4() fmt.Printf("IPv4 bytes: %v\n", bytes4) // Convert to 16-byte representation bytes16 := addr.As16() fmt.Printf("IPv4-mapped IPv6 bytes: %v\n", bytes16) } else if addr.Is6() { bytes16 := addr.As16() fmt.Printf("IPv6 bytes: %v\n", bytes16) } } 
Enter fullscreen mode Exit fullscreen mode

Address Type Checking

Let's create a comprehensive function to analyze an IP address:

func analyzeAddress(addr netip.Addr) { // Basic version checks fmt.Printf("Address: %v\n", addr) fmt.Printf("Is IPv4? %v\n", addr.Is4()) fmt.Printf("Is IPv6? %v\n", addr.Is6()) fmt.Printf("Is IPv4-mapped IPv6? %v\n", addr.Is4In6()) // Address properties fmt.Printf("Unmap if mapped: %v\n", addr.Unmap()) fmt.Printf("Bit length: %d\n", addr.BitLen()) fmt.Printf("Zone: %q\n", addr.Zone()) // Classification checks := []struct { name string fn func() bool }{ {"IsGlobalUnicast", addr.IsGlobalUnicast}, {"IsPrivate", addr.IsPrivate}, {"IsLoopback", addr.IsLoopback}, {"IsMulticast", addr.IsMulticast}, {"IsLinkLocalUnicast", addr.IsLinkLocalUnicast}, {"IsLinkLocalMulticast", addr.IsLinkLocalMulticast}, {"IsInterfaceLocalMulticast", addr.IsInterfaceLocalMulticast}, {"IsUnspecified", addr.IsUnspecified}, } fmt.Println("\nAddress Classifications:") for _, check := range checks { if check.fn() { fmt.Printf("- %s\n", check.name) } } } 
Enter fullscreen mode Exit fullscreen mode

Practical Use Cases

1. Network Interface Configuration Validator

This tool helps validate network interface configurations:

type InterfaceConfig struct { Address netip.Addr AllowedUse []string } func validateInterfaceConfig(config InterfaceConfig) []string { var issues []string // Check for unspecified address if config.Address.IsUnspecified() { issues = append(issues, "address is unspecified") } // Validate based on intended use for _, use := range config.AllowedUse { switch use { case "public": if !config.Address.IsGlobalUnicast() { issues = append(issues, "address is not suitable for public use") } if config.Address.IsPrivate() { issues = append(issues, "public interface cannot use private address") } case "private": if !config.Address.IsPrivate() && !config.Address.IsLoopback() { issues = append(issues, "private interface must use private address") } case "link-local": if !config.Address.IsLinkLocalUnicast() { issues = append(issues, "address is not link-local") } } } return issues } 
Enter fullscreen mode Exit fullscreen mode

2. IP Address Classifier Service

A service that provides detailed information about IP addresses:

type AddressInfo struct { Address string `json:"address"` Version int `json:"version"` Categories []string `json:"categories"` Properties []string `json:"properties"` Warnings []string `json:"warnings"` } func classifyAddress(addrStr string) (AddressInfo, error) { info := AddressInfo{ Address: addrStr, } addr, err := netip.ParseAddr(addrStr) if err != nil { return info, fmt.Errorf("invalid address: %w", err) } // Determine version if addr.Is4() { info.Version = 4 } else { info.Version = 6 } // Categorize address if addr.IsGlobalUnicast() { info.Categories = append(info.Categories, "global-unicast") if addr.IsPrivate() { info.Categories = append(info.Categories, "private") } else { info.Categories = append(info.Categories, "public") } } if addr.IsLoopback() { info.Categories = append(info.Categories, "loopback") } if addr.IsMulticast() { info.Categories = append(info.Categories, "multicast") if addr.IsLinkLocalMulticast() { info.Categories = append(info.Categories, "link-local-multicast") } if addr.IsInterfaceLocalMulticast() { info.Categories = append(info.Categories, "interface-local-multicast") } } if addr.IsLinkLocalUnicast() { info.Categories = append(info.Categories, "link-local-unicast") } // Add properties if zone := addr.Zone(); zone != "" { info.Properties = append(info.Properties, fmt.Sprintf("zone:%s", zone)) } if addr.Is4In6() { info.Properties = append(info.Properties, "ipv4-mapped-ipv6") } // Add warnings if addr.IsUnspecified() { info.Warnings = append(info.Warnings, "address is unspecified") } return info, nil } 
Enter fullscreen mode Exit fullscreen mode

3. Network Security Analyzer

A tool to check for potentially problematic IP configurations:

type SecurityCheck struct { Level string Issue string } func analyzeSecurityImplications(addr netip.Addr) []SecurityCheck { var checks []SecurityCheck // Check for obvious issues if addr.IsUnspecified() { checks = append(checks, SecurityCheck{ Level: "HIGH", Issue: "unspecified address should not be used in production", }) } if addr.IsLoopback() { checks = append(checks, SecurityCheck{ Level: "MEDIUM", Issue: "loopback address might indicate misconfiguration", }) } // Public service checks if addr.IsGlobalUnicast() && !addr.IsPrivate() { if addr.Is4() { checks = append(checks, SecurityCheck{ Level: "INFO", Issue: "public IPv4 address - ensure proper firewall rules", }) } else { checks = append(checks, SecurityCheck{ Level: "INFO", Issue: "public IPv6 address - ensure proper firewall rules and address privacy", }) } } // Link-local checks if addr.IsLinkLocalUnicast() { checks = append(checks, SecurityCheck{ Level: "LOW", Issue: "link-local address - verify if this is intended", }) if addr.Zone() == "" { checks = append(checks, SecurityCheck{ Level: "MEDIUM", Issue: "link-local address without zone identifier", }) } } // Multicast checks if addr.IsMulticast() { checks = append(checks, SecurityCheck{ Level: "INFO", Issue: "multicast address - verify scope and purpose", }) } return checks } 
Enter fullscreen mode Exit fullscreen mode

Method Deep-Dives

Compare and Less Methods

Understanding the comparison methods:

func demonstrateComparisons() { addr1 := netip.MustParseAddr("192.168.1.1") addr2 := netip.MustParseAddr("192.168.1.2") addr3 := netip.MustParseAddr("2001:db8::1") fmt.Printf("Compare 1 vs 2: %d\n", addr1.Compare(addr2)) // -1 fmt.Printf("Less 1 vs 2: %v\n", addr1.Less(addr2)) // true // Note: Comparing IPv4 and IPv6 fmt.Printf("Compare IPv4 vs IPv6: %d\n", addr1.Compare(addr3)) } 
Enter fullscreen mode Exit fullscreen mode

Next and Prev Methods

Working with address sequences:

func demonstrateSequencing() { start := netip.MustParseAddr("192.168.1.1") // Print next 5 addresses current := start fmt.Printf("Starting from: %v\n", current) for i := 0; i < 5; i++ { current = current.Next() fmt.Printf("Next: %v\n", current) } // Print previous 5 addresses current = start for i := 0; i < 5; i++ { current = current.Prev() fmt.Printf("Previous: %v\n", current) } } 
Enter fullscreen mode Exit fullscreen mode

Common Patterns and Best Practices

  1. Version-Specific Operations
 func handleAddress(addr netip.Addr) { if addr.Is4() { // IPv4-specific code bytes4 := addr.As4() // ... } else if addr.Is6() { // IPv6-specific code bytes16 := addr.As16() // ... } } 
Enter fullscreen mode Exit fullscreen mode
  1. Safe Conversion Patterns
 func convertToIPv4IfPossible(addr netip.Addr) netip.Addr { if addr.Is4In6() { return addr.Unmap() } return addr } 
Enter fullscreen mode Exit fullscreen mode
  1. Zone Handling
 func handleZonedAddress(addr netip.Addr) error { if addr.IsLinkLocalUnicast() && addr.Zone() == "" { return fmt.Errorf("link-local address requires zone") } return nil } 
Enter fullscreen mode Exit fullscreen mode

Performance Tips

  1. Avoid Unnecessary String Conversions
 // Bad addr.String() // Called in a loop // Good // Keep as Addr until string representation is needed 
Enter fullscreen mode Exit fullscreen mode
  1. Use As4/As16 Efficiently
 // Bad bytes := addr.As16() // Always converts to 16 bytes // Good if addr.Is4() { bytes4 := addr.As4() // More efficient for IPv4 } 
Enter fullscreen mode Exit fullscreen mode
  1. Caching Results
 type CachedAddr struct { addr netip.Addr isPrivate bool // Cached result } func newCachedAddr(addr netip.Addr) CachedAddr { return CachedAddr{ addr: addr, isPrivate: addr.IsPrivate(), } } 
Enter fullscreen mode Exit fullscreen mode

What's Next?

In our next article, we'll explore AddrPort methods in detail, building on what we've learned about Addr methods. We'll see how these types work together in real-world networking applications.

Until then, keep experimenting with these methods! They're the building blocks of robust network programming in Go.

Top comments (0)