Skip to content

Golang IP address conversion

IP address conversion with golang

Finding an IP network's broadcast IP

The following function calculates the broadcast IP for both IPv4 and IPv6 networks:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func subnetBroadcastIP(ipnet net.IPNet) (net.IP, error) {
    byteIp := []byte(ipnet.IP)                // []byte representation of IP
    byteMask := []byte(ipnet.Mask)            // []byte representation of mask
    byteTargetIp := make([]byte, len(byteIp)) // []byte holding target IP
    for k, _ := range byteIp {
        // mask will give us all fixed bits of the subnet (for the given byte)
        // inverted mask will give us all moving bits of the subnet (for the given byte)
        invertedMask := byteMask[k] ^ 0xff // inverted mask byte

        // broadcastIP = networkIP added to the inverted mask
        byteTargetIp[k] = byteIp[k]&byteMask[k] | invertedMask
    }

    return net.IP(byteTargetIp), nil
}

In order to understand the aforementioned example better, here is a longer version (the if / else block is useless and hence removed):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func subnetBroadcastIP(ipnet net.IPNet) (net.IP, error) {
    // ipv4
    if ipnet.IP.To4() != nil {
        // ip address in uint32
        ipBits := binary.BigEndian.Uint32(ipnet.IP.To4())

        // mask will give us all fixed bits of the subnet
        maskBits := binary.BigEndian.Uint32(ipnet.Mask)

        // inverted mask will give us all moving bits of the subnet
        invertedMaskBits := maskBits ^ 0xffffffff // xor the mask

        // network ip
        networkIpBits := ipBits & maskBits

        // broadcastIP = networkIP added to the inverted mask
        broadcastIpBits := networkIpBits | invertedMaskBits

        broadcastIp := make(net.IP, 4)
        binary.BigEndian.PutUint32(broadcastIp, broadcastIpBits)
        return broadcastIp, nil
    }
    // ipv6
    // this conversion is actually easier, follows the same principle as above
    byteIp := []byte(ipnet.IP)                // []byte representation of IP
    byteMask := []byte(ipnet.Mask)            // []byte representation of mask
    byteTargetIp := make([]byte, len(byteIp)) // []byte holding target IP
    for k, _ := range byteIp {
        invertedMask := byteMask[k] ^ 0xff // inverted mask byte
        byteTargetIp[k] = byteIp[k]&byteMask[k] | invertedMask
    }

    return net.IP(byteTargetIp), nil
}

Finding all IPs in a subnet

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func subnetIPs(ipnet net.IPNet) ([]net.IP, error) {
        var ipList []net.IP
        ip := ipnet.IP
        for ; ipnet.Contains(ip); ip = incIP(ip) {
                ipList = append(ipList, ip) 
        }

        return ipList, nil 
}

func incIP(ip net.IP) net.IP {
        // allocate a new IP
        newIp := make(net.IP, len(ip))
        copy(newIp, ip) 

        byteIp := []byte(newIp)
        l := len(byteIp)
        var i int 
        for k, _ := range byteIp {
                // start with the rightmost index first
                // increment it
                // if the index is < 256, then no overflow happened and we increment and break
                // else, continue to the next field in the byte
                i = l - 1 - k 
                if byteIp[i] < 0xff {
                        byteIp[i]++
                        break
                } else {
                        byteIp[i] = 0 
                }
        }
        return net.IP(byteIp)
}

Last update: March 30, 2022