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)