From c4011be41d431b5901d07b33710141f54963a87f Mon Sep 17 00:00:00 2001 From: ziti-ci Date: Thu, 16 Jul 2020 21:01:10 -0400 Subject: [PATCH] first pass at restructuring service to one ip per hostname and impl reference counting. still some bugs in it though particularly around toggling identities on/off --- service/cziti/dns.go | 98 +++++++++++++++++++++--------- service/cziti/sdk.go | 22 ++++--- service/ziti-tunnel/service/ipc.go | 1 + 3 files changed, 84 insertions(+), 37 deletions(-) diff --git a/service/cziti/dns.go b/service/cziti/dns.go index d25c3b2b..0eca8c90 100644 --- a/service/cziti/dns.go +++ b/service/cziti/dns.go @@ -17,9 +17,9 @@ package cziti +import "C" import ( "encoding/binary" - "errors" "fmt" "net" "sync" @@ -47,67 +47,108 @@ type dnsImpl struct { serviceMap map[string]ctxService // dnsName -> ip address - hostnameMap map[string]net.IP + hostnameMap map[string]ctxIp // ipv4 -> dnsName - ipMap map[uint32]string + //ipMap map[uint32]string +} + +type ctxIp struct { + ctx *CZitiCtx + ip net.IP + network string } type ctxService struct { - ctx *CZitiCtx - name string + ctx *CZitiCtx + name string + serviceId string + count int } // RegisterService will return the next ip address in the configured range. If the ip address is not // assigned to a hostname an error will also be returned indicating why. -func (dns *dnsImpl) RegisterService(dnsNameToReg string, port uint16, ctx *CZitiCtx, name string) (net.IP, error) { +func (dns *dnsImpl) RegisterService(svcId string, dnsNameToReg string, port uint16, ctx *CZitiCtx, name string) (net.IP, error) { log.Infof("adding DNS entry for service name %s@%s:%d", name, dnsNameToReg, port) DnsInit(defaultCidr, defaultMaskBits) dnsName := dnsNameToReg + "." key := fmt.Sprint(dnsName, ':', port) - nextAddr := dns.cidr | atomic.AddUint32(&dns.counter, 1) - nextIp := make(net.IP, 4) - binary.BigEndian.PutUint32(nextIp, nextAddr) + var ip net.IP - if cs, found := dns.serviceMap[key]; found { - if cs.ctx != ctx || cs.name != name { - return nextIp, fmt.Errorf( - "service mapping conflict: %s:%d is already mapped for another context", dnsNameToReg, port) + currentNetwork := C.GoString(ctx.options.controller) + + // check to see if the hostname is mapped... + if foundIp, found := dns.hostnameMap[dnsName]; found { + ip = foundIp.ip + // now check to see if the host *and* port are mapped... + if foundContext, found := dns.serviceMap[key]; found { + if foundIp.network != currentNetwork { + // means the host:port are mapped to some other *identity* already. that's an invalid state + return ip, fmt.Errorf("service mapping conflict: %s:%d in %s is already mapped for another identity in %s", dnsNameToReg, port, currentNetwork, foundIp.network) + } + if foundContext.serviceId != svcId { + // means the host:port are mapped to some other service already. that's an invalid state + return ip, fmt.Errorf("service mapping conflict: %s:%d is already mapped for another service", dnsNameToReg, port) + } + // while the host *AND* port are not used - the hostname is. + // need to increment the refcounter of how many service use this hostname + foundContext.count ++ + log.Debugf("DNS mapping for %s used by another service. total services using %s = %d", dnsNameToReg, dnsNameToReg, foundContext.count) + } else { + // good - means the service can be mapped + dns.serviceMap[key] = ctxService{ + ctx: ctx, + name: name, + serviceId: svcId, + count: 1, + } } - return nextIp, nil } else { + // if not used at all - map it + nextAddr := dns.cidr | atomic.AddUint32(&dns.counter, 1) + ip = make(net.IP, 4) + binary.BigEndian.PutUint32(ip, nextAddr) + + log.Infof("mapping hostname %s to ip %s", dnsNameToReg, ip.String()) + dns.hostnameMap[dnsName] = ctxIp { + ip: ip, + ctx: ctx, + network: currentNetwork, + } dns.serviceMap[key] = ctxService{ ctx: ctx, name: name, + serviceId: svcId, + count: 1, } - - if ip, mapped := dns.hostnameMap[dnsName]; mapped { - return ip, nil - } - - dns.hostnameMap[dnsName] = nextIp - dns.ipMap[nextAddr] = dnsName } - return nextIp, nil + return ip, nil } func (dns *dnsImpl) Resolve(dnsName string) net.IP { - return dns.hostnameMap[dnsName] + return dns.hostnameMap[dnsName].ip } func (dns *dnsImpl) DeregisterService(ctx *CZitiCtx, name string) { for k, sc := range dns.serviceMap { if sc.ctx == ctx && sc.name == name { - log.Infof("removing %s from DNS mapping", name) - delete(dns.serviceMap, k) + if sc.count < 1 { + log.Infof("removing %s from DNS mapping", name) + delete(dns.serviceMap, k) + } else { + // another service is using the mapping - can't remove it yet so decrement + sc.count -- + log.Debugf("cannot remove dns mapping for %s yet - %d other services still use this hostname", name, sc.count) + } return } } } func (this *dnsImpl) GetService(ip net.IP, port uint16) (*CZitiCtx, string, error) { - ipv4 := binary.BigEndian.Uint32(ip) + return nil, "", nil //not used yet + /*ipv4 := binary.BigEndian.Uint32(ip) dns, found := this.ipMap[ipv4] if !found { return nil, "", errors.New("service not available") @@ -119,13 +160,14 @@ func (this *dnsImpl) GetService(ip net.IP, port uint16) (*CZitiCtx, string, erro return nil, "", errors.New("service not available") } return sc.ctx, sc.name, nil + */ } func DnsInit(ip string, maskBits int) { initOnce.Do(func() { DNS.serviceMap = make(map[string]ctxService) - DNS.ipMap = make(map[uint32]string) - DNS.hostnameMap = make(map[string]net.IP) + //DNS.ipMap = make(map[uint32]string) + DNS.hostnameMap = make(map[string]ctxIp) i := net.ParseIP(ip).To4() mask := net.CIDRMask(maskBits, 32) DNS.cidr = binary.BigEndian.Uint32(i) & binary.BigEndian.Uint32(mask) diff --git a/service/cziti/sdk.go b/service/cziti/sdk.go index c2e9eb6a..83e1f822 100644 --- a/service/cziti/sdk.go +++ b/service/cziti/sdk.go @@ -100,6 +100,7 @@ type Service struct { InterceptHost string InterceptPort uint16 AssignedIP string + OwnsIntercept bool } type CZitiCtx struct { @@ -135,7 +136,7 @@ func (c *CZitiCtx) Controller() string { var tunCfgName = C.CString("ziti-tunneler-client.v1") //export serviceCB -func serviceCB(nf C.ziti_context, service *C.ziti_service, status C.int, data unsafe.Pointer) { +func serviceCB(_ C.ziti_context, service *C.ziti_service, status C.int, data unsafe.Pointer) { ctx := (*CZitiCtx)(data) if ctx.Services == nil { @@ -144,14 +145,14 @@ func serviceCB(nf C.ziti_context, service *C.ziti_service, status C.int, data un } name := C.GoString(service.name) - id := C.GoString(service.id) - log.Debugf("============ INSIDE serviceCB - status: %s:%s - %v, %v, %v ============", name, id, status, C.ZITI_SERVICE_UNAVAILABLE, C.ZITI_OK) + svcId := C.GoString(service.id) + log.Debugf("============ INSIDE serviceCB - status: %s:%s - %v, %v, %v ============", name, svcId, status, C.ZITI_SERVICE_UNAVAILABLE, C.ZITI_OK) if status == C.ZITI_SERVICE_UNAVAILABLE { - found, ok := ctx.Services.Load(id) + found, ok := ctx.Services.Load(svcId) fs := found.(Service) if ok { DNS.DeregisterService(ctx, name) - ctx.Services.Delete(id) + ctx.Services.Delete(svcId) ServiceChanges <- ServiceChange{ Operation: REMOVED, Service: &fs, @@ -174,23 +175,26 @@ func serviceCB(nf C.ziti_context, service *C.ziti_service, status C.int, data un } } if host != "" && port != -1 { - ip, err := DNS.RegisterService(host, uint16(port), ctx, name) + ownsIntercept := true + ip, err := DNS.RegisterService(svcId, host, uint16(port), ctx, name) if err != nil { log.Warn(err) + ownsIntercept = false } else { log.Infof("service intercept beginning for service: %s@%s:%d on ip %s", name, host, port, ip.String()) for _, t := range devMap { - t.AddIntercept(id, name, ip.String(), port, unsafe.Pointer(ctx.zctx)) + t.AddIntercept(svcId, name, ip.String(), port, unsafe.Pointer(ctx.zctx)) } } added := Service{ Name: name, - Id: id, + Id: svcId, InterceptHost: host, InterceptPort: uint16(port), AssignedIP: ip.String(), + OwnsIntercept: ownsIntercept, } - ctx.Services.Store(id, added) + ctx.Services.Store(svcId, added) ServiceChanges <- ServiceChange{ Operation: ADDED, Service: &added, diff --git a/service/ziti-tunnel/service/ipc.go b/service/ziti-tunnel/service/ipc.go index 9da12139..c55c5eff 100644 --- a/service/ziti-tunnel/service/ipc.go +++ b/service/ziti-tunnel/service/ipc.go @@ -829,6 +829,7 @@ func acceptServices() { InterceptPort: c.Service.InterceptPort, Id: c.Service.Id, AssignedIP: c.Service.AssignedIP, + OwnsIntercept: c.Service.OwnsIntercept, } id.Services = append(id.Services, &svc)