Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if IP address is in private network space

I have a program in go which accepts URLs from clients and gets them using the net/http package. Before doing further processing, I would like to check if the URL maps to private (non-routable / RFC1918 networks) address space.

The straight-forward way would be to perform an explicit DNS request and check the address for the known private ranges. After that, perform the HTTP GET request for the URL.

Is there a better way to accomplish this? Preferably integrating with http.Client so it can be performed as a part of the GET request.

like image 868
StefanOS Avatar asked Dec 20 '16 11:12

StefanOS


People also ask

Is 192.168 private or public?

And don't be surprised if you have a device or two at home with a so-called 192 IP address, or a private IP address beginning with 192.168. This is the most common default private IP address format assigned to network routers around the globe.

Are all 172 IP addresses private?

Note that only a portion of the “172” and the “192” address ranges are designated for private use. The remaining addresses are considered “public,” and thus are routable on the global Internet.


4 Answers

That should be easier to do with Go 1.17 (Q4 2021, 5 years later), as reported by Go 101:

See commit c73fccc and CL 272668:

net: add IP.IsPrivate()

Add IsPrivate() helper to check if an IP is private according to RFC 1918 & RFC 4193

That fixes golang issue 29146 raised by Aaran McGuire:

The net package seems to have many helpers to report what an IP is. e.g:

  • IsLoopback()
  • IsMulticast()
  • IsInterfaceLocalMulticast()

However there are no helpers to report if a IP address is in the private ranges (RFC 1918 & RFC 4193).

like image 124
VonC Avatar answered Oct 01 '22 05:10

VonC


package main

import (
    "fmt"
    "net"
)

func main() {
    fmt.Println(privateIPCheck("1.1.1.1"))  // False since this is not a private IP
    fmt.Println(privateIPCheck("10.8.0.1")) // True: Since this is a private ip.
}

// Check if a ip is private.
func privateIPCheck(ip string) bool {
    ipAddress := net.ParseIP(ip)
    return ipAddress.IsPrivate()
}

This requires Go 1.17.

like image 23
prajwal koirala Avatar answered Oct 01 '22 04:10

prajwal koirala


You might also want to include checks for loopback (IPv4 or IPv6) and/or IPv6 link-local or unique-local addresses. Here is an example with a list of RFC1918 address plus these others and a simple check against them as isPrivateIP(ip net.IP):

var privateIPBlocks []*net.IPNet

func init() {
    for _, cidr := range []string{
        "127.0.0.0/8",    // IPv4 loopback
        "10.0.0.0/8",     // RFC1918
        "172.16.0.0/12",  // RFC1918
        "192.168.0.0/16", // RFC1918
        "169.254.0.0/16", // RFC3927 link-local
        "::1/128",        // IPv6 loopback
        "fe80::/10",      // IPv6 link-local
        "fc00::/7",       // IPv6 unique local addr
    } {
        _, block, err := net.ParseCIDR(cidr)
        if err != nil {
            panic(fmt.Errorf("parse error on %q: %v", cidr, err))
        }
        privateIPBlocks = append(privateIPBlocks, block)
    }
}

func isPrivateIP(ip net.IP) bool {
    if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
        return true
    }

    for _, block := range privateIPBlocks {
        if block.Contains(ip) {
            return true
        }
    }
    return false
  }
like image 27
Brad Peabody Avatar answered Oct 01 '22 04:10

Brad Peabody


It seems there's no better way to accomplish than the one I described. Combining code from @MichaelHausenblas with the suggestion from @JimB, my code ended up kind of like this.

func privateIP(ip string) (bool, error) {
    var err error
    private := false
    IP := net.ParseIP(ip)
    if IP == nil {
        err = errors.New("Invalid IP")
    } else {
        _, private24BitBlock, _ := net.ParseCIDR("10.0.0.0/8")
        _, private20BitBlock, _ := net.ParseCIDR("172.16.0.0/12")
        _, private16BitBlock, _ := net.ParseCIDR("192.168.0.0/16")
        private = private24BitBlock.Contains(IP) || private20BitBlock.Contains(IP) || private16BitBlock.Contains(IP)
    }
    return private, err
}
like image 41
StefanOS Avatar answered Oct 01 '22 03:10

StefanOS