From 38efe4fc92b688df1d2f6341186810671f38ee07 Mon Sep 17 00:00:00 2001 From: Shawn Carey Date: Thu, 1 Feb 2024 11:40:47 -0500 Subject: [PATCH] don't check interface ips when getting ips from dns pool --- go.mod | 1 + go.sum | 2 + tunnel/entities/service.go | 4 +- tunnel/intercept/hosting.go | 2 +- tunnel/intercept/interceptor.go | 2 +- tunnel/intercept/iputils.go | 83 +++++++++++++++++------------- tunnel/utils/ipcalc.go | 90 ++++++--------------------------- 7 files changed, 71 insertions(+), 113 deletions(-) diff --git a/go.mod b/go.mod index 4291a68c6..abd0d1d20 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/emirpasic/gods v1.18.1 github.com/fatih/color v1.16.0 github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa + github.com/gaissmai/extnetip v0.4.0 github.com/go-acme/lego/v4 v4.14.2 github.com/go-openapi/errors v0.21.0 github.com/go-openapi/loads v0.21.5 diff --git a/go.sum b/go.sum index c0c66fee2..620a08213 100644 --- a/go.sum +++ b/go.sum @@ -192,6 +192,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/gaissmai/extnetip v0.4.0 h1:9pNd/Z6QSlkda35bug/IYuPYaPMTYRuqcxPce5Z9TTQ= +github.com/gaissmai/extnetip v0.4.0/go.mod h1:M3NWlyFKaVosQXWXKKeIPK+5VM4U85DahdIqNYX4TK4= github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= diff --git a/tunnel/entities/service.go b/tunnel/entities/service.go index a7a77e918..fa2a5a7a1 100644 --- a/tunnel/entities/service.go +++ b/tunnel/entities/service.go @@ -164,7 +164,7 @@ func makeAllowedAddress(addr string) (allowedAddress, error) { return &domainAddress{domain: strings.ToLower(addr)}, nil } - if _, cidr, err := utils.GetDialIP(addr); err == nil { + if cidr, err := utils.GetCidr(addr); err == nil { return &cidrAddress{cidr: *cidr}, nil } @@ -291,7 +291,7 @@ func (self *HostV1Config) GetAllowedSourceAddressRoutes() ([]*net.IPNet, error) var routes []*net.IPNet for _, addr := range self.AllowedSourceAddresses { // need to get CIDR from address - iputils.getInterceptIp? - _, ipNet, err := utils.GetDialIP(addr) + ipNet, err := utils.GetCidr(addr) if err != nil { return nil, errors.Errorf("failed to parse allowed source address '%s': %v", addr, err) } diff --git a/tunnel/intercept/hosting.go b/tunnel/intercept/hosting.go index 79da2b60e..5d403f68b 100644 --- a/tunnel/intercept/hosting.go +++ b/tunnel/intercept/hosting.go @@ -193,7 +193,7 @@ func (self *hostingContext) SetCloseCallback(f func()) { func (self *hostingContext) OnClose() { log := pfxlog.Logger().WithField("service", self.service.Name) for _, addr := range self.config.AllowedSourceAddresses { - _, ipNet, err := utils.GetDialIP(addr) + ipNet, err := utils.GetCidr(addr) if err != nil { log.WithError(err).Error("failed to get dial IP") } else if self.addrTracker.RemoveAddress(ipNet.String()) { diff --git a/tunnel/intercept/interceptor.go b/tunnel/intercept/interceptor.go index 1088ea1da..547077d84 100644 --- a/tunnel/intercept/interceptor.go +++ b/tunnel/intercept/interceptor.go @@ -82,7 +82,7 @@ type InterceptAddrCB interface { func GetInterceptAddresses(service *entities.Service, protocols []string, resolver dns.Resolver, addressCB InterceptAddrCB) error { for _, addr := range service.InterceptV1Config.Addresses { - err := getInterceptIP(service, addr, resolver, func(ip net.IP, ipNet *net.IPNet) { + err := getInterceptIP(service, addr, resolver, func(ipNet *net.IPNet) { for _, protocol := range protocols { for _, portRange := range service.InterceptV1Config.PortRanges { addr := &InterceptAddress{ diff --git a/tunnel/intercept/iputils.go b/tunnel/intercept/iputils.go index d0e2f4d77..6a61c7101 100644 --- a/tunnel/intercept/iputils.go +++ b/tunnel/intercept/iputils.go @@ -18,37 +18,35 @@ package intercept import ( "fmt" + "github.com/gaissmai/extnetip" "github.com/michaelquigley/pfxlog" "github.com/openziti/ziti/tunnel/dns" "github.com/openziti/ziti/tunnel/entities" "github.com/openziti/ziti/tunnel/utils" "net" + "net/netip" + "sync" ) -var dnsIpLow, dnsIpHigh net.IP +var dnsPrefix netip.Prefix +var dnsCurrentIp netip.Addr +var dnsCurrentIpMtx sync.Mutex func SetDnsInterceptIpRange(cidr string) error { - ip, ipnet, err := net.ParseCIDR(cidr) + prefix, err := netip.ParsePrefix(cidr) if err != nil { return fmt.Errorf("invalid cidr %s: %v", cidr, err) } - var ips []net.IP - for ip = ip.Mask(ipnet.Mask); ipnet.Contains(ip); utils.IncIP(ip) { - a := make(net.IP, len(ip)) - copy(a, ip) - ips = append(ips, a) - } - - // remove network address and broadcast address - dnsIpLow = ips[1] - dnsIpHigh = ips[len(ips)-2] - - if len(dnsIpLow) != len(dnsIpHigh) { - return fmt.Errorf("lower dns IP length %d differs from upper dns IP length %d", len(dnsIpLow), len(dnsIpHigh)) - } + dnsPrefix = prefix + // get last ip in range for logging + _, dnsIpHigh := extnetip.Range(dnsPrefix) - pfxlog.Logger().Infof("dns intercept IP range: %s - %s", dnsIpLow.String(), dnsIpHigh.String()) + // skip network address + dnsCurrentIpMtx.Lock() + dnsCurrentIp = dnsPrefix.Addr().Next() + dnsCurrentIpMtx.Unlock() + pfxlog.Logger().Infof("dns intercept IP range: %v - %v", dnsCurrentIp, dnsIpHigh) return nil } @@ -61,41 +59,56 @@ func cleanUpFunc(hostname string, resolver dns.Resolver) func() { return f } -func getInterceptIP(svc *entities.Service, hostname string, resolver dns.Resolver, addrCB func(net.IP, *net.IPNet)) error { +func incDnsIp() (err error) { + dnsCurrentIpMtx.Lock() + defer dnsCurrentIpMtx.Unlock() + ip := dnsCurrentIp.Next() + if ip.IsValid() && dnsPrefix.Contains(ip) { + dnsCurrentIp = ip + } else { + err = fmt.Errorf("cannot allocate ip addrress: ip range exhausted") + } + return +} + +func getDnsIp(host string, addrCB func(*net.IPNet), svc *entities.Service, resolver dns.Resolver) (net.IP, error) { + err := incDnsIp() + if err == nil { + addrCB(&net.IPNet{ + IP: dnsCurrentIp.AsSlice(), + Mask: net.CIDRMask(dnsCurrentIp.BitLen(), dnsCurrentIp.BitLen()), + }) + svc.AddCleanupAction(cleanUpFunc(host, resolver)) + } + return dnsCurrentIp.AsSlice(), err +} + +func getInterceptIP(svc *entities.Service, hostname string, resolver dns.Resolver, addrCB func(ipNet *net.IPNet)) error { logger := pfxlog.Logger() + // handle wildcard domain - IPs will be allocated when matching hostnames are queried if hostname[0] == '*' { err := resolver.AddDomain(hostname, func(host string) (net.IP, error) { - var ip net.IP - var err error - ip, err = utils.NextIP(dnsIpLow, dnsIpHigh) - - if err == nil { - addrCB(ip, utils.Ip2IPnet(ip)) - svc.AddCleanupAction(cleanUpFunc(host, resolver)) - } - return ip, err + return getDnsIp(hostname, addrCB, svc, resolver) }) return err } - ip, ipNet, err := utils.GetDialIP(hostname) + // handle IP or CIDR + ipNet, err := utils.GetCidr(hostname) if err == nil { - addrCB(ip, ipNet) + addrCB(ipNet) return err } - ip, _ = utils.NextIP(dnsIpLow, dnsIpHigh) - if ip == nil { + // handle hostnames + ip, err := getDnsIp(hostname, addrCB, svc, resolver) + if err != nil { return fmt.Errorf("invalid IP address or unresolvable hostname: %s", hostname) } if err = resolver.AddHostname(hostname, ip); err != nil { logger.WithError(err).Errorf("failed to add host/ip mapping to resolver: %v -> %v", hostname, ip) } - svc.AddCleanupAction(cleanUpFunc(hostname, resolver)) - - ipNet = utils.Ip2IPnet(ip) - addrCB(ip, ipNet) return nil } diff --git a/tunnel/utils/ipcalc.go b/tunnel/utils/ipcalc.go index c501ec2d5..46de704e1 100644 --- a/tunnel/utils/ipcalc.go +++ b/tunnel/utils/ipcalc.go @@ -17,85 +17,27 @@ package utils import ( - "bytes" - "github.com/michaelquigley/pfxlog" - "github.com/pkg/errors" + "fmt" "net" + "net/netip" ) -func IsLocallyAssigned(addr, lower, upper net.IP) bool { - return bytes.Compare(addr.To16(), lower.To16()) >= 0 && bytes.Compare(addr.To16(), upper.To16()) <= 0 -} - -// return the next available IP address in the range of provided IPs -func NextIP(lower, upper net.IP) (net.IP, error) { - usedAddrs, err := AllInterfaceAddrs() - if err != nil { - return nil, err - } - - // need to make a copy of lower net.IP, since they're just byte arrays. Otherwise - // we're continually changing the lower ip globally - ip := net.IP(make([]byte, len(lower))) - copy(ip, lower) - - for ; !ip.Equal(upper); IncIP(ip) { - inUse := false - for _, usedAddr := range usedAddrs { - usedIP, _, _ := net.ParseCIDR(usedAddr.String()) - if ip.Equal(usedIP) { - inUse = true - break - } - } - if !inUse { - return ip, nil - } - } - - return nil, nil -} - -func IncIP(ip net.IP) { - for i := len(ip) - 1; i >= 0; i-- { - ip[i]++ - if ip[i] > 0 { - break - } - } -} - -// Return the length of a full prefix (no subnetting) for the given IP address. -// Returns 32 for ipv4 addresses, and 128 for ipv6 addresses. -func AddrBits(ip net.IP) int { - if ip == nil { - return 0 - } else if ip.To4() != nil { - return net.IPv4len * 8 - } else if ip.To16() != nil { - return net.IPv6len * 8 - } - - pfxlog.Logger().Infof("invalid IP address %s", ip.String()) - return 0 -} - -func Ip2IPnet(ip net.IP) *net.IPNet { - prefixLen := AddrBits(ip) - ipNet := &net.IPNet{IP: ip, Mask: net.CIDRMask(prefixLen, prefixLen)} - return ipNet -} - -func GetDialIP(addr string) (net.IP, *net.IPNet, error) { - // hostname is an ip address, return it - if parsedIP := net.ParseIP(addr); parsedIP != nil { - ipNet := Ip2IPnet(parsedIP) - return parsedIP, ipNet, nil +func GetCidr(ipOrCidr string) (*net.IPNet, error) { + ip, err := netip.ParseAddr(ipOrCidr) + if err == nil { + return &net.IPNet{ + IP: ip.AsSlice(), + Mask: net.CIDRMask(ip.BitLen(), ip.BitLen()), + }, nil } - if parsedIP, cidr, err := net.ParseCIDR(addr); err == nil { - return parsedIP, cidr, nil + pfx, err := netip.ParsePrefix(ipOrCidr) + if err == nil { + return &net.IPNet{ + IP: pfx.Addr().AsSlice(), + Mask: net.CIDRMask(pfx.Bits(), pfx.Addr().BitLen()), + }, nil } - return nil, nil, errors.Errorf("could not parse '%s' as IP or CIDR", addr) + return nil, fmt.Errorf("failed to parse '%v' as IP or CIDR", ipOrCidr) }