wgctl/modules/ip.module.sh
2026-05-20 21:49:44 +00:00

99 lines
2.3 KiB
Bash

#!/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}$"
}
# ip::next_for_subnet <cidr>
# 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:-}"
[[ -z "$ip" ]] && return 1
# Strip CIDR mask if present
local addr="${ip%%/*}"
# Structural check — 4 octets, optional /mask
[[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$ ]] || return 1
# Octet range check — each must be 0-255
local IFS='.'
local -a octets
read -ra octets <<< "$addr"
for octet in "${octets[@]}"; do
(( octet >= 0 && octet <= 255 )) || return 1
done
return 0
}
function ip::is_cidr() {
[[ "$1" == *"/"* ]]
}
# ip::is_valid_for_subnet <cidr> <ip>
# 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 <cidr> <ip>
# 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
}