Aggregator.rb

This content was produced by an LLM and could include errors.

This script summarizes a list of IP addresses into the smallest possible CIDR blocks, then converts those blocks into standard Access Control List (ACL) format.

def summarize_ips(ip_list, min_mask: 0)
  ips = ip_list.uniq.map { IPAddr.new(_1) }.sort_by(&:to_i)
  
  ips.chunk_while { |a, b| b.to_i == a.to_i + 1 }
  .flat_map { |c| range_to_cidrs(c.first, c.last, min_mask: min_mask) }
end
#=> :summarize_ips

def cidrs_to_acl(entries)
  entries.map { |e| cidr_to_acl(e) }
end
#=> :cidrs_to_acl

def mask_to_netmask(mask)
  [(0xffffffff << (32 - mask)) & 0xffffffff]
  .pack('N').unpack('C4').join('.')
end
#=> :mask_to_netmask

def cidr_to_acl(entry)
  ip, mask = entry
  return "host #{ip}" if mask == 32
  
  "#{ip} #{mask_to_netmask(mask)}"
end
#=> :cidr_to_acl

def largest_block(start_ip, end_ip, min_mask)
  mask = 32
  best = [start_ip.to_s, 32]
  
  while mask >= min_mask
    block = IPAddr.new("#{start_ip}/#{mask}")
    
    break unless block.to_range.first == start_ip
    break if block.to_range.last > end_ip
    
    best = [start_ip.to_s, mask]
    mask -= 1
  end
  
  best
end
#=> :largest_block

def range_to_cidrs(start_ip, end_ip, min_mask:)
  res = []
  cur = start_ip
  
  while cur <= end_ip
    ip_str, mask = largest_block(cur, end_ip, min_mask)
    res << [ip_str, mask]
    block = IPAddr.new("#{cur}/#{mask}")
    cur = IPAddr.new(block.to_range.last.to_i + 1, Socket::AF_INET)
  end
  
  res
end
#=> :range_to_cidrs

ips = ['192.168.1.0', '192.168.1.1']
#=> ["192.168.1.0", "192.168.1.1"]
raw = summarize_ips(ips)
#=> [["192.168.1.0", 31]]
cidrs_to_acl(raw)
#=> ["192.168.1.0 255.255.255.254"]

Ruby 4.0.3