 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			405 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package cidranger
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"strings"
 | |
| 
 | |
| 	rnet "github.com/libp2p/go-cidranger/net"
 | |
| )
 | |
| 
 | |
| // prefixTrie is a path-compressed (PC) trie implementation of the
 | |
| // ranger interface inspired by this blog post:
 | |
| // https://vincent.bernat.im/en/blog/2017-ipv4-route-lookup-linux
 | |
| //
 | |
| // CIDR blocks are stored using a prefix tree structure where each node has its
 | |
| // parent as prefix, and the path from the root node represents current CIDR
 | |
| // block.
 | |
| //
 | |
| // For IPv4, the trie structure guarantees max depth of 32 as IPv4 addresses are
 | |
| // 32 bits long and each bit represents a prefix tree starting at that bit. This
 | |
| // property also guarantees constant lookup time in Big-O notation.
 | |
| //
 | |
| // Path compression compresses a string of node with only 1 child into a single
 | |
| // node, decrease the amount of lookups necessary during containment tests.
 | |
| //
 | |
| // Level compression dictates the amount of direct children of a node by
 | |
| // allowing it to handle multiple bits in the path.  The heuristic (based on
 | |
| // children population) to decide when the compression and decompression happens
 | |
| // is outlined in the prior linked blog, and will be experimented with in more
 | |
| // depth in this project in the future.
 | |
| //
 | |
| // Note: Can not insert both IPv4 and IPv6 network addresses into the same
 | |
| // prefix trie, use versionedRanger wrapper instead.
 | |
| //
 | |
| // TODO: Implement level-compressed component of the LPC trie.
 | |
| type prefixTrie struct {
 | |
| 	parent   *prefixTrie
 | |
| 	children [2]*prefixTrie
 | |
| 
 | |
| 	numBitsSkipped uint
 | |
| 	numBitsHandled uint
 | |
| 
 | |
| 	network rnet.Network
 | |
| 	entry   RangerEntry
 | |
| 
 | |
| 	size int // This is only maintained in the root trie.
 | |
| }
 | |
| 
 | |
| var ip4ZeroCIDR, ip6ZeroCIDR net.IPNet
 | |
| 
 | |
| func init() {
 | |
| 	_, v4, _ := net.ParseCIDR("0.0.0.0/0")
 | |
| 	_, v6, _ := net.ParseCIDR("0::0/0")
 | |
| 	ip4ZeroCIDR = *v4
 | |
| 	ip6ZeroCIDR = *v6
 | |
| }
 | |
| 
 | |
| func newRanger(version rnet.IPVersion) Ranger {
 | |
| 	return newPrefixTree(version)
 | |
| }
 | |
| 
 | |
| // newPrefixTree creates a new prefixTrie.
 | |
| func newPrefixTree(version rnet.IPVersion) *prefixTrie {
 | |
| 	rootNet := ip4ZeroCIDR
 | |
| 	if version == rnet.IPv6 {
 | |
| 		rootNet = ip6ZeroCIDR
 | |
| 	}
 | |
| 	return &prefixTrie{
 | |
| 		numBitsSkipped: 0,
 | |
| 		numBitsHandled: 1,
 | |
| 		network:        rnet.NewNetwork(rootNet),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newPathprefixTrie(network rnet.Network, numBitsSkipped uint) *prefixTrie {
 | |
| 	version := rnet.IPv4
 | |
| 	if len(network.Number) == rnet.IPv6Uint32Count {
 | |
| 		version = rnet.IPv6
 | |
| 	}
 | |
| 	path := newPrefixTree(version)
 | |
| 	path.numBitsSkipped = numBitsSkipped
 | |
| 	path.network = network.Masked(int(numBitsSkipped))
 | |
| 	return path
 | |
| }
 | |
| 
 | |
| func newEntryTrie(network rnet.Network, entry RangerEntry) *prefixTrie {
 | |
| 	leaf := newPathprefixTrie(network, uint(network.Mask))
 | |
| 	leaf.entry = entry
 | |
| 	return leaf
 | |
| }
 | |
| 
 | |
| // Insert inserts a RangerEntry into prefix trie.
 | |
| func (p *prefixTrie) Insert(entry RangerEntry) error {
 | |
| 	network := entry.Network()
 | |
| 	sizeIncreased, err := p.insert(rnet.NewNetwork(network), entry)
 | |
| 	if sizeIncreased {
 | |
| 		p.size++
 | |
| 	}
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Remove removes RangerEntry identified by given network from trie.
 | |
| func (p *prefixTrie) Remove(network net.IPNet) (RangerEntry, error) {
 | |
| 	entry, err := p.remove(rnet.NewNetwork(network))
 | |
| 	if entry != nil {
 | |
| 		p.size--
 | |
| 	}
 | |
| 	return entry, err
 | |
| }
 | |
| 
 | |
| // Contains returns boolean indicating whether given ip is contained in any
 | |
| // of the inserted networks.
 | |
| func (p *prefixTrie) Contains(ip net.IP) (bool, error) {
 | |
| 	nn := rnet.NewNetworkNumber(ip)
 | |
| 	if nn == nil {
 | |
| 		return false, ErrInvalidNetworkNumberInput
 | |
| 	}
 | |
| 	return p.contains(nn)
 | |
| }
 | |
| 
 | |
| // ContainingNetworks returns the list of RangerEntry(s) the given ip is
 | |
| // contained in in ascending prefix order.
 | |
| func (p *prefixTrie) ContainingNetworks(ip net.IP) ([]RangerEntry, error) {
 | |
| 	nn := rnet.NewNetworkNumber(ip)
 | |
| 	if nn == nil {
 | |
| 		return nil, ErrInvalidNetworkNumberInput
 | |
| 	}
 | |
| 	return p.containingNetworks(nn)
 | |
| }
 | |
| 
 | |
| // CoveredNetworks returns the list of RangerEntry(s) the given ipnet
 | |
| // covers.  That is, the networks that are completely subsumed by the
 | |
| // specified network.
 | |
| func (p *prefixTrie) CoveredNetworks(network net.IPNet) ([]RangerEntry, error) {
 | |
| 	net := rnet.NewNetwork(network)
 | |
| 	return p.coveredNetworks(net)
 | |
| }
 | |
| 
 | |
| // Len returns number of networks in ranger.
 | |
| func (p *prefixTrie) Len() int {
 | |
| 	return p.size
 | |
| }
 | |
| 
 | |
| // String returns string representation of trie, mainly for visualization and
 | |
| // debugging.
 | |
| func (p *prefixTrie) String() string {
 | |
| 	children := []string{}
 | |
| 	padding := strings.Repeat("| ", p.level()+1)
 | |
| 	for bits, child := range p.children {
 | |
| 		if child == nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		childStr := fmt.Sprintf("\n%s%d--> %s", padding, bits, child.String())
 | |
| 		children = append(children, childStr)
 | |
| 	}
 | |
| 	return fmt.Sprintf("%s (target_pos:%d:has_entry:%t)%s", p.network,
 | |
| 		p.targetBitPosition(), p.hasEntry(), strings.Join(children, ""))
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) contains(number rnet.NetworkNumber) (bool, error) {
 | |
| 	if !p.network.Contains(number) {
 | |
| 		return false, nil
 | |
| 	}
 | |
| 	if p.hasEntry() {
 | |
| 		return true, nil
 | |
| 	}
 | |
| 	if p.targetBitPosition() < 0 {
 | |
| 		return false, nil
 | |
| 	}
 | |
| 	bit, err := p.targetBitFromIP(number)
 | |
| 	if err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 	child := p.children[bit]
 | |
| 	if child != nil {
 | |
| 		return child.contains(number)
 | |
| 	}
 | |
| 	return false, nil
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) containingNetworks(number rnet.NetworkNumber) ([]RangerEntry, error) {
 | |
| 	results := []RangerEntry{}
 | |
| 	if !p.network.Contains(number) {
 | |
| 		return results, nil
 | |
| 	}
 | |
| 	if p.hasEntry() {
 | |
| 		results = []RangerEntry{p.entry}
 | |
| 	}
 | |
| 	if p.targetBitPosition() < 0 {
 | |
| 		return results, nil
 | |
| 	}
 | |
| 	bit, err := p.targetBitFromIP(number)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	child := p.children[bit]
 | |
| 	if child != nil {
 | |
| 		ranges, err := child.containingNetworks(number)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if len(ranges) > 0 {
 | |
| 			if len(results) > 0 {
 | |
| 				results = append(results, ranges...)
 | |
| 			} else {
 | |
| 				results = ranges
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return results, nil
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) coveredNetworks(network rnet.Network) ([]RangerEntry, error) {
 | |
| 	var results []RangerEntry
 | |
| 	if network.Covers(p.network) {
 | |
| 		for entry := range p.walkDepth() {
 | |
| 			results = append(results, entry)
 | |
| 		}
 | |
| 	} else if p.targetBitPosition() >= 0 {
 | |
| 		bit, err := p.targetBitFromIP(network.Number)
 | |
| 		if err != nil {
 | |
| 			return results, err
 | |
| 		}
 | |
| 		child := p.children[bit]
 | |
| 		if child != nil {
 | |
| 			return child.coveredNetworks(network)
 | |
| 		}
 | |
| 	}
 | |
| 	return results, nil
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) insert(network rnet.Network, entry RangerEntry) (bool, error) {
 | |
| 	if p.network.Equal(network) {
 | |
| 		sizeIncreased := p.entry == nil
 | |
| 		p.entry = entry
 | |
| 		return sizeIncreased, nil
 | |
| 	}
 | |
| 
 | |
| 	bit, err := p.targetBitFromIP(network.Number)
 | |
| 	if err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 	existingChild := p.children[bit]
 | |
| 
 | |
| 	// No existing child, insert new leaf trie.
 | |
| 	if existingChild == nil {
 | |
| 		p.appendTrie(bit, newEntryTrie(network, entry))
 | |
| 		return true, nil
 | |
| 	}
 | |
| 
 | |
| 	// Check whether it is necessary to insert additional path prefix between current trie and existing child,
 | |
| 	// in the case that inserted network diverges on its path to existing child.
 | |
| 	lcb, err := network.LeastCommonBitPosition(existingChild.network)
 | |
| 	divergingBitPos := int(lcb) - 1
 | |
| 	if divergingBitPos > existingChild.targetBitPosition() {
 | |
| 		pathPrefix := newPathprefixTrie(network, p.totalNumberOfBits()-lcb)
 | |
| 		err := p.insertPrefix(bit, pathPrefix, existingChild)
 | |
| 		if err != nil {
 | |
| 			return false, err
 | |
| 		}
 | |
| 		// Update new child
 | |
| 		existingChild = pathPrefix
 | |
| 	}
 | |
| 	return existingChild.insert(network, entry)
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) appendTrie(bit uint32, prefix *prefixTrie) {
 | |
| 	p.children[bit] = prefix
 | |
| 	prefix.parent = p
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) insertPrefix(bit uint32, pathPrefix, child *prefixTrie) error {
 | |
| 	// Set parent/child relationship between current trie and inserted pathPrefix
 | |
| 	p.children[bit] = pathPrefix
 | |
| 	pathPrefix.parent = p
 | |
| 
 | |
| 	// Set parent/child relationship between inserted pathPrefix and original child
 | |
| 	pathPrefixBit, err := pathPrefix.targetBitFromIP(child.network.Number)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	pathPrefix.children[pathPrefixBit] = child
 | |
| 	child.parent = pathPrefix
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) remove(network rnet.Network) (RangerEntry, error) {
 | |
| 	if p.hasEntry() && p.network.Equal(network) {
 | |
| 		entry := p.entry
 | |
| 		p.entry = nil
 | |
| 
 | |
| 		err := p.compressPathIfPossible()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return entry, nil
 | |
| 	}
 | |
| 	bit, err := p.targetBitFromIP(network.Number)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	child := p.children[bit]
 | |
| 	if child != nil {
 | |
| 		return child.remove(network)
 | |
| 	}
 | |
| 	return nil, nil
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) qualifiesForPathCompression() bool {
 | |
| 	// Current prefix trie can be path compressed if it meets all following.
 | |
| 	//		1. records no CIDR entry
 | |
| 	//		2. has single or no child
 | |
| 	//		3. is not root trie
 | |
| 	return !p.hasEntry() && p.childrenCount() <= 1 && p.parent != nil
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) compressPathIfPossible() error {
 | |
| 	if !p.qualifiesForPathCompression() {
 | |
| 		// Does not qualify to be compressed
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	// Find lone child.
 | |
| 	var loneChild *prefixTrie
 | |
| 	for _, child := range p.children {
 | |
| 		if child != nil {
 | |
| 			loneChild = child
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Find root of currnt single child lineage.
 | |
| 	parent := p.parent
 | |
| 	for ; parent.qualifiesForPathCompression(); parent = parent.parent {
 | |
| 	}
 | |
| 	parentBit, err := parent.targetBitFromIP(p.network.Number)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	parent.children[parentBit] = loneChild
 | |
| 
 | |
| 	// Attempts to furthur apply path compression at current lineage parent, in case current lineage
 | |
| 	// compressed into parent.
 | |
| 	return parent.compressPathIfPossible()
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) childrenCount() int {
 | |
| 	count := 0
 | |
| 	for _, child := range p.children {
 | |
| 		if child != nil {
 | |
| 			count++
 | |
| 		}
 | |
| 	}
 | |
| 	return count
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) totalNumberOfBits() uint {
 | |
| 	return rnet.BitsPerUint32 * uint(len(p.network.Number))
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) targetBitPosition() int {
 | |
| 	return int(p.totalNumberOfBits()-p.numBitsSkipped) - 1
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) targetBitFromIP(n rnet.NetworkNumber) (uint32, error) {
 | |
| 	// This is a safe uint boxing of int since we should never attempt to get
 | |
| 	// target bit at a negative position.
 | |
| 	return n.Bit(uint(p.targetBitPosition()))
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) hasEntry() bool {
 | |
| 	return p.entry != nil
 | |
| }
 | |
| 
 | |
| func (p *prefixTrie) level() int {
 | |
| 	if p.parent == nil {
 | |
| 		return 0
 | |
| 	}
 | |
| 	return p.parent.level() + 1
 | |
| }
 | |
| 
 | |
| // walkDepth walks the trie in depth order, for unit testing.
 | |
| func (p *prefixTrie) walkDepth() <-chan RangerEntry {
 | |
| 	entries := make(chan RangerEntry)
 | |
| 	go func() {
 | |
| 		if p.hasEntry() {
 | |
| 			entries <- p.entry
 | |
| 		}
 | |
| 		childEntriesList := []<-chan RangerEntry{}
 | |
| 		for _, trie := range p.children {
 | |
| 			if trie == nil {
 | |
| 				continue
 | |
| 			}
 | |
| 			childEntriesList = append(childEntriesList, trie.walkDepth())
 | |
| 		}
 | |
| 		for _, childEntries := range childEntriesList {
 | |
| 			for entry := range childEntries {
 | |
| 				entries <- entry
 | |
| 			}
 | |
| 		}
 | |
| 		close(entries)
 | |
| 	}()
 | |
| 	return entries
 | |
| }
 |