|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/urfave/cli"
|
|
|
|
apinet "k8s.io/apimachinery/pkg/util/net"
|
|
|
|
)
|
|
|
|
|
|
|
|
// JoinIPs stringifies and joins a list of IP addresses with commas.
|
|
|
|
func JoinIPs(elems []net.IP) string {
|
|
|
|
var strs []string
|
|
|
|
for _, elem := range elems {
|
|
|
|
strs = append(strs, elem.String())
|
|
|
|
}
|
|
|
|
return strings.Join(strs, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
// JoinIPNets stringifies and joins a list of IP networks with commas.
|
|
|
|
func JoinIPNets(elems []*net.IPNet) string {
|
|
|
|
var strs []string
|
|
|
|
for _, elem := range elems {
|
|
|
|
strs = append(strs, elem.String())
|
|
|
|
}
|
|
|
|
return strings.Join(strs, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirst4Net returns the first IPv4 network from the list of IP networks.
|
|
|
|
// If no IPv4 addresses are found, an error is raised.
|
|
|
|
func GetFirst4Net(elems []*net.IPNet) (*net.IPNet, error) {
|
|
|
|
for _, elem := range elems {
|
|
|
|
if elem == nil || elem.IP.To4() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return elem, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("no IPv4 CIDRs found")
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirst4 returns the first IPv4 address from the list of IP addresses.
|
|
|
|
// If no IPv4 addresses are found, an error is raised.
|
|
|
|
func GetFirst4(elems []net.IP) (net.IP, error) {
|
|
|
|
for _, elem := range elems {
|
|
|
|
if elem == nil || elem.To4() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return elem, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("no IPv4 address found")
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirst4String returns the first IPv4 address from a list of IP address strings.
|
|
|
|
// If no IPv4 addresses are found, an error is raised.
|
|
|
|
func GetFirst4String(elems []string) (string, error) {
|
|
|
|
ips := []net.IP{}
|
|
|
|
for _, elem := range elems {
|
|
|
|
for _, v := range strings.Split(elem, ",") {
|
|
|
|
ips = append(ips, net.ParseIP(v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip, err := GetFirst4(ips)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return ip.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// JoinIP4Nets stringifies and joins a list of IPv4 networks with commas.
|
|
|
|
func JoinIP4Nets(elems []*net.IPNet) string {
|
|
|
|
var strs []string
|
|
|
|
for _, elem := range elems {
|
|
|
|
if elem != nil && elem.IP.To4() != nil {
|
|
|
|
strs = append(strs, elem.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return strings.Join(strs, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirst6 returns the first IPv6 address from the list of IP addresses.
|
|
|
|
// If no IPv6 addresses are found, an error is raised.
|
|
|
|
func GetFirst6(elems []net.IP) (net.IP, error) {
|
|
|
|
for _, elem := range elems {
|
|
|
|
if elem == nil || elem.To16() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return elem, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("no IPv6 address found")
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirst6Net returns the first IPv4 network from the list of IP networks.
|
|
|
|
// If no IPv6 addresses are found, an error is raised.
|
|
|
|
func GetFirst6Net(elems []*net.IPNet) (*net.IPNet, error) {
|
|
|
|
for _, elem := range elems {
|
|
|
|
if elem == nil || elem.IP.To16() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return elem, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("no IPv6 CIDRs found")
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirst6String returns the first IPv6 address from a list of IP address strings.
|
|
|
|
// If no IPv6 addresses are found, an error is raised.
|
|
|
|
func GetFirst6String(elems []string) (string, error) {
|
|
|
|
ips := []net.IP{}
|
|
|
|
for _, elem := range elems {
|
|
|
|
for _, v := range strings.Split(elem, ",") {
|
|
|
|
ips = append(ips, net.ParseIP(v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip, err := GetFirst6(ips)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return ip.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// JoinIP6Nets stringifies and joins a list of IPv6 networks with commas.
|
|
|
|
func JoinIP6Nets(elems []*net.IPNet) string {
|
|
|
|
var strs []string
|
|
|
|
for _, elem := range elems {
|
|
|
|
if elem != nil && elem.IP.To4() == nil {
|
|
|
|
strs = append(strs, elem.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return strings.Join(strs, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetHostnameAndIPs takes a node name and list of IPs, usually from CLI args.
|
|
|
|
// If set, these are used to return the node's name and addresses. If not set,
|
|
|
|
// the system hostname and primary interface address are returned instead.
|
|
|
|
func GetHostnameAndIPs(name string, nodeIPs cli.StringSlice) (string, []net.IP, error) {
|
|
|
|
ips := []net.IP{}
|
|
|
|
if len(nodeIPs) == 0 {
|
|
|
|
hostIP, err := apinet.ChooseHostInterface()
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
ips = append(ips, hostIP)
|
|
|
|
// If IPv6 it's an IPv6 only node
|
|
|
|
if hostIP.To4() != nil {
|
|
|
|
hostIPv6, err := apinet.ResolveBindAddress(net.IPv6loopback)
|
|
|
|
if err == nil && !hostIPv6.Equal(hostIP) {
|
|
|
|
ips = append(ips, hostIPv6)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var err error
|
|
|
|
ips, err = ParseStringSliceToIPs(nodeIPs)
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, fmt.Errorf("invalid node-ip: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if name == "" {
|
|
|
|
hostname, err := os.Hostname()
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
name = hostname
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use lower case hostname to comply with kubernetes constraint:
|
|
|
|
// https://github.com/kubernetes/kubernetes/issues/71140
|
|
|
|
name = strings.ToLower(name)
|
|
|
|
|
|
|
|
return name, ips, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseStringSliceToIPs converts slice of strings that in turn can be lists of comma separated unparsed IP addresses
|
|
|
|
// into a single slice of net.IP, it returns error if at any point parsing failed
|
|
|
|
func ParseStringSliceToIPs(s cli.StringSlice) ([]net.IP, error) {
|
|
|
|
var ips []net.IP
|
|
|
|
for _, unparsedIP := range s {
|
|
|
|
for _, v := range strings.Split(unparsedIP, ",") {
|
|
|
|
ip := net.ParseIP(v)
|
|
|
|
if ip == nil {
|
|
|
|
return nil, fmt.Errorf("invalid ip format '%s'", v)
|
|
|
|
}
|
|
|
|
ips = append(ips, ip)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ips, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirstIP returns the first IPv4 address from the list of IP addresses.
|
|
|
|
// If no IPv4 addresses are found, returns the first IPv6 address
|
|
|
|
// if neither of IPv4 or IPv6 are found an error is raised.
|
|
|
|
// Additionally matching listen address and IP version is returned.
|
|
|
|
func GetFirstIP(nodeIPs []net.IP) (net.IP, string, bool, error) {
|
|
|
|
nodeIP, err := GetFirst4(nodeIPs)
|
|
|
|
ListenAddress := "0.0.0.0"
|
|
|
|
IPv6only := false
|
|
|
|
if err != nil {
|
|
|
|
nodeIP, err = GetFirst6(nodeIPs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", false, err
|
|
|
|
}
|
|
|
|
ListenAddress = "::"
|
|
|
|
IPv6only = true
|
|
|
|
}
|
|
|
|
return nodeIP, ListenAddress, IPv6only, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirstNet returns the first IPv4 network from the list of IP networks.
|
|
|
|
// If no IPv4 addresses are found, returns the first IPv6 address
|
|
|
|
// if neither of IPv4 or IPv6 are found an error is raised.
|
|
|
|
func GetFirstNet(elems []*net.IPNet) (*net.IPNet, error) {
|
|
|
|
serviceIPRange, err := GetFirst4Net(elems)
|
|
|
|
if err != nil {
|
|
|
|
serviceIPRange, err = GetFirst6Net(elems)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return serviceIPRange, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFirstString returns the first IP4 address from a list of IP address strings.
|
|
|
|
// If no IPv4 addresses are found, returns the first IPv6 address
|
|
|
|
// if neither of IPv4 or IPv6 are found an error is raised.
|
|
|
|
func GetFirstString(elems []string) (string, bool, error) {
|
|
|
|
ip, err := GetFirst4String(elems)
|
|
|
|
IPv6only := false
|
|
|
|
if err != nil {
|
|
|
|
ip, err = GetFirst6String(elems)
|
|
|
|
if err != nil {
|
|
|
|
return "", false, err
|
|
|
|
}
|
|
|
|
IPv6only = true
|
|
|
|
}
|
|
|
|
return ip, IPv6only, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsIPv6OnlyCIDRs returns if
|
|
|
|
// - all are valid cidrs
|
|
|
|
// - at least one cidr from v6 family is found
|
|
|
|
// - v4 family cidr is not found
|
|
|
|
func IsIPv6OnlyCIDRs(cidrs []*net.IPNet) (bool, error) {
|
|
|
|
v4Found := false
|
|
|
|
v6Found := false
|
|
|
|
for _, cidr := range cidrs {
|
|
|
|
if cidr == nil {
|
|
|
|
return false, fmt.Errorf("cidr %v is invalid", cidr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v4Found && v6Found {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if cidr.IP != nil && cidr.IP.To4() == nil {
|
|
|
|
v6Found = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
v4Found = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return !v4Found && v6Found, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IPToIPNet converts an IP to an IPNet, using a fully filled mask appropriate for the address family.
|
|
|
|
func IPToIPNet(ip net.IP) (*net.IPNet, error) {
|
|
|
|
address := ip.String()
|
|
|
|
if strings.Contains(address, ":") {
|
|
|
|
address += "/128"
|
|
|
|
} else {
|
|
|
|
address += "/32"
|
|
|
|
}
|
|
|
|
_, cidr, err := net.ParseCIDR(address)
|
|
|
|
return cidr, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// IPStringToIPNet converts an IP string to an IPNet, using a fully filled mask appropriate for the address family.
|
|
|
|
func IPStringToIPNet(address string) (*net.IPNet, error) {
|
|
|
|
if strings.Contains(address, ":") {
|
|
|
|
address += "/128"
|
|
|
|
} else {
|
|
|
|
address += "/32"
|
|
|
|
}
|
|
|
|
_, cidr, err := net.ParseCIDR(address)
|
|
|
|
return cidr, err
|
|
|
|
}
|