2025-04-19 23:00:43 +05:00
#!/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
# 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 =============== #
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 "
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 " 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 " 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 = '.' parts
read -ra parts <<< " $ip "
for part in " ${ parts [@] } " ; do
( ( part >= 0 && part <= 255 ) ) || return 1
done
return 0
}
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 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
echo " ${ ips % } "
}
# Update tags
update_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( ) {
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( ) {
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
$changes_detected || echo "No changes detected in system status"
}
# Initialize time variables
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 loop
main( ) {
while true; do
check
sleep " ${ LOOP_INTERVAL :- $DEFAULT_CHECK_INTERVAL } "
done
}
main
EOF
chmod +x /opt/iptag/iptag
# Update service file
cat <<EOF >/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-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..."
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 <<EOF >/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
)
# 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
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 =============== #
2025-06-17 17:56:27 +03:00
readonly CONFIG_FILE = "/opt/iptag/iptag.conf"
readonly DEFAULT_TAG_FORMAT = "full"
readonly DEFAULT_CHECK_INTERVAL = 60
2025-04-19 23:00:43 +05:00
# Load the configuration file if it exists
if [ -f " $CONFIG_FILE " ] ; then
2025-06-17 17:56:27 +03:00
# shellcheck source=./iptag.conf
source " $CONFIG_FILE "
2025-04-19 23:00:43 +05:00
fi
# Convert IP to integer for comparison
ip_to_int( ) {
2025-06-17 17:56:27 +03:00
local ip = " $1 "
local a b c d
IFS = . read -r a b c d <<< " ${ ip } "
echo " $(( a << 24 | b << 16 | c << 8 | d)) "
2025-04-19 23:00:43 +05:00
}
# Check if IP is in CIDR
ip_in_cidr( ) {
2025-06-17 17:56:27 +03:00
local ip = " $1 " cidr = " $2 "
ipcalc -c " $ip " " $cidr " >/dev/null 2>& 1 || return 1
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
local network prefix ip_parts net_parts
2025-04-19 23:00:43 +05:00
network = $( echo " $cidr " | cut -d/ -f1)
prefix = $( echo " $cidr " | cut -d/ -f2)
2025-06-17 17:56:27 +03:00
IFS = . read -r -a ip_parts <<< " $ip "
IFS = . read -r -a net_parts <<< " $network "
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
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
2025-04-19 23:00:43 +05:00
}
# Format IP address according to the configuration
format_ip_tag( ) {
2025-06-17 17:56:27 +03:00
local ip = " $1 "
local format = " ${ TAG_FORMAT :- $DEFAULT_TAG_FORMAT } "
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
case " $format " in
"last_octet" ) echo " ${ ip ##*. } " ; ;
"last_two_octets" ) echo " ${ ip #*.*. } " ; ;
*) echo " $ip " ; ;
esac
2025-04-19 23:00:43 +05:00
}
# Check if IP is in any CIDRs
ip_in_cidrs( ) {
2025-06-17 17:56:27 +03:00
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
2025-04-19 23:00:43 +05:00
}
# Check if IP is valid
is_valid_ipv4( ) {
2025-06-17 17:56:27 +03:00
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
2025-04-19 23:00:43 +05:00
return 0
}
# Get VM IPs using MAC addresses and ARP table
get_vm_ips( ) {
2025-06-17 17:56:27 +03:00
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 "
2025-04-19 23:00:43 +05:00
fi
2025-06-17 17:56:27 +03:00
echo " ${ ips % } "
2025-04-19 23:00:43 +05:00
}
# Update tags for container or VM
update_tags( ) {
2025-06-17 17:56:27 +03:00
local type = " $1 " vmid = " $2 " config_cmd = "pct"
[ [ " $type " = = "vm" ] ] && config_cmd = "qm"
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
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 } " )
2025-04-19 23:00:43 +05:00
fi
2025-06-17 17:56:27 +03:00
[ [ -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
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
# 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
2025-04-19 23:00:43 +05:00
fi
2025-06-17 17:56:27 +03:00
}
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
# Update all instances of specified type
update_all_tags( ) {
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
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
for vmid in $vmids ; do
update_tags " $type " " $vmid "
done
2025-04-19 23:00:43 +05:00
}
# Check if status changed
check_status_changed( ) {
2025-06-17 17:56:27 +03:00
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
2025-04-19 23:00:43 +05:00
}
2025-06-17 17:56:27 +03:00
# Main check function
2025-04-19 23:00:43 +05:00
check( ) {
2025-06-17 17:56:27 +03:00
local current_time changes_detected = false
current_time = $( date +%s)
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
# Check LXC status
local time_since_last_lxc_check = $(( current_time - last_lxc_status_check_time))
if [ [ " ${ LXC_STATUS_CHECK_INTERVAL :- 60 } " -gt 0 ] ] && \
[ [ " ${ time_since_last_lxc_check } " -ge " ${ LXC_STATUS_CHECK_INTERVAL :- 60 } " ] ] ; then
echo "Checking LXC status..."
last_lxc_status_check_time = ${ current_time }
if check_status_changed "lxc" ; then
changes_detected = true
update_all_tags "lxc"
last_update_lxc_time = ${ current_time }
fi
2025-04-19 23:00:43 +05:00
fi
2025-06-17 17:56:27 +03:00
# Check VM status
local time_since_last_vm_check = $(( current_time - last_vm_status_check_time))
if [ [ " ${ VM_STATUS_CHECK_INTERVAL :- 60 } " -gt 0 ] ] && \
[ [ " ${ time_since_last_vm_check } " -ge " ${ VM_STATUS_CHECK_INTERVAL :- 60 } " ] ] ; then
echo "Checking VM status..."
last_vm_status_check_time = ${ current_time }
if check_status_changed "vm" ; then
changes_detected = true
update_all_tags "vm"
last_update_vm_time = ${ current_time }
fi
2025-04-19 23:00:43 +05:00
fi
2025-06-17 17:56:27 +03:00
# Check network interface changes
local time_since_last_fw_check = $(( current_time - last_fw_net_interface_check_time))
if [ [ " ${ FW_NET_INTERFACE_CHECK_INTERVAL :- 60 } " -gt 0 ] ] && \
[ [ " ${ time_since_last_fw_check } " -ge " ${ FW_NET_INTERFACE_CHECK_INTERVAL :- 60 } " ] ] ; then
echo "Checking network interfaces..."
last_fw_net_interface_check_time = ${ current_time }
if check_status_changed "fw" ; then
changes_detected = true
update_all_tags "lxc"
update_all_tags "vm"
last_update_lxc_time = ${ current_time }
last_update_vm_time = ${ current_time }
fi
2025-04-19 23:00:43 +05:00
fi
2025-06-17 17:56:27 +03:00
# 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 :- 1800 } ] ] ; then
echo " Force updating ${ type } tags... "
changes_detected = true
update_all_tags " $type "
eval " ${ last_update_var } = ${ current_time } "
fi
done
$changes_detected || echo "No changes detected in system status"
2025-04-19 23:00:43 +05:00
}
# Initialize time variables
2025-06-17 17:56:27 +03:00
declare -g last_lxc_status = "" last_vm_status = "" last_fw_status = ""
declare -g last_lxc_status_check_time = 0 last_vm_status_check_time = 0 last_fw_net_interface_check_time = 0
declare -g last_update_lxc_time = 0 last_update_vm_time = 0
2025-04-19 23:00:43 +05:00
2025-06-17 17:56:27 +03:00
# Main loop
2025-04-19 23:00:43 +05:00
main( ) {
2025-06-17 17:56:27 +03:00
while true; do
check
sleep " ${ LOOP_INTERVAL :- $DEFAULT_CHECK_INTERVAL } "
done
2025-04-19 23:00:43 +05:00
}
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 <<EOF >/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 "