#!/usr/bin/env bash # ============================================ # IP Assignment # ============================================ function ip::assigned() { grep -h "^Address" "$(ctx::clients)"/*.conf 2>/dev/null \ | awk '{print $3}' \ | cut -d'/' -f1 } function ip::is_assigned() { local candidate="$1" ip::assigned | grep -q "^${candidate}$" } function ip::next_for_type() { local type="$1" local subnet subnet=$(config::subnet_for "$type") if [[ -z "$subnet" ]]; then log::error "Unknown device type: ${type}" return 1 fi for i in $(seq 1 254); do local candidate="${subnet}.${i}" if ! ip::is_assigned "$candidate"; then echo "$candidate" return 0 fi done log::error "No available IPs in subnet ${subnet}.0/24" return 1 } # ip::next_for_subnet # Finds the next unassigned host IP within a CIDR. # Replaces ip::next_for_type for the subnet-aware allocation path. function ip::next_for_subnet() { local cidr="${1:-}" if [[ -z "$cidr" ]]; then log::error "No subnet CIDR provided for IP allocation" return 1 fi local prefix prefix=$(subnet::prefix "$cidr") local candidate for i in $(subnet::host_range "$cidr"); do candidate="${prefix}.${i}" ip::is_assigned "$candidate" || { echo "$candidate"; return 0; } done log::error "No available IPs in subnet ${cidr}" return 1 } # ============================================ # Validation # ============================================ function ip::is_valid() { local ip="$1" [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$ ]] } function ip::is_cidr() { [[ "$1" == *"/"* ]] } # ip::is_valid_for_subnet # Convenience wrapper — validates an IP against a specific subnet. # Delegates to subnet::ip_valid_for which handles all the checks. function ip::is_valid_for_subnet() { local cidr="${1:-}" ip="${2:-}" subnet::ip_valid_for "$cidr" "$ip" } # ip::require_valid_for_subnet # Errors and returns 1 if the IP is not valid for the subnet. # Used when a manual --ip override is provided. function ip::require_valid_for_subnet() { local cidr="${1:-}" ip="${2:-}" subnet::require_ip_valid_for "$cidr" "$ip" } function ip::validate() { local ip="$1" if ! ip::is_valid "$ip"; then log::error "Invalid IP or CIDR: ${ip}" return 1 fi return 0 } function ip::require_valid() { local ip="$1" ip::validate "$ip" || exit 1 }