
The Go programming language has a robust networking package called net, which provides a comprehensive set of functions for working with IP addresses.
Go's net package includes the netip package, which is a more modern and flexible alternative to the traditional net.IP type. The netip package is designed to work with both IPv4 and IPv6 addresses.
One of the key benefits of using netip is that it provides a more concise and expressive way to work with IP addresses. For example, you can use the netip.ParseAddr function to parse an IP address string into a netip.Addr object in a single line of code.
The netip package also includes a range of useful functions for working with IP addresses, such as netip.AddrToString, which converts a netip.Addr object back into a string.
For more insights, see: Golang Net Listen
Types and Classification
Tailscale needed a better IP address type, so they created a new package called inet.af/netaddr containing a new IP type and more.
The Go standard library's net.IP and net.IPNet types have problems, which is why Tailscale made their own package.
The Addr type provides several methods to classify IP addresses, making it a useful tool for tasks like binding to all interfaces except loopback.
Worth a look: Google Net
Type

The Addr type is a special kind of value that represents an IPv4 or IPv6 address. It's like net.IP or net.IPAddr, but with some extra features.
Unlike other types, Addr is comparable, which means you can use it in maps and it will work just fine. It's also immutable, so once you create an Addr, you can't change it.
The zero Addr is not a valid IP address, so don't try to use it. Addr{} is actually different from 0.0.0.0 and ::, so keep that in mind.
IP addresses can be classified into two main categories: IPv4 and IPv6. The Addr type has two methods to help you figure out which one you're dealing with: Is4 and Is6.
IsGlobalUnicast
The IsGlobalUnicast function is a handy tool for determining if an IP address is a global unicast address. It returns true for IPv6 addresses that fall outside of the 2000::/3 global unicast space, except for link-local addresses.

This function also returns true for IPv4 private address space and IPv6 unique local address space. It's worth noting that it returns false for the zero Addr.
In practical terms, this means that if you're working with IP addresses, you can use IsGlobalUnicast to quickly identify which ones are global unicast addresses and which ones are not. This can be especially useful when writing services that need to bind to specific interfaces.
As16
As16 is a method that returns the IP address in its 16-byte representation. This can be useful for certain applications or uses.
IPv4 addresses are returned as IPv4-mapped IPv6 addresses. This is a unique way of representing IPv4 addresses within the IPv6 address space.
The ip zero value returns all zeroes. This is a specific behavior to be aware of when working with the As16 method.
IPv6 addresses with zones are returned without their zone. If you need to get the zone, you can use the Addr.Zone method.
Zone

Zones are a crucial aspect of working with IPv6 addresses. They're primarily used with link-local addresses to specify which network interface to use.
An IPv6 address can have a zone, which is returned by the Zone function. This function takes an IP address as input and returns its IPv6 scoped addressing zone, if any.
You can also use the WithZone function to add a zone to an IP address. If the zone is empty, the zone is removed. This function is a no-op if the IP address is an IPv4 address.
IPv4 addresses don't have zones, so the WithZone function doesn't change them. This is an important distinction to make when working with different types of IP addresses.
A different take: Golang Reflect to Call Function in Package
Tailscale Needed Better Type
Tailscale needed a better IP address type to work effectively with networking and manipulate IP addresses.
The Go standard library's net.IP address type and net.IPNet type for networks were not suitable due to several problems.
Tailscale wrote a new package called inet.af/netaddr to address the issues with the standard library's types.
This new package contained a new IP type and more, which was used in some places but not widely adopted.
You might like: Golang Package
Link Local All Nodes
Link Local All Nodes is a crucial concept in IPv6 networking, and it's essential to understand how it works. IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast address ff02::1.
Link-local addresses are used to communicate with devices on the same network, and the all nodes address is used to broadcast a message to all devices on the network. This is particularly useful for network discovery and debugging.
The IPv6LinkLocalAllNodes function is a convenient way to get the all nodes address, making it easier to work with link-local addresses in your code.
See what others are reading: Golang Network Programming
Loopback Added in Go 1.20
The Go 1.20 release introduced a new function called IPv6Loopback.
This function returns the IPv6 loopback address, which is ::1.
The IPv6 loopback address is a special address that allows a network interface to communicate with itself.
In Go, this function can be used to get the IPv6 loopback address of a network interface.
See what others are reading: Go High Level Twilio Integration
Parsing and Validation
Parsing and validation are crucial steps when working with IP addresses in Go. The netip library provides several functions to help you achieve this.
MustParseAddr and MustParseAddrPort are two functions that call their Parse counterparts and panic on error, making them suitable for use in tests with hard-coded strings. These functions are particularly useful when you need to ensure that an IP address or address-port pair is valid.
You can use ParseAddr to parse an IP address from a string, which can be in dotted decimal, IPv6, or IPv6 with a scoped addressing zone format. Note that "0.0.0.0" and "::" are both valid values.
Here are some key differences between the various parsing functions:
When validating IP addresses, you can use the IsValid method, which reports whether the address is an initialized address. Note that all ports are valid, including zero.
Notes
As a developer, I've learned that understanding the history of a package can be beneficial in grasping its current implementation. The net/netip package was introduced to the Go standard library to provide advantages over the existing package net APIs.
The net/netip package was prototyped as an open source package, which is now found at https://inet.af/netaddr. This prototype played a significant role in shaping the final implementation of the net/netip package.
Net/netip offers several concrete advantages, including the ability to use netip.Addr as a map key, making it a valuable tool for developers of all levels.
IsMulticast
The "IsMulticast" function is a useful tool for determining whether an IP address is a multicast address. It's a simple way to check if an IP is being used for group communication.
This function is part of the "Addr" type and can be used to identify multicast addresses in both IPv4 and IPv6. It's a straightforward way to determine if an IP is being used for multicasting.
According to the documentation, "IsMulticast reports whether ip is a multicast address." This means that if you're working with IP addresses and need to check if they're being used for multicasting, this function is a good place to start.
The "IsMulticast" function is a useful addition to any networking code, as it helps prevent errors and ensures that IP addresses are being used correctly.
Related reading: Golang Generic Function
Parse

Parsing is a crucial step in working with IP addresses, and it's essential to understand how to parse them correctly. You can parse an IP address using the `ParseAddr` function, which takes a string as input and returns the parsed address.
The `ParseAddr` function can parse IP addresses in various formats, including dotted decimal, IPv6, and IPv6 with a scoped addressing zone. For example, you can parse the string "192.0.2.1" as an IPv4 address, or the string "2001:db8::68" as an IPv6 address.
Here are some examples of valid inputs for `ParseAddr`:
- Dotted decimal: "192.0.2.1"
- IPv6: "2001:db8::68"
- IPv6 with a scoped addressing zone: "fe80::1cc0:3e8c:119f:c2e1%ens18"
Keep in mind that `ParseAddr` will return an error if the input string is not a valid IP address. If you want to handle errors in a more robust way, you can use the `ParseAddr` function in combination with a try-catch block.
It's also worth noting that the `MustParseAddr` function is similar to `ParseAddr`, but it will panic if the input string is not a valid IP address. This can be useful in test code or other situations where you want to ensure that the input is valid, but it's generally not recommended for use in production code.
PortFrom

When working with IP addresses and port numbers, it's essential to have a reliable way to create an AddrPort object from a given IP and port.
You can use the AddrPortFrom function to create an AddrPort object without allocating new memory.
This function takes the provided IP and port as input and returns an AddrPort object with the specified IP and port.
The AddrPortFrom function does not allocate new memory, making it a memory-efficient option for creating AddrPort objects.
In Go, the AddrPortFrom function is a convenient way to create AddrPort objects from IP and port combinations.
For more insights, see: Golang Memory Management
Source Files
Source files contain the raw data that needs to be parsed and validated. This can include text files, JSON files, or even XML files.
In the context of parsing and validation, source files are the input data that needs to be processed. The parser will break down this data into smaller pieces, called tokens, to understand its structure and content.

A good example of a source file is the JSON data provided earlier, which contains a list of books with their respective titles, authors, and prices. This data needs to be parsed and validated to ensure it conforms to the expected format.
The parser will check the source file for errors, such as missing or extra fields, and validate the data against a set of predefined rules. This ensures that the data is accurate and consistent throughout the entire system.
Intriguing read: Golang Os.writefile
What's Wrong With Go's Net Type?
Go's net.IP type has some significant issues that can make it tricky to work with. Its underlying type is just a []byte, which means it's mutable and can be changed by external functions.
This mutability can lead to safety issues and make it harder to reason about the code. Immutable data structures, on the other hand, are safer and easier to work with.
Go's net.IP type also has some limitations when it comes to comparisons. A slice in Go is not comparable, which means it doesn't support the == operator and can't be used as a map key.

This can make it difficult to write efficient and effective code that needs to compare IP addresses. For example, if you're trying to create a map of IP addresses to some other value, you'll have to use a different data structure.
The net.IP type is also quite large, with a 24-byte slice header and then the actual IP address bytes. This can lead to performance issues and increased memory usage.
If you need to support IPv6 zone scopes, you'll have to use the net.IPAddr type, which adds even more overhead. This can make it harder to write efficient code that needs to work with IP addresses.
Parsing an IP address from a string can also be problematic with Go's net.IP type. It can't distinguish between IPv4-mapped IPv6 addresses and IPv4 addresses, which can lead to errors and inconsistencies.
Here are some of the key issues with Go's net.IP type:
- Mutability
- Lack of comparability
- Size and performance issues
- Allocation problems
- Inability to distinguish between IPv4-mapped IPv6 addresses and IPv4 addresses
Filter Application
In a real-world scenario, filtering is crucial to ensure the integrity of our data. IP address filtering is a simple yet effective way to achieve this.

The IP address filter is used to restrict access to a web service based on the client's IP address. This is demonstrated in the example of an IP filter that could be used in a web service.
A practical application of IP filtering is to block malicious traffic from certain IP addresses. This can be achieved by creating a list of known malicious IP addresses and checking each incoming request against this list.
IP filtering can be implemented using various programming languages and frameworks, including Python and JavaScript. This allows developers to easily integrate IP filtering into their web services.
By implementing IP filtering, developers can protect their web services from malicious traffic and ensure the integrity of their data.
Here's an interesting read: Golang List
Opaque Comparable Interfaces
In Go, interfaces are comparable, but they panic at runtime if the underlying value in the interface is not comparable. This can be used to create an opaque struct that embeds an interface, making it comparable.
Related reading: Type Interface Golang

The first version of netaddr.IP took advantage of this by representing it as an opaque struct embedding an interface. This reduced the size from 24 bytes to 16 bytes on 64-bit machines.
Here's a comparison of different IP representations:
The "Take 2" representation is smaller than the standard library's net.IP, but it still needs a pointer to the actual bytes of the IP address.
Methods and Operations
Golang's net/ip package provides a way to work with IP addresses, which are crucial for network communication.
The net/ip package uses the IP address format to represent IPv4 and IPv6 addresses, which is a combination of a prefix length and an address.
To work with IP addresses, you can use the Parse function to convert a string to an IP address, as demonstrated in the example code.
MarshalBinary
MarshalBinary is a method that implements the encoding.BinaryMarshaler interface. It's a crucial operation that allows us to convert complex data into a binary format.

The MarshalBinary method returns the result of calling Addr.MarshalBinary with an additional two bytes appended. These extra bytes contain the port in little-endian format.
This method is useful for encoding data in a binary format that can be easily transmitted or stored. I've seen it used in network programming where data needs to be sent over the wire in a compact binary format.
The MarshalBinary method takes into account the port number when encoding the data. It ensures that the port is included in the binary output, which is essential for network communication.
In summary, MarshalBinary is an important method that helps us convert data into a binary format with a specific structure.
Append to Port
The AddrPort type has an AppendTo method that appends a text encoding of p to a buffer b and returns the extended buffer.
This method is similar to MarshalText, but it appends the encoding to a buffer instead of returning it as a string.

The encoding is the same as returned by AddrPort.String, with one exception: if p.Addr() is the zero Addr, the encoding is the empty string.
AddrPort also has an AppendText method that implements the encoding.TextAppender interface, which is similar to AppendTo.
Here's a comparison of the two methods:
BitLen
The BitLen method is a straightforward way to determine the number of bits in an IP address. It returns 128 for IPv6 addresses, which is consistent regardless of the address's actual value.
IPv4 addresses, on the other hand, have a bit length of 32. This is true even for IPv4-mapped IPv6 addresses, which are considered IPv6 addresses and therefore have a bit length of 128.
The zero Addr, a special case, returns a bit length of 0. This makes sense, as it's a placeholder value rather than a valid IP address.
For your interest: Golang String Length
Less
Less is a method that reports whether one IP address sorts before another. It's used to compare IP addresses, which sort first by length and then by their actual address.

For example, IPv6 addresses with zones sort just after the same address without a zone. This means that if you have two IPv6 addresses, one with a zone and one without, the one without a zone will come before the one with a zone in a sorted list.
IP addresses sort by length first, so a shorter IP address will come before a longer one, even if the longer one is actually the same address without a zone.
Type Port
Type Port is a specific format that represents an IP address and a port number.
In programming, you can use the AddrPort type to specify both the address and the port number.
The AddrPort type is an IP address and a port number combined.
You can parse an AddrPort using the ParseAddrPort function, but be aware that it doesn't do any name resolution, so both the address and the port must be numeric.
A unique perspective: Check Type of Interface Golang
Get Preferred Outbound

You can get the preferred outbound IP address from within a Go application using a UDP connection. This method involves preparing a UDP connection to any external address.
The net.Dial() function from the net package is used to prepare a UDP connection to the Google DNS server (8.8.8.8). The destination address and port don't matter, as UDP doesn't establish a connection.
The connection created can be used to get the local IP address that a UDP connection would use if it were sending data to the destination address. This can be done using the LocalAddr() method of the connection.
The LocalAddr() method returns the net.Addr interface, which can then be casted using type assertion to a specific net.UDPAddr. This allows the IP address to be extracted as a net.IP struct.
This method is useful for getting the local IP address from within a Go application. It's a straightforward approach that works by leveraging the characteristics of UDP connections.
Comparison and Analysis
In Go, you can compare IP addresses and port numbers using the Compare method. The result will be 0 if the IP addresses are equal, -1 if one IP address is less than the other, and +1 if one IP address is greater than the other.
The definition of "less than" is the same as the Addr.Less method, which means it compares the IP addresses in a specific way. This comparison is used in both IP and AddrPort comparisons.
IP addresses are compared first by their numerical value, and if they're equal, the port numbers are compared. This means that two IP addresses with the same numerical value but different port numbers will be considered less than each other if the port numbers are in the correct order.
Intriguing read: Golang Mapof Nil Value
Compare
The Compare function is a useful tool in determining the relationship between two IP addresses or AddrPorts. It returns an integer value that indicates whether one is less than, equal to, or greater than the other.

For IP addresses, Compare returns 0 if the two are equal, -1 if the first is less than the second, and +1 if the first is greater than the second.
The definition of "less than" for IP addresses is the same as the Addr.Less method. This means that the Compare function will return the same result as Addr.Less would.
AddrPorts, on the other hand, are compared first by their IP address, then by their port. This means that if two AddrPorts have the same IP address, their port numbers will be compared to determine their order.
The Compare function returns 0 if the two AddrPorts are equal, -1 if the first is less than the second, and +1 if the first is greater than the second.
Prefix Overlaps
Prefix Overlaps is a crucial concept in IP address management, and it's essential to understand how it works. The Prefix Overlaps function, as seen in Example 3, reports whether two prefixes, p and o, contain any IP addresses in common.

If p and o are of different address families, Prefix Overlaps will return false. This is because different address families, such as IPv4 and IPv6, cannot overlap.
A prefix with a zero IP will also return false, as there are no IP addresses to overlap. This is consistent with the behavior of the Prefix function in Example 1.
Prefix Overlaps is useful for determining whether two prefixes share any IP addresses, which can be critical in network design and management.
Local vs Public
Local IP addresses are used for communication within a local network, such as a home or office network. They are also known as private IP addresses and are typically assigned to a device by a local router.
Local IP addresses usually begin with 192.168.x.x, 172.16.x.x to 172.31.x.x, or 10.x.x.x. This is in contrast to public IP addresses, which are used for communication over the Internet and are assigned to a device by an Internet Service Provider.

Public IP addresses are necessary for devices that need to communicate outside of a local network, such as accessing websites or remote servers. They are unique globally, meaning that no two devices can have the same public IP address.
The main difference between local and public IP addresses is their scope of usage and reachability. If you're trying to access a device within your local network, you'll use its local IP address. If you need to communicate with a device outside of your network, you'll use its public IP address.
Performance and Optimization
When working with Golang's net/ip package, performance and optimization are crucial considerations.
The net/netip.Addr type is a value type, which means it doesn't allocate memory on the heap for basic operations.
This results in efficient comparison operations, making it a great choice for performance-critical code.
The netip version typically performs better than the old net.IP version and generates less garbage for the GC to handle.
Here are some key performance characteristics of net/netip.Addr:
- No heap allocations for basic operations
- Efficient comparison operations
- Zero-value is invalid
Other INET Fun
INET offers more than just IP addresses. You can work with IP addresses and ports using the IPPort value type.
The IPPort value type is a combination of an IP address and a port. This is useful when you need to represent a connection or a service.
INET also includes the IP Prefix, which is an IP address with a CIDR prefix. For example, 192.168.0.1/16.
You can represent a range of IP addresses using the IPRange value type. This is useful when you need to specify a group of IP addresses.
The IPSet is an efficient and immutable set of IP addresses. It's built using an IPSetBuilder.
Here are some examples of INET's value types:
- IPPort: IP address and port
- IPPrefix: IP address and CIDR prefix
- IPRange: Range of IP addresses
- IPSet: Efficient, immutable set of IP addresses
Featured Images: pexels.com


