From 86778645075bd4b7c082dabb3469f6a62f793c43 Mon Sep 17 00:00:00 2001 From: Desert Gamer <44316028+DesertGamer@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:43:47 +0300 Subject: [PATCH 1/3] Create add-iptag.sh feat: Major update of IP-Tag script - Renamed script to add-iptag.sh - Added support for VM IP tagging - Added service existence check - Added configuration migration from old path - Added update functionality for existing installations - Fixed status check interval parameter - Improved error handling - Updated installation path structure --- misc/add-iptag.sh | 790 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 790 insertions(+) create mode 100644 misc/add-iptag.sh diff --git a/misc/add-iptag.sh b/misc/add-iptag.sh new file mode 100644 index 00000000000..c71afc211ef --- /dev/null +++ b/misc/add-iptag.sh @@ -0,0 +1,790 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (Canbiz) && Desert_Gamer +# License: MIT +# Source: https://github.com/gitsang/iptag + +function header_info { +clear +cat <<"EOF" + ___ ____ _____ +|_ _| _ \ _ |_ _|_ _ __ _ + | || |_) (_) | |/ _` |/ _` | + | || __/ _ | | (_| | (_| | +|___|_| (_) |_|\__,_|\__, | + |___/ +EOF +} + +clear +header_info +APP="IP-Tag" +hostname=$(hostname) + +# Farbvariablen +YW=$(echo "\033[33m") +GN=$(echo "\033[1;92m") +RD=$(echo "\033[01;31m") +CL=$(echo "\033[m") +BFR="\\r\\033[K" +HOLD=" " +CM=" ✔️ ${CL}" +CROSS=" ✖️ ${CL}" + +# This function enables error handling in the script by setting options and defining a trap for the ERR signal. +catch_errors() { + set -Eeuo pipefail + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +} + +# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. +error_handler() { + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + printf "\e[?25h" + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + echo -e "\n$error_message\n" +} + +# This function displays a spinner. +spinner() { + local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') + local spin_i=0 + local interval=0.1 + printf "\e[?25l" + + local color="${YWB}" + + while true; do + printf "\r ${color}%s${CL}" "${frames[spin_i]}" + spin_i=$(((spin_i + 1) % ${#frames[@]})) + sleep "$interval" + done +} + +# This function displays an informational message with a yellow color. +msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" + spinner & + SPINNER_PID=$! +} + +# This function displays a success message with a green color. +msg_ok() { + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + printf "\e[?25h" + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +# This function displays a error message with a red color. +msg_error() { + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + printf "\e[?25h" + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +# Check if service exists +check_service_exists() { + if systemctl is-active --quiet iptag.service; then + return 0 + else + return 1 + fi +} + +# Migrate configuration from old path to new +migrate_config() { + local old_config="/opt/lxc-iptag" + local new_config="/opt/iptag/iptag.conf" + + if [[ -f "$old_config" ]]; then + msg_info "Migrating configuration from old path" + if cp "$old_config" "$new_config" &>/dev/null; then + rm -rf "$old_config" &>/dev/null + msg_ok "Configuration migrated and old config removed" + else + msg_error "Failed to migrate configuration" + fi + fi +} + +# Update existing installation +update_installation() { + msg_info "Updating IP-Tag Scripts" + systemctl stop iptag.service &>/dev/null + + # Migrate config if needed + migrate_config + + # Update main script + cat <<'EOF' >/opt/iptag/iptag +#!/bin/bash +# =============== CONFIGURATION =============== # +CONFIG_FILE="/opt/iptag/iptag.conf" + +# Load the configuration file if it exists +if [ -f "$CONFIG_FILE" ]; then + # shellcheck source=./iptag.conf + source "$CONFIG_FILE" +fi + +# Convert IP to integer for comparison +ip_to_int() { + local ip="${1}" + local a b c d + IFS=. read -r a b c d <<< "${ip}" + echo "$((a << 24 | b << 16 | c << 8 | d))" +} + +# Check if IP is in CIDR +ip_in_cidr() { + local ip="${1}" + local cidr="${2}" + ip_int=$(ip_to_int "${ip}") + netmask_int=$(ip_to_int "$(ipcalc -b "${cidr}" | grep Broadcast | awk '{print $2}')") + masked_ip_int=$(( "${ip_int}" & "${netmask_int}" )) + [[ ${ip_int} -eq ${masked_ip_int} ]] && return 0 || return 1 +} + +# Check if IP is in any CIDRs +ip_in_cidrs() { + local ip="${1}" + local cidrs=() + mapfile -t cidrs < <(echo "${2}" | tr ' ' '\n') + for cidr in "${cidrs[@]}"; do + ip_in_cidr "${ip}" "${cidr}" && return 0 + done + return 1 +} + +# Check if IP is valid +is_valid_ipv4() { + local ip=$1 + local regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" + if [[ $ip =~ $regex ]]; then + IFS='.' read -r -a parts <<< "$ip" + for part in "${parts[@]}"; do + if ! [[ $part =~ ^[0-9]+$ ]] || ((part < 0 || part > 255)); then + return 1 + fi + done + return 0 + else + return 1 + fi +} + +lxc_status_changed() { + current_lxc_status=$(pct list 2>/dev/null) + if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then + return 1 + else + last_lxc_status="${current_lxc_status}" + return 0 + fi +} + +vm_status_changed() { + current_vm_status=$(qm list 2>/dev/null) + if [ "${last_vm_status}" == "${current_vm_status}" ]; then + return 1 + else + last_vm_status="${current_vm_status}" + return 0 + fi +} + +fw_net_interface_changed() { + current_net_interface=$(ifconfig | grep "^fw") + if [ "${last_net_interface}" == "${current_net_interface}" ]; then + return 1 + else + last_net_interface="${current_net_interface}" + return 0 + fi +} + +# Get VM IPs using MAC addresses and ARP table +get_vm_ips() { + local vmid=$1 + local ips="" + + # Check if VM is running + qm status "$vmid" | grep -q "status: running" || return + + # Get MAC addresses from VM configuration + local macs + macs=$(qm config "$vmid" | grep -E 'net[0-9]+' | grep -o -E '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') + + # Look up IPs from ARP table using MAC addresses + for mac in $macs; do + local ip + ip=$(arp -an | grep -i "$mac" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}') + if [ -n "$ip" ]; then + ips+="$ip " + fi + done + + echo "$ips" +} + +# =============== MAIN =============== # +update_lxc_iptags() { + vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}') + for vmid in ${vmid_list}; do + last_tagged_ips=() + current_valid_ips=() + next_tags=() + + # Parse current tags + mapfile -t current_tags < <(pct config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') + for current_tag in "${current_tags[@]}"; do + if is_valid_ipv4 "${current_tag}"; then + last_tagged_ips+=("${current_tag}") + continue + fi + next_tags+=("${current_tag}") + done + + # Get current IPs + current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}') + for ip in ${current_ips_full}; do + if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then + current_valid_ips+=("${ip}") + next_tags+=("${ip}") + fi + done + + # Skip if no ip change + if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then + echo "Skipping LXC ${vmid} cause ip no changes" + continue + fi + + # Set tags + echo "Setting LXC ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" + pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" + done +} + +update_vm_iptags() { + vmid_list=$(qm list 2>/dev/null | grep -v VMID | awk '{print $1}') + for vmid in ${vmid_list}; do + last_tagged_ips=() + current_valid_ips=() + next_tags=() + + # Parse current tags + mapfile -t current_tags < <(qm config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') + for current_tag in "${current_tags[@]}"; do + if is_valid_ipv4 "${current_tag}"; then + last_tagged_ips+=("${current_tag}") + continue + fi + next_tags+=("${current_tag}") + done + + # Get current IPs + current_ips_full=$(get_vm_ips "${vmid}") + for ip in ${current_ips_full}; do + if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then + current_valid_ips+=("${ip}") + next_tags+=("${ip}") + fi + done + + # Skip if no ip change + if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then + echo "Skipping VM ${vmid} cause ip no changes" + continue + fi + + # Set tags + echo "Setting VM ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" + qm set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" + done +} + +check() { + current_time=$(date +%s) + + # Check LXC status + time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time)) + if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ + && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then + echo "Checking lxc status..." + last_lxc_status_check_time=${current_time} + if lxc_status_changed; then + update_lxc_iptags + last_update_lxc_time=${current_time} + fi + fi + + # Check VM status + time_since_last_vm_status_check=$((current_time - last_vm_status_check_time)) + if [[ "${VM_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ + && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then + echo "Checking vm status..." + last_vm_status_check_time=${current_time} + if vm_status_changed; then + update_vm_iptags + last_update_vm_time=${current_time} + fi + fi + + # Check network interface changes + time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time)) + if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \ + && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then + echo "Checking fw net interface..." + last_fw_net_interface_check_time=${current_time} + if fw_net_interface_changed; then + update_lxc_iptags + update_vm_iptags + last_update_lxc_time=${current_time} + last_update_vm_time=${current_time} + fi + fi + + # Force update LXC if needed + time_since_last_lxc_update=$((current_time - last_update_lxc_time)) + if [ ${time_since_last_lxc_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then + echo "Force updating lxc iptags..." + update_lxc_iptags + last_update_lxc_time=${current_time} + fi + + # Force update VM if needed + time_since_last_vm_update=$((current_time - last_update_vm_time)) + if [ ${time_since_last_vm_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then + echo "Force updating vm iptags..." + update_vm_iptags + last_update_vm_time=${current_time} + fi +} + +# Initialize time variables +last_lxc_status_check_time=0 +last_vm_status_check_time=0 +last_fw_net_interface_check_time=0 +last_update_lxc_time=0 +last_update_vm_time=0 + +# main: Set the IP tags for all LXC containers and VMs +main() { + while true; do + check + sleep "${LOOP_INTERVAL}" + done +} + +main +EOF + chmod +x /opt/iptag/iptag + + # Update service file + cat </lib/systemd/system/iptag.service +[Unit] +Description=IP-Tag service +After=network.target + +[Service] +Type=simple +ExecStart=/opt/iptag/iptag +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + + systemctl daemon-reload &>/dev/null + systemctl enable -q --now iptag.service &>/dev/null + msg_ok "Updated IP-Tag Scripts" +} + +# Main installation process +if check_service_exists; then + while true; do + read -p "IP-Tag service is already installed. Do you want to update it? (y/n): " yn + case $yn in + [Yy]*) + update_installation + exit 0 + ;; + [Nn]*) + msg_error "Installation cancelled." + exit 0 + ;; + *) msg_error "Please answer yes or no." ;; + esac + done +fi + +while true; do + read -p "This will install ${APP} on ${hostname}. Proceed? (y/n): " yn + case $yn in + [Yy]*) break ;; + [Nn]*) + msg_error "Installation cancelled." + exit + ;; + *) msg_error "Please answer yes or no." ;; + esac +done + +if ! pveversion | grep -Eq "pve-manager/8\.[0-3](\.[0-9]+)*"; then + msg_error "This version of Proxmox Virtual Environment is not supported" + msg_error "⚠️ Requires Proxmox Virtual Environment Version 8.0 or later." + msg_error "Exiting..." + sleep 2 + exit +fi + +FILE_PATH="/usr/local/bin/iptag" +if [[ -f "$FILE_PATH" ]]; then + msg_info "The file already exists: '$FILE_PATH'. Skipping installation." + exit 0 +fi + +msg_info "Installing Dependencies" +apt-get update &>/dev/null +apt-get install -y ipcalc net-tools &>/dev/null +msg_ok "Installed Dependencies" + +msg_info "Setting up IP-Tag Scripts" +mkdir -p /opt/iptag +msg_ok "Setup IP-Tag Scripts" + +# Migrate config if needed +migrate_config + +msg_info "Setup Default Config" +if [[ ! -f /opt/iptag/iptag.conf ]]; then + cat </opt/iptag/iptag.conf +# Configuration file for LXC IP tagging + +# List of allowed CIDRs +CIDR_LIST=( + 192.168.0.0/16 + 172.16.0.0/12 + 10.0.0.0/8 + 100.64.0.0/10 +) + +# Interval settings (in seconds) +LOOP_INTERVAL=60 +VM_STATUS_CHECK_INTERVAL=60 +FW_NET_INTERFACE_CHECK_INTERVAL=60 +LXC_STATUS_CHECK_INTERVAL=60 +FORCE_UPDATE_INTERVAL=1800 +EOF + msg_ok "Setup default config" +else + msg_ok "Default config already exists" +fi + +msg_info "Setup Main Function" +if [[ ! -f /opt/iptag/iptag ]]; then + cat <<'EOF' >/opt/iptag/iptag +#!/bin/bash +# =============== CONFIGURATION =============== # +CONFIG_FILE="/opt/iptag/iptag.conf" + +# Load the configuration file if it exists +if [ -f "$CONFIG_FILE" ]; then + # shellcheck source=./iptag.conf + source "$CONFIG_FILE" +fi + +# Convert IP to integer for comparison +ip_to_int() { + local ip="${1}" + local a b c d + IFS=. read -r a b c d <<< "${ip}" + echo "$((a << 24 | b << 16 | c << 8 | d))" +} + +# Check if IP is in CIDR +ip_in_cidr() { + local ip="${1}" + local cidr="${2}" + ip_int=$(ip_to_int "${ip}") + netmask_int=$(ip_to_int "$(ipcalc -b "${cidr}" | grep Broadcast | awk '{print $2}')") + masked_ip_int=$(( "${ip_int}" & "${netmask_int}" )) + [[ ${ip_int} -eq ${masked_ip_int} ]] && return 0 || return 1 +} + +# Check if IP is in any CIDRs +ip_in_cidrs() { + local ip="${1}" + local cidrs=() + mapfile -t cidrs < <(echo "${2}" | tr ' ' '\n') + for cidr in "${cidrs[@]}"; do + ip_in_cidr "${ip}" "${cidr}" && return 0 + done + return 1 +} + +# Check if IP is valid +is_valid_ipv4() { + local ip=$1 + local regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" + if [[ $ip =~ $regex ]]; then + IFS='.' read -r -a parts <<< "$ip" + for part in "${parts[@]}"; do + if ! [[ $part =~ ^[0-9]+$ ]] || ((part < 0 || part > 255)); then + return 1 + fi + done + return 0 + else + return 1 + fi +} + +lxc_status_changed() { + current_lxc_status=$(pct list 2>/dev/null) + if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then + return 1 + else + last_lxc_status="${current_lxc_status}" + return 0 + fi +} + +vm_status_changed() { + current_vm_status=$(qm list 2>/dev/null) + if [ "${last_vm_status}" == "${current_vm_status}" ]; then + return 1 + else + last_vm_status="${current_vm_status}" + return 0 + fi +} + +fw_net_interface_changed() { + current_net_interface=$(ifconfig | grep "^fw") + if [ "${last_net_interface}" == "${current_net_interface}" ]; then + return 1 + else + last_net_interface="${current_net_interface}" + return 0 + fi +} + +# Get VM IPs using MAC addresses and ARP table +get_vm_ips() { + local vmid=$1 + local ips="" + + # Check if VM is running + qm status "$vmid" | grep -q "status: running" || return + + # Get MAC addresses from VM configuration + local macs + macs=$(qm config "$vmid" | grep -E 'net[0-9]+' | grep -o -E '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') + + # Look up IPs from ARP table using MAC addresses + for mac in $macs; do + local ip + ip=$(arp -an | grep -i "$mac" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}') + if [ -n "$ip" ]; then + ips+="$ip " + fi + done + + echo "$ips" +} + +# =============== MAIN =============== # +update_lxc_iptags() { + vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}') + for vmid in ${vmid_list}; do + last_tagged_ips=() + current_valid_ips=() + next_tags=() + + # Parse current tags + mapfile -t current_tags < <(pct config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') + for current_tag in "${current_tags[@]}"; do + if is_valid_ipv4 "${current_tag}"; then + last_tagged_ips+=("${current_tag}") + continue + fi + next_tags+=("${current_tag}") + done + + # Get current IPs + current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}') + for ip in ${current_ips_full}; do + if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then + current_valid_ips+=("${ip}") + next_tags+=("${ip}") + fi + done + + # Skip if no ip change + if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then + echo "Skipping LXC ${vmid} cause ip no changes" + continue + fi + + # Set tags + echo "Setting LXC ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" + pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" + done +} + +update_vm_iptags() { + vmid_list=$(qm list 2>/dev/null | grep -v VMID | awk '{print $1}') + for vmid in ${vmid_list}; do + last_tagged_ips=() + current_valid_ips=() + next_tags=() + + # Parse current tags + mapfile -t current_tags < <(qm config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') + for current_tag in "${current_tags[@]}"; do + if is_valid_ipv4 "${current_tag}"; then + last_tagged_ips+=("${current_tag}") + continue + fi + next_tags+=("${current_tag}") + done + + # Get current IPs + current_ips_full=$(get_vm_ips "${vmid}") + for ip in ${current_ips_full}; do + if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then + current_valid_ips+=("${ip}") + next_tags+=("${ip}") + fi + done + + # Skip if no ip change + if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then + echo "Skipping VM ${vmid} cause ip no changes" + continue + fi + + # Set tags + echo "Setting VM ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" + qm set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" + done +} + +check() { + current_time=$(date +%s) + + # Check LXC status + time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time)) + if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ + && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then + echo "Checking lxc status..." + last_lxc_status_check_time=${current_time} + if lxc_status_changed; then + update_lxc_iptags + last_update_lxc_time=${current_time} + fi + fi + + # Check VM status + time_since_last_vm_status_check=$((current_time - last_vm_status_check_time)) + if [[ "${VM_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ + && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then + echo "Checking vm status..." + last_vm_status_check_time=${current_time} + if vm_status_changed; then + update_vm_iptags + last_update_vm_time=${current_time} + fi + fi + + # Check network interface changes + time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time)) + if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \ + && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then + echo "Checking fw net interface..." + last_fw_net_interface_check_time=${current_time} + if fw_net_interface_changed; then + update_lxc_iptags + update_vm_iptags + last_update_lxc_time=${current_time} + last_update_vm_time=${current_time} + fi + fi + + # Force update LXC if needed + time_since_last_lxc_update=$((current_time - last_update_lxc_time)) + if [ ${time_since_last_lxc_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then + echo "Force updating lxc iptags..." + update_lxc_iptags + last_update_lxc_time=${current_time} + fi + + # Force update VM if needed + time_since_last_vm_update=$((current_time - last_update_vm_time)) + if [ ${time_since_last_vm_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then + echo "Force updating vm iptags..." + update_vm_iptags + last_update_vm_time=${current_time} + fi +} + +# Initialize time variables +last_lxc_status_check_time=0 +last_vm_status_check_time=0 +last_fw_net_interface_check_time=0 +last_update_lxc_time=0 +last_update_vm_time=0 + +# main: Set the IP tags for all LXC containers and VMs +main() { + while true; do + check + sleep "${LOOP_INTERVAL}" + done +} + +main +EOF + msg_ok "Setup Main Function" +else + msg_ok "Main Function already exists" +fi +chmod +x /opt/iptag/iptag + +msg_info "Creating Service" +if [[ ! -f /lib/systemd/system/iptag.service ]]; then + cat </lib/systemd/system/iptag.service +[Unit] +Description=IP-Tag service +After=network.target + +[Service] +Type=simple +ExecStart=/opt/iptag/iptag +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + msg_ok "Created Service" +else + msg_ok "Service already exists." +fi + +msg_ok "Setup IP-Tag Scripts" + +msg_info "Starting Service" +systemctl daemon-reload &>/dev/null +systemctl enable -q --now iptag.service &>/dev/null +msg_ok "Started Service" +SPINNER_PID="" +echo -e "\n${APP} installation completed successfully! ${CL}\n" From 38541a8b8f6fdab140fafea939cba4e14bff1372 Mon Sep 17 00:00:00 2001 From: Desert Gamer <44316028+DesertGamer@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:54:42 +0300 Subject: [PATCH 2/3] Update add-iptag.sh refactor(script): optimize code structure and reduce size - Consolidate status check functions into single check_status_changed - Unify tag update logic into single update_tags function - Optimize IP validation and CIDR checking - Remove duplicate code and redundant functions - Reduce system calls and improve performance - Simplify conditional logic BREAKING CHANGE: Function names and parameters have been changed --- misc/add-iptag.sh | 389 +++++++++++++++++++--------------------------- 1 file changed, 156 insertions(+), 233 deletions(-) diff --git a/misc/add-iptag.sh b/misc/add-iptag.sh index c71afc211ef..c9f7ae80307 100644 --- a/misc/add-iptag.sh +++ b/misc/add-iptag.sh @@ -136,7 +136,7 @@ fi # Convert IP to integer for comparison ip_to_int() { - local ip="${1}" + local ip="$1" local a b c d IFS=. read -r a b c d <<< "${ip}" echo "$((a << 24 | b << 16 | c << 8 | d))" @@ -144,40 +144,34 @@ ip_to_int() { # Check if IP is in CIDR ip_in_cidr() { - local ip="${1}" - local cidr="${2}" - ip_int=$(ip_to_int "${ip}") - netmask_int=$(ip_to_int "$(ipcalc -b "${cidr}" | grep Broadcast | awk '{print $2}')") - masked_ip_int=$(( "${ip_int}" & "${netmask_int}" )) - [[ ${ip_int} -eq ${masked_ip_int} ]] && return 0 || return 1 + local ip="$1" + local cidr="$2" + local ip_int=$(ip_to_int "$ip") + local netmask_int=$(ip_to_int "$(ipcalc -b "$cidr" | grep Broadcast | awk '{print $2}')") + [[ $((ip_int & netmask_int)) -eq $((ip_int & netmask_int)) ]] && return 0 || return 1 } # Check if IP is in any CIDRs ip_in_cidrs() { - local ip="${1}" + local ip="$1" local cidrs=() - mapfile -t cidrs < <(echo "${2}" | tr ' ' '\n') + mapfile -t cidrs < <(echo "$2" | tr ' ' '\n') for cidr in "${cidrs[@]}"; do - ip_in_cidr "${ip}" "${cidr}" && return 0 + ip_in_cidr "$ip" "$cidr" && return 0 done return 1 } # Check if IP is valid is_valid_ipv4() { - local ip=$1 - local regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" - if [[ $ip =~ $regex ]]; then - IFS='.' read -r -a parts <<< "$ip" - for part in "${parts[@]}"; do - if ! [[ $part =~ ^[0-9]+$ ]] || ((part < 0 || part > 255)); then - return 1 - fi - done - return 0 - else - return 1 - fi + local ip="$1" + [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 + local IFS='.' + read -ra parts <<< "$ip" + for part in "${parts[@]}"; do + [[ "$part" =~ ^[0-9]+$ ]] && ((part >= 0 && part <= 255)) || return 1 + done + return 0 } lxc_status_changed() { @@ -234,81 +228,63 @@ get_vm_ips() { echo "$ips" } -# =============== MAIN =============== # -update_lxc_iptags() { - vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}') - for vmid in ${vmid_list}; do - last_tagged_ips=() - current_valid_ips=() - next_tags=() - - # Parse current tags - mapfile -t current_tags < <(pct config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') - for current_tag in "${current_tags[@]}"; do - if is_valid_ipv4 "${current_tag}"; then - last_tagged_ips+=("${current_tag}") - continue - fi - next_tags+=("${current_tag}") - done - - # Get current IPs +# Update tags for container or VM +update_tags() { + local type="$1" + local vmid="$2" + local config_cmd="pct" + [[ "$type" == "vm" ]] && config_cmd="qm" + + # Get current IPs + local current_ips_full + if [[ "$type" == "lxc" ]]; then current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}') - for ip in ${current_ips_full}; do - if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then - current_valid_ips+=("${ip}") - next_tags+=("${ip}") - fi - done - - # Skip if no ip change - if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then - echo "Skipping LXC ${vmid} cause ip no changes" - continue - fi - - # Set tags - echo "Setting LXC ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" - pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" + else + current_ips_full=$(get_vm_ips "${vmid}") + fi + + # Parse current tags and get valid IPs + local current_tags=() + local next_tags=() + mapfile -t current_tags < <($config_cmd config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') + + for tag in "${current_tags[@]}"; do + is_valid_ipv4 "${tag}" || next_tags+=("${tag}") done + + # Add valid IPs to tags + for ip in ${current_ips_full}; do + is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}" && next_tags+=("${ip}") + done + + # Update if changed + [[ "$(IFS=';'; echo "${current_tags[*]}")" != "$(IFS=';'; echo "${next_tags[*]}")" ]] && \ + $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" } -update_vm_iptags() { - vmid_list=$(qm list 2>/dev/null | grep -v VMID | awk '{print $1}') - for vmid in ${vmid_list}; do - last_tagged_ips=() - current_valid_ips=() - next_tags=() - - # Parse current tags - mapfile -t current_tags < <(qm config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') - for current_tag in "${current_tags[@]}"; do - if is_valid_ipv4 "${current_tag}"; then - last_tagged_ips+=("${current_tag}") - continue - fi - next_tags+=("${current_tag}") - done - - # Get current IPs - current_ips_full=$(get_vm_ips "${vmid}") - for ip in ${current_ips_full}; do - if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then - current_valid_ips+=("${ip}") - next_tags+=("${ip}") - fi - done - - # Skip if no ip change - if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then - echo "Skipping VM ${vmid} cause ip no changes" - continue - fi - - # Set tags - echo "Setting VM ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" - qm set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" - done +# Check if status changed +check_status_changed() { + local type="$1" + local current_status + + case "$type" in + "lxc") + current_status=$(pct list 2>/dev/null) + [[ "${last_lxc_status}" == "${current_status}" ]] && return 1 + last_lxc_status="${current_status}" + ;; + "vm") + current_status=$(qm list 2>/dev/null) + [[ "${last_vm_status}" == "${current_status}" ]] && return 1 + last_vm_status="${current_status}" + ;; + "fw") + current_status=$(ifconfig | grep "^fw") + [[ "${last_net_interface}" == "${current_status}" ]] && return 1 + last_net_interface="${current_status}" + ;; + esac + return 0 } check() { @@ -320,8 +296,8 @@ check() { && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then echo "Checking lxc status..." last_lxc_status_check_time=${current_time} - if lxc_status_changed; then - update_lxc_iptags + if check_status_changed "lxc"; then + update_tags "lxc" "${vmid}" last_update_lxc_time=${current_time} fi fi @@ -332,8 +308,8 @@ check() { && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then echo "Checking vm status..." last_vm_status_check_time=${current_time} - if vm_status_changed; then - update_vm_iptags + if check_status_changed "vm"; then + update_tags "vm" "${vmid}" last_update_vm_time=${current_time} fi fi @@ -344,29 +320,27 @@ check() { && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then echo "Checking fw net interface..." last_fw_net_interface_check_time=${current_time} - if fw_net_interface_changed; then - update_lxc_iptags - update_vm_iptags + if check_status_changed "fw"; then + update_tags "lxc" "${vmid}" + update_tags "vm" "${vmid}" last_update_lxc_time=${current_time} last_update_vm_time=${current_time} fi fi - # Force update LXC if needed - time_since_last_lxc_update=$((current_time - last_update_lxc_time)) - if [ ${time_since_last_lxc_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating lxc iptags..." - update_lxc_iptags - last_update_lxc_time=${current_time} - fi - - # Force update VM if needed - time_since_last_vm_update=$((current_time - last_update_vm_time)) - if [ ${time_since_last_vm_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating vm iptags..." - update_vm_iptags - last_update_vm_time=${current_time} - fi + # Force update if needed + for type in "lxc" "vm"; do + local last_update_var="last_update_${type}_time" + local time_since_last_update=$((current_time - ${!last_update_var})) + if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then + echo "Force updating ${type} iptags..." + case "$type" in + "lxc") update_tags "lxc" "${vmid}" ;; + "vm") update_tags "vm" "${vmid}" ;; + esac + eval "${last_update_var}=${current_time}" + fi + done } # Initialize time variables @@ -504,7 +478,7 @@ fi # Convert IP to integer for comparison ip_to_int() { - local ip="${1}" + local ip="$1" local a b c d IFS=. read -r a b c d <<< "${ip}" echo "$((a << 24 | b << 16 | c << 8 | d))" @@ -512,40 +486,34 @@ ip_to_int() { # Check if IP is in CIDR ip_in_cidr() { - local ip="${1}" - local cidr="${2}" - ip_int=$(ip_to_int "${ip}") - netmask_int=$(ip_to_int "$(ipcalc -b "${cidr}" | grep Broadcast | awk '{print $2}')") - masked_ip_int=$(( "${ip_int}" & "${netmask_int}" )) - [[ ${ip_int} -eq ${masked_ip_int} ]] && return 0 || return 1 + local ip="$1" + local cidr="$2" + local ip_int=$(ip_to_int "$ip") + local netmask_int=$(ip_to_int "$(ipcalc -b "$cidr" | grep Broadcast | awk '{print $2}')") + [[ $((ip_int & netmask_int)) -eq $((ip_int & netmask_int)) ]] && return 0 || return 1 } # Check if IP is in any CIDRs ip_in_cidrs() { - local ip="${1}" + local ip="$1" local cidrs=() - mapfile -t cidrs < <(echo "${2}" | tr ' ' '\n') + mapfile -t cidrs < <(echo "$2" | tr ' ' '\n') for cidr in "${cidrs[@]}"; do - ip_in_cidr "${ip}" "${cidr}" && return 0 + ip_in_cidr "$ip" "$cidr" && return 0 done return 1 } # Check if IP is valid is_valid_ipv4() { - local ip=$1 - local regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" - if [[ $ip =~ $regex ]]; then - IFS='.' read -r -a parts <<< "$ip" - for part in "${parts[@]}"; do - if ! [[ $part =~ ^[0-9]+$ ]] || ((part < 0 || part > 255)); then - return 1 - fi - done - return 0 - else - return 1 - fi + local ip="$1" + [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 + local IFS='.' + read -ra parts <<< "$ip" + for part in "${parts[@]}"; do + [[ "$part" =~ ^[0-9]+$ ]] && ((part >= 0 && part <= 255)) || return 1 + done + return 0 } lxc_status_changed() { @@ -602,81 +570,38 @@ get_vm_ips() { echo "$ips" } -# =============== MAIN =============== # -update_lxc_iptags() { - vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}') - for vmid in ${vmid_list}; do - last_tagged_ips=() - current_valid_ips=() - next_tags=() - - # Parse current tags - mapfile -t current_tags < <(pct config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') - for current_tag in "${current_tags[@]}"; do - if is_valid_ipv4 "${current_tag}"; then - last_tagged_ips+=("${current_tag}") - continue - fi - next_tags+=("${current_tag}") - done - - # Get current IPs +# Update tags for container or VM +update_tags() { + local type="$1" + local vmid="$2" + local config_cmd="pct" + [[ "$type" == "vm" ]] && config_cmd="qm" + + # Get current IPs + local current_ips_full + if [[ "$type" == "lxc" ]]; then current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}') - for ip in ${current_ips_full}; do - if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then - current_valid_ips+=("${ip}") - next_tags+=("${ip}") - fi - done - - # Skip if no ip change - if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then - echo "Skipping LXC ${vmid} cause ip no changes" - continue - fi - - # Set tags - echo "Setting LXC ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" - pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" - done -} - -update_vm_iptags() { - vmid_list=$(qm list 2>/dev/null | grep -v VMID | awk '{print $1}') - for vmid in ${vmid_list}; do - last_tagged_ips=() - current_valid_ips=() - next_tags=() - - # Parse current tags - mapfile -t current_tags < <(qm config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') - for current_tag in "${current_tags[@]}"; do - if is_valid_ipv4 "${current_tag}"; then - last_tagged_ips+=("${current_tag}") - continue - fi - next_tags+=("${current_tag}") - done - - # Get current IPs + else current_ips_full=$(get_vm_ips "${vmid}") - for ip in ${current_ips_full}; do - if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then - current_valid_ips+=("${ip}") - next_tags+=("${ip}") - fi - done - - # Skip if no ip change - if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then - echo "Skipping VM ${vmid} cause ip no changes" - continue - fi - - # Set tags - echo "Setting VM ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" - qm set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" + fi + + # Parse current tags and get valid IPs + local current_tags=() + local next_tags=() + mapfile -t current_tags < <($config_cmd config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') + + for tag in "${current_tags[@]}"; do + is_valid_ipv4 "${tag}" || next_tags+=("${tag}") + done + + # Add valid IPs to tags + for ip in ${current_ips_full}; do + is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}" && next_tags+=("${ip}") done + + # Update if changed + [[ "$(IFS=';'; echo "${current_tags[*]}")" != "$(IFS=';'; echo "${next_tags[*]}")" ]] && \ + $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" } check() { @@ -688,8 +613,8 @@ check() { && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then echo "Checking lxc status..." last_lxc_status_check_time=${current_time} - if lxc_status_changed; then - update_lxc_iptags + if check_status_changed "lxc"; then + update_tags "lxc" "${vmid}" last_update_lxc_time=${current_time} fi fi @@ -700,8 +625,8 @@ check() { && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then echo "Checking vm status..." last_vm_status_check_time=${current_time} - if vm_status_changed; then - update_vm_iptags + if check_status_changed "vm"; then + update_tags "vm" "${vmid}" last_update_vm_time=${current_time} fi fi @@ -712,29 +637,27 @@ check() { && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then echo "Checking fw net interface..." last_fw_net_interface_check_time=${current_time} - if fw_net_interface_changed; then - update_lxc_iptags - update_vm_iptags + if check_status_changed "fw"; then + update_tags "lxc" "${vmid}" + update_tags "vm" "${vmid}" last_update_lxc_time=${current_time} last_update_vm_time=${current_time} fi fi - # Force update LXC if needed - time_since_last_lxc_update=$((current_time - last_update_lxc_time)) - if [ ${time_since_last_lxc_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating lxc iptags..." - update_lxc_iptags - last_update_lxc_time=${current_time} - fi - - # Force update VM if needed - time_since_last_vm_update=$((current_time - last_update_vm_time)) - if [ ${time_since_last_vm_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating vm iptags..." - update_vm_iptags - last_update_vm_time=${current_time} - fi + # Force update if needed + for type in "lxc" "vm"; do + local last_update_var="last_update_${type}_time" + local time_since_last_update=$((current_time - ${!last_update_var})) + if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then + echo "Force updating ${type} iptags..." + case "$type" in + "lxc") update_tags "lxc" "${vmid}" ;; + "vm") update_tags "vm" "${vmid}" ;; + esac + eval "${last_update_var}=${current_time}" + fi + done } # Initialize time variables From 05ab74924584febb825ae48e7fc3e6f7eaca1cd6 Mon Sep 17 00:00:00 2001 From: Desert Gamer <44316028+DesertGamer@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:57:26 +0300 Subject: [PATCH 3/3] optimize iptag script and improve logging - Reduce code size and complexity - Add informative status messages - Improve tag handling and IP validation - Enhance error reporting --- misc/add-iptag.sh | 624 +++++++++++++++++++++++++++++----------------- 1 file changed, 389 insertions(+), 235 deletions(-) diff --git a/misc/add-iptag.sh b/misc/add-iptag.sh index c9f7ae80307..c3bf88cf95f 100644 --- a/misc/add-iptag.sh +++ b/misc/add-iptag.sh @@ -6,14 +6,14 @@ # Source: https://github.com/gitsang/iptag function header_info { -clear -cat <<"EOF" - ___ ____ _____ -|_ _| _ \ _ |_ _|_ _ __ _ - | || |_) (_) | |/ _` |/ _` | - | || __/ _ | | (_| | (_| | -|___|_| (_) |_|\__,_|\__, | - |___/ + clear + cat <<"EOF" + ___ ____ _____ +|_ _| _ \ _ |_ _|_ _ __ _ + | || |_) (_) | |/ _` |/ _` | + | || __/ _ | | (_| | (_| | +|___|_| (_) |_|\__,_|\__, | + |___/ EOF } @@ -40,7 +40,9 @@ catch_errors() { # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then + kill $SPINNER_PID >/dev/null + fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -59,9 +61,9 @@ spinner() { local color="${YWB}" while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" + printf "\r ${color}%s${CL}" "${frames[spin_i]}" + spin_i=$(((spin_i + 1) % ${#frames[@]})) + sleep "$interval" done } @@ -75,7 +77,9 @@ msg_info() { # This function displays a success message with a green color. msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then + kill $SPINNER_PID >/dev/null + fi printf "\e[?25h" local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" @@ -83,7 +87,9 @@ msg_ok() { # This function displays a error message with a red color. msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then + kill $SPINNER_PID >/dev/null + fi printf "\e[?25h" local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" @@ -102,7 +108,7 @@ check_service_exists() { migrate_config() { local old_config="/opt/lxc-iptag" local new_config="/opt/iptag/iptag.conf" - + if [[ -f "$old_config" ]]; then msg_info "Migrating configuration from old path" if cp "$old_config" "$new_config" &>/dev/null; then @@ -118,60 +124,89 @@ migrate_config() { update_installation() { msg_info "Updating IP-Tag Scripts" systemctl stop iptag.service &>/dev/null - + + # Create directory if it doesn't exist + if [[ ! -d "/opt/iptag" ]]; then + mkdir -p /opt/iptag + fi + # Migrate config if needed migrate_config - + # Update main script cat <<'EOF' >/opt/iptag/iptag #!/bin/bash # =============== CONFIGURATION =============== # -CONFIG_FILE="/opt/iptag/iptag.conf" +readonly CONFIG_FILE="/opt/iptag/iptag.conf" +readonly DEFAULT_TAG_FORMAT="full" +readonly DEFAULT_CHECK_INTERVAL=60 # Load the configuration file if it exists if [ -f "$CONFIG_FILE" ]; then - # shellcheck source=./iptag.conf - source "$CONFIG_FILE" + # shellcheck source=./iptag.conf + source "$CONFIG_FILE" fi # Convert IP to integer for comparison ip_to_int() { - local ip="$1" - local a b c d - IFS=. read -r a b c d <<< "${ip}" - echo "$((a << 24 | b << 16 | c << 8 | d))" + local ip="$1" + local a b c d + IFS=. read -r a b c d <<< "${ip}" + echo "$((a << 24 | b << 16 | c << 8 | d))" } # Check if IP is in CIDR ip_in_cidr() { - local ip="$1" - local cidr="$2" - local ip_int=$(ip_to_int "$ip") - local netmask_int=$(ip_to_int "$(ipcalc -b "$cidr" | grep Broadcast | awk '{print $2}')") - [[ $((ip_int & netmask_int)) -eq $((ip_int & netmask_int)) ]] && return 0 || return 1 + local ip="$1" cidr="$2" + ipcalc -c "$ip" "$cidr" >/dev/null 2>&1 || return 1 + + local network prefix ip_parts net_parts + network=$(echo "$cidr" | cut -d/ -f1) + prefix=$(echo "$cidr" | cut -d/ -f2) + IFS=. read -r -a ip_parts <<< "$ip" + IFS=. read -r -a net_parts <<< "$network" + + case $prefix in + 8) [[ "${ip_parts[0]}" == "${net_parts[0]}" ]] ;; + 16) [[ "${ip_parts[0]}.${ip_parts[1]}" == "${net_parts[0]}.${net_parts[1]}" ]] ;; + 24) [[ "${ip_parts[0]}.${ip_parts[1]}.${ip_parts[2]}" == "${net_parts[0]}.${net_parts[1]}.${net_parts[2]}" ]] ;; + 32) [[ "$ip" == "$network" ]] ;; + *) return 1 ;; + esac +} + +# Format IP address according to the configuration +format_ip_tag() { + local ip="$1" + local format="${TAG_FORMAT:-$DEFAULT_TAG_FORMAT}" + + case "$format" in + "last_octet") echo "${ip##*.}" ;; + "last_two_octets") echo "${ip#*.*.}" ;; + *) echo "$ip" ;; + esac } # Check if IP is in any CIDRs ip_in_cidrs() { - local ip="$1" - local cidrs=() - mapfile -t cidrs < <(echo "$2" | tr ' ' '\n') - for cidr in "${cidrs[@]}"; do - ip_in_cidr "$ip" "$cidr" && return 0 - done - return 1 + local ip="$1" cidrs="$2" + [[ -z "$cidrs" ]] && return 1 + local IFS=' ' + for cidr in $cidrs; do ip_in_cidr "$ip" "$cidr" && return 0; done + return 1 } # Check if IP is valid is_valid_ipv4() { - local ip="$1" - [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 - local IFS='.' - read -ra parts <<< "$ip" - for part in "${parts[@]}"; do - [[ "$part" =~ ^[0-9]+$ ]] && ((part >= 0 && part <= 255)) || return 1 - done - return 0 + local ip="$1" + [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 + + local IFS='.' parts + read -ra parts <<< "$ip" + for part in "${parts[@]}"; do + (( part >= 0 && part <= 255 )) || return 1 + done + return 0 } lxc_status_changed() { @@ -206,162 +241,178 @@ fw_net_interface_changed() { # Get VM IPs using MAC addresses and ARP table get_vm_ips() { - local vmid=$1 - local ips="" - - # Check if VM is running - qm status "$vmid" | grep -q "status: running" || return - - # Get MAC addresses from VM configuration - local macs - macs=$(qm config "$vmid" | grep -E 'net[0-9]+' | grep -o -E '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') - - # Look up IPs from ARP table using MAC addresses - for mac in $macs; do - local ip - ip=$(arp -an | grep -i "$mac" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}') - if [ -n "$ip" ]; then - ips+="$ip " + local vmid=$1 ips="" macs found_ip=false + qm status "$vmid" 2>/dev/null | grep -q "status: running" || return + + macs=$(qm config "$vmid" 2>/dev/null | grep -E 'net[0-9]+' | grep -oE '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') + [[ -z "$macs" ]] && return + + for mac in $macs; do + local ip + ip=$(arp -an 2>/dev/null | grep -i "$mac" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}') + [[ -n "$ip" ]] && { ips+="$ip "; found_ip=true; } + done + + if ! $found_ip; then + local agent_ip + agent_ip=$(qm agent "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' || true) + [[ -n "$agent_ip" ]] && ips+="$agent_ip " fi - done - - echo "$ips" + + echo "${ips% }" } -# Update tags for container or VM +# Update tags update_tags() { - local type="$1" - local vmid="$2" - local config_cmd="pct" - [[ "$type" == "vm" ]] && config_cmd="qm" - - # Get current IPs - local current_ips_full - if [[ "$type" == "lxc" ]]; then - current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}') - else - current_ips_full=$(get_vm_ips "${vmid}") - fi - - # Parse current tags and get valid IPs - local current_tags=() - local next_tags=() - mapfile -t current_tags < <($config_cmd config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') - - for tag in "${current_tags[@]}"; do - is_valid_ipv4 "${tag}" || next_tags+=("${tag}") - done - - # Add valid IPs to tags - for ip in ${current_ips_full}; do - is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}" && next_tags+=("${ip}") - done - - # Update if changed - [[ "$(IFS=';'; echo "${current_tags[*]}")" != "$(IFS=';'; echo "${next_tags[*]}")" ]] && \ - $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" + local type="$1" vmid="$2" config_cmd="pct" + [[ "$type" == "vm" ]] && config_cmd="qm" + + local current_ips_full + if [[ "$type" == "lxc" ]]; then + current_ips_full=$(lxc-info -n "${vmid}" -i 2>/dev/null | grep -E "^IP:" | awk '{print $2}') + else + current_ips_full=$(get_vm_ips "${vmid}") + fi + [[ -z "$current_ips_full" ]] && return + + local current_tags=() next_tags=() current_ip_tags=() + mapfile -t current_tags < <($config_cmd config "${vmid}" 2>/dev/null | grep tags | awk '{print $2}' | sed 's/;/\n/g') + + # Separate IP and non-IP tags + for tag in "${current_tags[@]}"; do + if is_valid_ipv4 "${tag}" || [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + current_ip_tags+=("${tag}") + else + next_tags+=("${tag}") + fi + done + + local formatted_ips=() needs_update=false added_ips=() + for ip in ${current_ips_full}; do + if is_valid_ipv4 "$ip" && ip_in_cidrs "$ip" "${CIDR_LIST[*]}"; then + local formatted_ip=$(format_ip_tag "$ip") + formatted_ips+=("$formatted_ip") + if [[ ! " ${current_ip_tags[*]} " =~ " ${formatted_ip} " ]]; then + needs_update=true + added_ips+=("$formatted_ip") + next_tags+=("$formatted_ip") + fi + fi + done + + [[ ${#formatted_ips[@]} -eq 0 ]] && return + + # Add existing IP tags that are still valid + for tag in "${current_ip_tags[@]}"; do + if [[ " ${formatted_ips[*]} " =~ " ${tag} " ]]; then + if [[ ! " ${next_tags[*]} " =~ " ${tag} " ]]; then + next_tags+=("$tag") + fi + fi + done + + if [[ "$needs_update" == true ]]; then + echo "${type^} ${vmid}: adding IP tags: ${added_ips[*]}" + $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null + elif [[ ${#current_ip_tags[@]} -gt 0 ]]; then + echo "${type^} ${vmid}: IP tags already set: ${current_ip_tags[*]}" + else + echo "${type^} ${vmid}: setting initial IP tags: ${formatted_ips[*]}" + $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${formatted_ips[*]}")" &>/dev/null + fi } # Check if status changed -check_status_changed() { - local type="$1" - local current_status - - case "$type" in - "lxc") - current_status=$(pct list 2>/dev/null) - [[ "${last_lxc_status}" == "${current_status}" ]] && return 1 - last_lxc_status="${current_status}" - ;; - "vm") - current_status=$(qm list 2>/dev/null) - [[ "${last_vm_status}" == "${current_status}" ]] && return 1 - last_vm_status="${current_status}" - ;; - "fw") - current_status=$(ifconfig | grep "^fw") - [[ "${last_net_interface}" == "${current_status}" ]] && return 1 - last_net_interface="${current_status}" - ;; - esac - return 0 +check_status() { + local type="$1" current + case "$type" in + "lxc") current=$(pct list 2>/dev/null | grep -v VMID) ;; + "vm") current=$(qm list 2>/dev/null | grep -v VMID) ;; + "fw") current=$(ifconfig 2>/dev/null | grep "^fw") ;; + esac + local last_var="last_${type}_status" + [[ "${!last_var}" == "$current" ]] && return 1 + eval "$last_var='$current'" + return 0 } +# Update all instances +update_all() { + local type="$1" list_cmd="pct" vmids count=0 + [[ "$type" == "vm" ]] && list_cmd="qm" + + vmids=$($list_cmd list 2>/dev/null | grep -v VMID | awk '{print $1}') + for vmid in $vmids; do ((count++)); done + + echo "Found ${count} running ${type}s" + [[ $count -eq 0 ]] && return + + for vmid in $vmids; do + update_tags "$type" "$vmid" + done +} + +# Main check function check() { - current_time=$(date +%s) - - # Check LXC status - time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time)) - if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking lxc status..." - last_lxc_status_check_time=${current_time} - if check_status_changed "lxc"; then - update_tags "lxc" "${vmid}" - last_update_lxc_time=${current_time} + local current_time changes_detected=false + current_time=$(date +%s) + + for type in "lxc" "vm"; do + local interval_var="${type^^}_STATUS_CHECK_INTERVAL" + local last_check_var="last_${type}_check_time" + local last_update_var="last_update_${type}_time" + + if [[ "${!interval_var}" -gt 0 ]] && (( current_time - ${!last_check_var} >= ${!interval_var} )); then + echo "Checking ${type^^} status..." + eval "${last_check_var}=\$current_time" + if check_status "$type"; then + changes_detected=true + update_all "$type" + eval "${last_update_var}=\$current_time" + fi + fi + + if (( current_time - ${!last_update_var} >= FORCE_UPDATE_INTERVAL )); then + echo "Force updating ${type} tags..." + changes_detected=true + update_all "$type" + eval "${last_update_var}=\$current_time" + fi + done + + if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] && \ + (( current_time - last_fw_check_time >= FW_NET_INTERFACE_CHECK_INTERVAL )); then + echo "Checking network interfaces..." + last_fw_check_time=$current_time + if check_status "fw"; then + changes_detected=true + update_all "lxc" + update_all "vm" + last_update_lxc_time=$current_time + last_update_vm_time=$current_time + fi fi - fi - - # Check VM status - time_since_last_vm_status_check=$((current_time - last_vm_status_check_time)) - if [[ "${VM_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking vm status..." - last_vm_status_check_time=${current_time} - if check_status_changed "vm"; then - update_tags "vm" "${vmid}" - last_update_vm_time=${current_time} - fi - fi - - # Check network interface changes - time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time)) - if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then - echo "Checking fw net interface..." - last_fw_net_interface_check_time=${current_time} - if check_status_changed "fw"; then - update_tags "lxc" "${vmid}" - update_tags "vm" "${vmid}" - last_update_lxc_time=${current_time} - last_update_vm_time=${current_time} - fi - fi - - # Force update if needed - for type in "lxc" "vm"; do - local last_update_var="last_update_${type}_time" - local time_since_last_update=$((current_time - ${!last_update_var})) - if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating ${type} iptags..." - case "$type" in - "lxc") update_tags "lxc" "${vmid}" ;; - "vm") update_tags "vm" "${vmid}" ;; - esac - eval "${last_update_var}=${current_time}" - fi - done + + $changes_detected || echo "No changes detected in system status" } # Initialize time variables -last_lxc_status_check_time=0 -last_vm_status_check_time=0 -last_fw_net_interface_check_time=0 -last_update_lxc_time=0 -last_update_vm_time=0 +declare -g last_lxc_status="" last_vm_status="" last_fw_status="" +declare -g last_lxc_check_time=0 last_vm_check_time=0 last_fw_check_time=0 +declare -g last_update_lxc_time=0 last_update_vm_time=0 -# main: Set the IP tags for all LXC containers and VMs +# Main loop main() { - while true; do - check - sleep "${LOOP_INTERVAL}" - done + while true; do + check + sleep "${LOOP_INTERVAL:-$DEFAULT_CHECK_INTERVAL}" + done } main EOF chmod +x /opt/iptag/iptag - + # Update service file cat </lib/systemd/system/iptag.service [Unit] @@ -376,7 +427,7 @@ Restart=always [Install] WantedBy=multi-user.target EOF - + systemctl daemon-reload &>/dev/null systemctl enable -q --now iptag.service &>/dev/null msg_ok "Updated IP-Tag Scripts" @@ -387,15 +438,17 @@ if check_service_exists; then while true; do read -p "IP-Tag service is already installed. Do you want to update it? (y/n): " yn case $yn in - [Yy]*) - update_installation - exit 0 - ;; - [Nn]*) - msg_error "Installation cancelled." - exit 0 - ;; - *) msg_error "Please answer yes or no." ;; + [Yy]*) + update_installation + exit 0 + ;; + [Nn]*) + msg_error "Installation cancelled." + exit 0 + ;; + *) + msg_error "Please answer yes or no." + ;; esac done fi @@ -403,16 +456,20 @@ fi while true; do read -p "This will install ${APP} on ${hostname}. Proceed? (y/n): " yn case $yn in - [Yy]*) break ;; + [Yy]*) + break + ;; [Nn]*) - msg_error "Installation cancelled." - exit - ;; - *) msg_error "Please answer yes or no." ;; + msg_error "Installation cancelled." + exit + ;; + *) + msg_error "Please answer yes or no." + ;; esac done -if ! pveversion | grep -Eq "pve-manager/8\.[0-3](\.[0-9]+)*"; then +if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then msg_error "This version of Proxmox Virtual Environment is not supported" msg_error "⚠️ Requires Proxmox Virtual Environment Version 8.0 or later." msg_error "Exiting..." @@ -451,6 +508,12 @@ CIDR_LIST=( 100.64.0.0/10 ) +# Tag format options: +# - "full": full IP address (e.g., 192.168.0.100) +# - "last_octet": only the last octet (e.g., 100) +# - "last_two_octets": last two octets (e.g., 0.100) +TAG_FORMAT="full" + # Interval settings (in seconds) LOOP_INTERVAL=60 VM_STATUS_CHECK_INTERVAL=60 @@ -488,17 +551,65 @@ ip_to_int() { ip_in_cidr() { local ip="$1" local cidr="$2" - local ip_int=$(ip_to_int "$ip") - local netmask_int=$(ip_to_int "$(ipcalc -b "$cidr" | grep Broadcast | awk '{print $2}')") - [[ $((ip_int & netmask_int)) -eq $((ip_int & netmask_int)) ]] && return 0 || return 1 + + # Use ipcalc with the -c option (check), which returns 0 if the IP is in the network + if ipcalc -c "$ip" "$cidr" >/dev/null 2>&1; then + # Get network address and mask from CIDR + local network prefix + network=$(echo "$cidr" | cut -d/ -f1) + prefix=$(echo "$cidr" | cut -d/ -f2) + + # Check if IP is in the network + local ip_a ip_b ip_c ip_d net_a net_b net_c net_d + IFS=. read -r ip_a ip_b ip_c ip_d <<< "$ip" + IFS=. read -r net_a net_b net_c net_d <<< "$network" + + # Check octets match based on prefix length + local result=0 + if (( prefix >= 8 )); then + [[ "$ip_a" != "$net_a" ]] && result=1 + fi + if (( prefix >= 16 )); then + [[ "$ip_b" != "$net_b" ]] && result=1 + fi + if (( prefix >= 24 )); then + [[ "$ip_c" != "$net_c" ]] && result=1 + fi + + return $result + fi + + return 1 +} + +# Format IP address according to the configuration +format_ip_tag() { + local ip="$1" + local format="${TAG_FORMAT:-full}" + + case "$format" in + "last_octet") + echo "${ip##*.}" + ;; + "last_two_octets") + echo "${ip#*.*.}" + ;; + *) + echo "$ip" + ;; + esac } # Check if IP is in any CIDRs ip_in_cidrs() { local ip="$1" - local cidrs=() - mapfile -t cidrs < <(echo "$2" | tr ' ' '\n') - for cidr in "${cidrs[@]}"; do + local cidrs="$2" + + # Check that cidrs is not empty + [[ -z "$cidrs" ]] && return 1 + + local IFS=' ' + for cidr in $cidrs; do ip_in_cidr "$ip" "$cidr" && return 0 done return 1 @@ -550,23 +661,23 @@ fw_net_interface_changed() { get_vm_ips() { local vmid=$1 local ips="" - + # Check if VM is running - qm status "$vmid" | grep -q "status: running" || return - + qm status "$vmid" 2>/dev/null | grep -q "status: running" || return + # Get MAC addresses from VM configuration local macs - macs=$(qm config "$vmid" | grep -E 'net[0-9]+' | grep -o -E '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') - + macs=$(qm config "$vmid" 2>/dev/null | grep -E 'net[0-9]+' | grep -o -E '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') + # Look up IPs from ARP table using MAC addresses for mac in $macs; do local ip - ip=$(arp -an | grep -i "$mac" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}') + ip=$(arp -an 2>/dev/null | grep -i "$mac" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}') if [ -n "$ip" ]; then ips+="$ip " fi done - + echo "$ips" } @@ -576,85 +687,128 @@ update_tags() { local vmid="$2" local config_cmd="pct" [[ "$type" == "vm" ]] && config_cmd="qm" - + # Get current IPs local current_ips_full if [[ "$type" == "lxc" ]]; then - current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}') + # Redirect error output to suppress AppArmor warnings + current_ips_full=$(lxc-info -n "${vmid}" -i 2>/dev/null | grep -E "^IP:" | awk '{print $2}') else current_ips_full=$(get_vm_ips "${vmid}") fi - + # Parse current tags and get valid IPs local current_tags=() local next_tags=() - mapfile -t current_tags < <($config_cmd config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') - + mapfile -t current_tags < <($config_cmd config "${vmid}" 2>/dev/null | grep tags | awk '{print $2}' | sed 's/;/\n/g') + for tag in "${current_tags[@]}"; do - is_valid_ipv4 "${tag}" || next_tags+=("${tag}") + # Skip tag if it looks like an IP (full or partial) + if ! is_valid_ipv4 "${tag}" && ! [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + next_tags+=("${tag}") + fi done - + # Add valid IPs to tags + local added_ips=() + local skipped_ips=() + for ip in ${current_ips_full}; do - is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}" && next_tags+=("${ip}") + if is_valid_ipv4 "${ip}"; then + if ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then + local formatted_ip=$(format_ip_tag "$ip") + next_tags+=("${formatted_ip}") + added_ips+=("${formatted_ip}") + else + skipped_ips+=("${ip}") + fi + fi done - + + # Log only if there are changes + if [ ${#added_ips[@]} -gt 0 ]; then + echo "${type^} ${vmid}: added IP tags: ${added_ips[*]}" + fi + # Update if changed - [[ "$(IFS=';'; echo "${current_tags[*]}")" != "$(IFS=';'; echo "${next_tags[*]}")" ]] && \ - $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" + if [[ "$(IFS=';'; echo "${current_tags[*]}")" != "$(IFS=';'; echo "${next_tags[*]}")" ]]; then + $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null + fi +} + +# Check if status changed +check_status_changed() { + local type="$1" + local current_status + + case "$type" in + "lxc") + current_status=$(pct list 2>/dev/null | grep -v VMID) + [[ "${last_lxc_status}" == "${current_status}" ]] && return 1 + last_lxc_status="${current_status}" + ;; + "vm") + current_status=$(qm list 2>/dev/null | grep -v VMID) + [[ "${last_vm_status}" == "${current_status}" ]] && return 1 + last_vm_status="${current_status}" + ;; + "fw") + current_status=$(ifconfig 2>/dev/null | grep "^fw") + [[ "${last_net_interface}" == "${current_status}" ]] && return 1 + last_net_interface="${current_status}" + ;; + esac + return 0 } check() { current_time=$(date +%s) - + # Check LXC status time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time)) if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking lxc status..." + echo "Checking LXC status..." last_lxc_status_check_time=${current_time} if check_status_changed "lxc"; then - update_tags "lxc" "${vmid}" + update_all_tags "lxc" last_update_lxc_time=${current_time} fi fi - + # Check VM status time_since_last_vm_status_check=$((current_time - last_vm_status_check_time)) if [[ "${VM_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking vm status..." + echo "Checking VM status..." last_vm_status_check_time=${current_time} if check_status_changed "vm"; then - update_tags "vm" "${vmid}" + update_all_tags "vm" last_update_vm_time=${current_time} fi fi - + # Check network interface changes time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time)) if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \ && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then - echo "Checking fw net interface..." + echo "Checking network interfaces..." last_fw_net_interface_check_time=${current_time} if check_status_changed "fw"; then - update_tags "lxc" "${vmid}" - update_tags "vm" "${vmid}" + update_all_tags "lxc" + update_all_tags "vm" last_update_lxc_time=${current_time} last_update_vm_time=${current_time} fi fi - + # Force update if needed for type in "lxc" "vm"; do local last_update_var="last_update_${type}_time" local time_since_last_update=$((current_time - ${!last_update_var})) if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating ${type} iptags..." - case "$type" in - "lxc") update_tags "lxc" "${vmid}" ;; - "vm") update_tags "vm" "${vmid}" ;; - esac + echo "Force updating ${type} tags..." + update_all_tags "$type" eval "${last_update_var}=${current_time}" fi done