...

Source file src/golang.conradwood.net/go-easyops/utils/ip_helper.go

Documentation: golang.conradwood.net/go-easyops/utils

     1  package utils
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  /*
    11  Parse an IPAddress. this may be either IPv4 or IPv6.
    12  The result is the IPaddress (perhaps normalised), the port (if any), the version
    13  or an error (if unparseable)
    14  */
    15  func ParseIP(ip_s string) (string, uint32, int, error) {
    16  	if len(ip_s) < 4 {
    17  		return "", 0, 0, fmt.Errorf("\"%s\" is not a valid ip", ip_s)
    18  	}
    19  	ct := strings.Count(ip_s, ":")
    20  	if ct == 1 {
    21  		idx := strings.Index(ip_s, ":")
    22  		ip := ip_s[:idx]
    23  		port, err := strconv.Atoi(ip_s[idx+1:])
    24  		if err != nil {
    25  			return "", 0, 0, err
    26  		}
    27  		return ip, uint32(port), 4, nil
    28  	}
    29  
    30  	if ct == 0 {
    31  		return ip_s, 0, 4, nil
    32  	}
    33  
    34  	// must be ip6:
    35  	if ip_s[0] != '[' {
    36  		// without port
    37  		return ip_s, 0, 6, nil
    38  	}
    39  
    40  	ep := ip_s[1:]
    41  	idx := strings.Index(ep, "]")
    42  	if idx == -1 {
    43  		return "", 0, 0, fmt.Errorf("not a valid ipv6 with port: \"%s\"", ip_s)
    44  	}
    45  	ip := ep[:idx]
    46  	port, err := strconv.Atoi(ep[idx+2:]) // skip "]" and ":"
    47  	if err != nil {
    48  		return "", 0, 0, err
    49  	}
    50  	return ip, uint32(port), 6, nil
    51  }
    52  
    53  // error if not a valid ip
    54  // true if it is a non-routeable IP, such as link-local, loopback, or rfc 1918
    55  func IsPrivateIP(ip_s string) (bool, error) {
    56  	ip, masksize, err := GetIPAndNet(ip_s)
    57  	if err != nil {
    58  		return false, err
    59  	}
    60  	ipa, _, err := net.ParseCIDR(fmt.Sprintf("%s/%d", ip, masksize))
    61  	if err != nil {
    62  		return false, err
    63  	}
    64  
    65  	if ipa.IsPrivate() {
    66  		return true, nil
    67  	}
    68  	if ipa.IsLoopback() {
    69  		return true, nil
    70  	}
    71  	if ipa.IsLinkLocalUnicast() {
    72  		return true, nil
    73  	}
    74  	if ipa.IsLinkLocalMulticast() {
    75  		return true, nil
    76  	}
    77  	if ipa.IsInterfaceLocalMulticast() {
    78  		return true, nil
    79  	}
    80  	return false, nil
    81  
    82  }
    83  
    84  /*
    85  splits things like so:
    86  
    87  		172.29.1.0/24  => "172.29.1.0" and 24
    88  		172.29.1.5:5000  => "172.29.1.5" and 32
    89  	        2a01:4b00:ab0f:5100:5::5/64 => "2a01:4b00:ab0f:5100:5::5" and 64
    90  */
    91  func GetIPAndNet(ip_s string) (string, int, error) {
    92  	if strings.Contains(ip_s, "/") {
    93  		// with CIDR
    94  		ip, ipnet, err := net.ParseCIDR(ip_s)
    95  		if err != nil {
    96  			return "", 0, err
    97  		}
    98  		ones, _ := ipnet.Mask.Size()
    99  		return ip.String(), ones, nil
   100  	}
   101  	// no mask (but possibly with port)
   102  	ip_ns := ""
   103  	host, _, err := net.SplitHostPort(ip_s)
   104  	if err == nil {
   105  		ip_ns = host
   106  	} else {
   107  		ip_ns = ip_s
   108  	}
   109  	ip, _, t, err := ParseIP(ip_ns)
   110  	if err != nil {
   111  		return "", 0, err
   112  	}
   113  	if t == 4 {
   114  		return ip, 32, nil
   115  	} else if t == 6 {
   116  		return ip, 128, nil
   117  	}
   118  	return "", 0, fmt.Errorf("invalid ip type %d for \"%s\"", t, ip_s)
   119  }
   120  
   121  // return true if this is a IPv6 link local ip
   122  func IsLinkLocal(s string) bool {
   123  	ips, _, version, err := ParseIP(s)
   124  	if err != nil {
   125  		panic(fmt.Sprintf("invalid ip %s", s))
   126  	}
   127  	if version != 6 {
   128  		return false
   129  	}
   130  	if strings.HasPrefix(ips, "fe80:") {
   131  		return true
   132  	}
   133  	return false
   134  }
   135  
   136  // returns true if this is an IPv4 or IPv6 loopback address
   137  func IsLoopback(s string) bool {
   138  	s = strings.ToLower(s)
   139  	if strings.HasPrefix(s, "127.") {
   140  		return true
   141  	}
   142  	if s == "::1" {
   143  		return true
   144  	}
   145  	return false
   146  }
   147  

View as plain text