*=$metric?>*
:eth_ipv4_default_gateway_help:
@@ -213,7 +213,7 @@ _(IPv6 address)_:
_(IPv6 default gateway)_:
:
" pattern="=$validIP6?>" title="_(IPv6 address nnnn:xxxx::yyyy)_">
-
" class="slim">
*=$metric?>*
+
" class="slim">
*=$metric?>*
:eth_ipv6_default_gateway_help:
@@ -293,7 +293,7 @@ _(IPv4 address)_:
diff --git a/emhttp/plugins/dynamix/include/RoutingTable.php b/emhttp/plugins/dynamix/include/RoutingTable.php
index 2bfa73efb..1741cbc99 100644
--- a/emhttp/plugins/dynamix/include/RoutingTable.php
+++ b/emhttp/plugins/dynamix/include/RoutingTable.php
@@ -26,8 +26,8 @@ case 'Add Route':
if ($gateway && $route) exec("/etc/rc.d/rc.inet1 ".escapeshellarg("{$gateway}_{$route}_{$metric}_add"));
break;
default:
- exec("ip -4 route show table all|grep -Pv '^(127\\.0\\.0\\.0)|table local|unreachable'",$ipv4);
- exec("ip -6 route show table all|grep -Pv '^([am:]|(f[ef][0-9][0-9])::)|table local|unreachable'",$ipv6);
+ exec("ip -4 route show table all | grep -Pv '^(127\\.0\\.0\\.0)|table local|unreachable|linkdown|broadcast'",$ipv4);
+ exec("ip -6 route show table all | grep -Pv '^([am:]|(f[ef][0-9][0-9])::)|table local|unreachable|linkdown'",$ipv6);
foreach ($ipv4 as $info) {
$cell = explode(' ',$info);
$route = $cell[0];
diff --git a/emhttp/plugins/dynamix/scripts/reload_services b/emhttp/plugins/dynamix/scripts/reload_services
index a1fe713cb..babe9c7c1 100755
--- a/emhttp/plugins/dynamix/scripts/reload_services
+++ b/emhttp/plugins/dynamix/scripts/reload_services
@@ -8,9 +8,6 @@ queue(){
atq | grep -Pom1 '^\d+'
}
-# delayed execution
-sleep ${1:-1}
-
JOB=$(queue)
if [[ -n $JOB ]]; then
atrm $JOB 2>/dev/null
diff --git a/emhttp/plugins/dynamix/scripts/update_services b/emhttp/plugins/dynamix/scripts/update_services
index a89950733..4fbebbb2a 100755
--- a/emhttp/plugins/dynamix/scripts/update_services
+++ b/emhttp/plugins/dynamix/scripts/update_services
@@ -15,6 +15,6 @@ else
log "no queued job present"
fi
-echo "/usr/local/emhttp/webGui/scripts/reload_services ${1:-1}" | at -M now 2>/dev/null
+echo "sleep ${1:-1}; /usr/local/emhttp/webGui/scripts/reload_services" | at -M now 2>/dev/null
log "queue new job $(queue), wait for ${1:-1}s"
exit 0
diff --git a/etc/rc.d/rc.docker b/etc/rc.d/rc.docker
index 6a9719d35..8d9b5221f 100755
--- a/etc/rc.d/rc.docker
+++ b/etc/rc.d/rc.docker
@@ -10,7 +10,7 @@
# VMs, bare metal, OpenStack clusters, public clouds and more.
#
# LimeTech - modified for Unraid OS
-# Bergware - modified for Unraid OS, January 2025
+# Bergware - modified for Unraid OS, May 2025
DAEMON="Docker daemon"
UNSHARE="/usr/bin/unshare"
@@ -35,6 +35,22 @@ TMP=/var/tmp/network.tmp
# run & log functions
. /etc/rc.d/rc.runlog
+# return interface index
+index(){
+ cat $SYSTEM/$1/ifindex 2>/dev/null
+}
+
+# return active interface
+active(){
+ if [[ -e $SYSTEM/${1/eth/br} ]]; then
+ echo ${1/eth/br}
+ elif [[ -e $SYSTEM/${1/eth/bond} ]]; then
+ echo ${1/eth/bond}
+ else
+ echo $1
+ fi
+}
+
# wait for interface to go up
carrier(){
local n e
@@ -49,20 +65,13 @@ carrier(){
# initialize docker settings
docker_read_options(){
# determine active port name
- [[ -e $SYSTEM/bond0 ]] && PORT=bond0 || PORT=eth0
- [[ -e $SYSTEM/br0 ]] && PORT=br0
+ PORT=$(active eth0)
[[ ! $(carrier $PORT) && $(carrier wlan0 1) ]] && PORT=wlan0
# Set defaults used by the docker daemon
if [[ -f $DOCKER_CFG ]]; then
for NIC in $NICS; do
- if [[ ${NIC:0:3} == eth ]]; then
- if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
- NIC=${NIC/eth/br}
- elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
- NIC=${NIC/eth/bond}
- fi
- fi
+ [[ ${NIC:0:3} == eth ]] && NIC=$(active $NIC)
CFG=($(grep -Pom2 "_SUBNET_|_${NIC^^}(_[0-9]+)?=" $DOCKER_CFG))
if [[ ${CFG[0]} == _SUBNET_ && -z ${CFG[1]} ]]; then
# interface has changed, update configuration
@@ -103,7 +112,7 @@ docker_read_options(){
[[ -n $MTU && $MTU -ne 1500 ]] && DOCKER_OPTS="--mtu=$MTU $DOCKER_OPTS"
# Enable IPv6 for docker bridge network
- if [[ -n $(ip -6 route show default dev $PORT) ]]; then
+ if [[ -n $(ip -6 route show to default dev $PORT) ]]; then
DOCKER0='fd17::/64'
DOCKER_OPTS="--ipv6 --fixed-cidr-v6=$DOCKER0 $DOCKER_OPTS"
IPV6_FORWARD=${IPV6_FORWARD:=accept}
@@ -165,6 +174,8 @@ driver(){
ATTACH='macvlan'
MODE='bridge'
fi
+ # wlan0 has forced ipvlan
+ [[ $1 == wlan && $2 == forced ]] && ATTACH=ipvlan
}
# Custom networks
@@ -172,6 +183,11 @@ network(){
docker network ls --filter driver="$1" --format='{{.Name}}' 2>/dev/null | grep -P "^[a-z]+$2(\$|\.)" | tr '\n' ' '
}
+# Does the ipv4 address exist?
+ipv4_exist(){
+ ip -4 -br addr show to $2 dev $1 | awk '{print $3;exit}'
+}
+
# Is container running?
container_running(){
local CONTAINER
@@ -213,9 +229,9 @@ container_add_route(){
local NET=${CT[1]#*[}
local LAN=${NET%:*}
if [[ $PID -gt 0 && "eth0 br0 bond0 wlan0" =~ $LAN ]]; then
- local THISIP=$(ip -4 -br addr show dev $LAN scope global | awk '{print $3;exit}')
+ local THISIP=$(ip -4 -br addr show scope global primary dev $LAN | awk '{print $3;exit}')
for CFG in /etc/wireguard/wg*.cfg ; do
- local NETWORK=$(ip -4 show route dev $LAN $THISIP | awk '{print $1;exit}')
+ local NETWORK=$(ip -4 route show to $THISIP dev $LAN | awk '{print $1;exit}')
[[ -n $NETWORK ]] && nsenter -n -t $PID ip -4 route add $NETWORK via ${THISIP%/*} dev $LAN 2>/dev/null
done
fi
@@ -246,8 +262,7 @@ docker_network_start(){
if ! docker_running; then return 1; fi
# get container settings for custom networks to reconnect later
declare -A NETRESTORE CTRESTORE
- CONTAINERS=$(docker container ls -a --format='{{.Names}}' | tr '\n' ' ')
- for CONTAINER in $CONTAINERS; do
+ for CONTAINER in $(docker container ls -a --format='{{.Names}}'); do
# the file case (due to fat32) might be different so use find to match
XMLFILE=$(find /boot/config/plugins/dockerMan/templates-user -maxdepth 1 -iname my-${CONTAINER}.xml)
if [[ -n $XMLFILE ]]; then
@@ -255,13 +270,7 @@ docker_network_start(){
MAIN=
# update custom network reference (if changed)
for NIC in $NICS; do
- if [[ ${NIC:0:3} == eth ]]; then
- if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
- NIC=${NIC/eth/br}
- elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
- NIC=${NIC/eth/bond}
- fi
- fi
+ [[ ${NIC:0:3} == eth ]] && NIC=$(active $NIC)
X=${NIC//[^0-9]/}
REF=$(grep -Pom1 "
\K(br|bond|eth|wlan)$X" $XMLFILE)
[[ $X == 0 && ! $(carrier $NIC 1) ]] && continue
@@ -307,24 +316,18 @@ docker_network_start(){
done
# detach custom networks
for NIC in $NICS; do
- if [[ ${NIC:0:3} == eth ]]; then
- if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
- NIC=${NIC/eth/br}
- elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
- NIC=${NIC/eth/bond}
- fi
- fi
+ [[ ${NIC:0:3} == eth ]] && NIC=$(active $NIC)
X=${NIC//[^0-9]/}
driver ${NIC//[0-9]/}
for NETWORK in $(network $DETACH $X); do
- [[ $STOCK =~ ${NETWORK%%[0-9]*} || $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK >/dev/null
+ [[ $STOCK =~ ${NETWORK%%[0-9]*} || $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK &>/dev/null
done
# get existing custom networks
for NETWORK in $(network $ATTACH $X); do
if [[ $STOCK =~ ${NETWORK%%[0-9]*} ]]; then
- [[ $EXCLUDE =~ "$NETWORK " || ! $ACTIVE =~ "$NETWORK " ]] && docker network rm $NETWORK >/dev/null
+ [[ $EXCLUDE =~ "$NETWORK " || ! $ACTIVE =~ "$NETWORK " ]] && docker network rm $NETWORK &>/dev/null
else
- [[ $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK >/dev/null
+ [[ $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK &>/dev/null
fi
done
NETWORKS=$(network $ATTACH $X)
@@ -336,27 +339,27 @@ docker_network_start(){
AUTO=${NETWORK/./_}
AUTO=DOCKER_AUTO_${AUTO^^}
if [[ ${!AUTO} == no ]]; then
- [[ $NETWORKS =~ "$NETWORK " ]] && docker network rm $NETWORK >/dev/null
+ [[ $NETWORKS =~ "$NETWORK " ]] && docker network rm $NETWORK &>/dev/null
continue
fi
# add auto defined networks
SUBNET=; GATEWAY=; SERVER=; RANGE=;
- [[ -z ${!AUTO} || ${!AUTO} =~ "4" ]] && IPV4=$(ip -4 -br addr show $NETWORK scope global | awk '{print $3;exit}') || IPV4=
+ [[ -z ${!AUTO} || ${!AUTO} =~ "4" ]] && IPV4=$(ip -4 -br addr show scope global primary dev $NETWORK | awk '{print $3;exit}') || IPV4=
if [[ -n $IPV4 ]]; then
- SUBNET=$(ip -4 route show $IPV4 dev $NETWORK | awk '{print $1;exit}')
+ SUBNET=$(ip -4 route show to $IPV4 dev $NETWORK | awk '{print $1;exit}')
SERVER=${IPV4%/*}
DHCP=${NETWORK/./_}
DHCP=DOCKER_DHCP_${DHCP^^}
RANGE=${!DHCP}
- GATEWAY=$(ip -4 route show default dev $NETWORK | awk '{print $3;exit}')
+ GATEWAY=$(ip -4 route show to default dev $NETWORK | awk '{print $3;exit}')
fi
SUBNET6=; GATEWAY6=; SERVER6=;
- [[ -z ${!AUTO} || ${!AUTO} =~ "6" ]] && IPV6=$(ip -6 -br addr show $NETWORK scope global -temporary -deprecated | awk '{print $3;exit}') || IPV6=
+ # get IPv6 address - ignore any /128 networks
+ [[ -z ${!AUTO} || ${!AUTO} =~ "6" ]] && IPV6=$(ip -6 -br addr show scope global primary -deprecated dev $NETWORK | awk -v RS='[[:space:]]+' '(NR>2){print}' | grep -Pvm1 '^.+/128|^$') || IPV6=
if [[ -n $IPV6 ]]; then
- # get IPV6 subnet, preset to /64 if single host address is given
- [[ ${IPV6#*/} == 128 ]] && SUBNET6=$(echo $IPV6 | sed -r 's/^([^:]+):([^:]+):([^:]+):([^:]+).*$/\1:\2:\3:\4::\/64/') || SUBNET6=$(ip -6 route show $IPV6 dev $NETWORK | awk '{print $1;exit}')
+ SUBNET6=$(ip -6 route show to $IPV6 dev $NETWORK | awk '{print $1;exit}')
SERVER6=${IPV6%/*}
- GATEWAY6=$(ip -6 route show default dev $NETWORK | awk '{print $3;exit}')
+ GATEWAY6=$(ip -6 route show to default dev $NETWORK | awk '{print $3;exit}')
# replace link local address for first address in subnet
[[ ${GATEWAY6:0:4} == fe80 ]] && GATEWAY6=${SUBNET6%/*}1
fi
@@ -378,38 +381,7 @@ docker_network_start(){
GATEWAY6=${!GATEWAY6}
SERVER6=;
fi
- # custom network already existing and changed?
- if [[ $NETWORKS =~ "$NETWORK " ]]; then
- UPDATE=;
- SUBNETS=($(docker network inspect --format='{{range .IPAM.Config}}{{.Subnet}} {{end}}' $NETWORK 2>/dev/null))
- RANGES=($(docker network inspect --format='{{range .IPAM.Config}}{{.IPRange}} {{end}}' $NETWORK 2>/dev/null))
- GATEWAYS=($(docker network inspect --format='{{range .IPAM.Config}}{{.Gateway}} {{end}}' $NETWORK 2>/dev/null))
- SERVERS=($(docker network inspect --format='{{range .IPAM.Config}}{{range $IPAddr := .AuxiliaryAddresses}}{{$IPAddr}}{{end}} {{end}}' $NETWORK 2>/dev/null))
- # distribute ipv4 and ipv6 assignments
- [[ ${SUBNETS[0]} =~ '.' ]] && SUBNET0=${SUBNETS[0]} || SUBNET1=${SUBNETS[0]}
- [[ -n ${SUBNETS[1]} && ${SUBNETS[1]} =~ '.' ]] && SUBNET0=${SUBNETS[1]} || SUBNET1=${SUBNETS[1]}
- [[ ${RANGES[0]} =~ '.' ]] && RANGE0=${RANGES[0]} || RANGE1=${RANGES[0]}
- [[ -n ${RANGES[1]} && ${RANGES[1]} =~ '.' ]] && RANGE0=${RANGES[1]} || RANGE1=${RANGES[1]}
- [[ ${GATEWAYS[0]} =~ '.' ]] && GATEWAY0=${GATEWAYS[0]} || GATEWAY1=${GATEWAYS[0]}
- [[ -n ${GATEWAYS[1]} && ${GATEWAYS[1]} =~ '.' ]] && GATEWAY0=${GATEWAYS[1]} || GATEWAY1=${GATEWAYS[1]}
- [[ ${SERVERS[0]} =~ '.' ]] && SERVER0=${SERVERS[0]} || SERVER1=${SERVERS[0]}
- [[ -n ${SERVERS[1]} && ${SERVERS[1]} =~ '.' ]] && SERVER0=${SERVERS[1]} || SERVER1=${SERVERS[1]}
- # check for changes
- [[ $SUBNET != $SUBNET0 || $SUBNET6 != $SUBNET1 ]] && UPDATE=1
- [[ $RANGE != $RANGE0 ]] && UPDATE=1
- [[ (-n $GATEWAY && $GATEWAY != $GATEWAY0) || (-n $GATEWAY6 && $GATEWAY6 != $GATEWAY1) ]] && UPDATE=1
- [[ (-n $SERVER && $SERVER != $SERVER0) || (-n $SERVER6 && $SERVER6 != $SERVER1) ]] && UPDATE=1
- if [[ -z $UPDATE ]]; then
- # no changes, ignore
- SUBNET=; SUBNET6=;
- else
- # changed, remove first
- docker network rm $NETWORK >/dev/null
- fi
- fi
# set parameters for custom network creation
- N4=$SUBNET; R4=$RANGE;
- N6=$SUBNET6;
[[ -n $SUBNET && -n $GATEWAY ]] && GATEWAY="--gateway=$GATEWAY" || GATEWAY=;
[[ -n $SUBNET && -n $SERVER ]] && SERVER="--aux-address=server=$SERVER" || SERVER=;
[[ -n $SUBNET && -n $RANGE ]] && RANGE="--ip-range=$RANGE" || RANGE=;
@@ -419,15 +391,15 @@ docker_network_start(){
[[ -n $SUBNET6 ]] && SUBNET6="--ipv6 --subnet=$SUBNET6"
if [[ -n $SUBNET || -n $SUBNET6 ]]; then
TYPE=${NETWORK//[0-9.]/}
- driver $TYPE
- if [[ $TYPE == br ]]; then
+ driver $TYPE forced
+ if [[ $TYPE == br || $TYPE == wlan ]]; then
VHOST=$NETWORK
- elif [[ $TYPE == wlan ]]; then
- VHOST=$NETWORK
- ATTACH=ipvlan
else
- [[ $DOCKER_ALLOW_ACCESS == yes && -n $IPV4 ]] && VHOST=vhost${NETWORK//[^0-9.]/} || VHOST=$NETWORK
+ [[ -n $IPV4 && $DOCKER_ALLOW_ACCESS == yes ]] && VHOST=vhost${NETWORK//[^0-9.]/} || VHOST=$NETWORK
fi
+ # delete and recreate unconditionally
+ log "Processing... $NETWORK"
+ docker network rm $NETWORK &>/dev/null
docker network create -d $ATTACH $SUBNET $GATEWAY $SERVER $RANGE $SUBNET6 $GATEWAY6 $SERVER6 -o parent=$VHOST $NETWORK | xargs docker network inspect -f "created network $ATTACH {{.Name}} with subnets: {{range .IPAM.Config}}{{.Subnet}}; {{end}}" 2>/dev/null | log
# connect containers to this new network
for CONNECT in ${NETRESTORE[$NETWORK]}; do
@@ -448,57 +420,50 @@ docker_network_start(){
# hack to let containers talk to host
if [[ $TYPE == br ]]; then
LINK=shim-$NETWORK
- GW=($(ip -4 route show default dev $NETWORK | awk '{print $3,$5;exit}'))
if [[ $DOCKER_ALLOW_ACCESS == yes && -n $IPV4 ]]; then
- # create shim interface and copy parent IPv4 address to shim interface
+ IPV4="$IPV4 metric $((1000 - 1 + $(index $NETWORK)))"
+ # create shim interface
[[ -e $SYSTEM/$LINK ]] || run ip link add link $NETWORK name $LINK type $ATTACH mode $MODE
- run ip addr flush dev $LINK scope global
- run ip -4 addr add $IPV4 dev $LINK metric 0
# disable IPv6 on shim interface
echo 1 >$CONF6/$LINK/disable_ipv6
+ run ip -6 addr flush dev $LINK
+ # copy parent IPv4 address to shim interface
+ run ip addr add $IPV4 dev $LINK
run ip link set $LINK up
- if [[ -n $GW ]]; then
- if [[ -z ${GW[1]} ]]; then
- METRIC=1
- METRICS=$(ip -4 route show default | grep -Po 'metric \K\d+')
- while [[ " $METRICS " =~ " $METRIC " ]]; do ((METRIC++)); done
- # update existing route to avoid conflict with shim route
- run ip -4 route del default via $GW dev $NETWORK
- run ip -4 route add default via $GW dev $NETWORK metric $METRIC
- fi
- run ip -4 route add default via $GW dev $LINK metric 0
- fi
log "created network $LINK for host access"
elif [[ -e $SYSTEM/$LINK ]]; then
# remove shim interface
- [[ -n $GW ]] && ip -4 route del default via $GW dev $LINK
run ip addr flush dev $LINK
run ip link set $LINK down
run ip link del $LINK
fi
- elif [[ $TYPE != wlan ]]; then
- if [[ $DOCKER_ALLOW_ACCESS == yes && -n $IPV4 ]]; then
- run ip addr flush dev $VHOST scope global
- # copy IPv4 address to vhost interface
- run ip -4 addr add $IPV4 dev $VHOST metric 0
- log "prepared network $VHOST for host access"
+ else
+ if [[ $TYPE == wlan ]]; then
+ VHOST=shim-$NETWORK
+ INDEX=3000
else
VHOST=vhost${NETWORK//[^0-9.]/}
- if [[ -e $SYSTEM/$VHOST ]]; then
- # remove IP addresses of vhost
- run ip addr flush dev $VHOST scope global
- # remove routing of vhost
- run ip -4 route flush dev $VHOST
- run ip -6 route flush dev $VHOST
- fi
+ INDEX=1000
+ fi
+ INDEX=$(($INDEX - 1 + $(index $NETWORK)))
+ if [[ -n $IPV4 && $DOCKER_ALLOW_ACCESS == yes ]]; then
+ # disable IPv6 on vhost interface
+ echo 1 >$CONF6/$VHOST/disable_ipv6
+ run ip -6 addr flush dev $VHOST
+ # copy parent IPv4 address to vhost interface
+ [[ -z $(ipv4_exist $VHOST ${IPV4%/*}) ]] && run ip addr add $IPV4 metric $INDEX dev $VHOST
+ log "created network $VHOST for host access"
+ elif [[ -n $IPV4 && -e $SYSTEM/$VHOST && -n $(ipv4_exist $VHOST ${IPV4%/*}) ]]; then
+ # remove parent IPv4 address from vhost interface
+ run ip addr del $IPV4 metric $INDEX dev $VHOST
fi
fi
fi
done
# create IPv6 forward accept rule
if [[ $IPV6_FORWARD == accept ]]; then
- log "creating forward accept rule for IPv6 network"
ip6tables -P FORWARD ACCEPT
+ log "created forward accept rule for IPv6 network"
fi
log "Network started."
}
@@ -506,33 +471,31 @@ docker_network_start(){
docker_network_stop(){
log "Stopping network..."
if ! docker_running; then return 1; fi
+ # Read docker configuration file
+ [[ -f $DOCKER_CFG ]] && . $DOCKER_CFG
for NIC in $NICS; do
- if [[ ${NIC:0:3} == eth ]]; then
- if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
- NIC=${NIC/eth/br}
- elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
- NIC=${NIC/eth/bond}
- fi
- fi
- driver ${NIC//[0-9]/}
+ [[ ${NIC:0:3} == eth ]] && NIC=$(active $NIC)
+ driver ${NIC//[0-9]/} forced
for NETWORK in $(network $ATTACH ${NIC//[^0-9]/}); do
- [[ $STOCK =~ ${NETWORK%%[0-9]*} || $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK >/dev/null
+ [[ $STOCK =~ ${NETWORK%%[0-9]*} || $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK &>/dev/null
TYPE=${NETWORK//[0-9.]/}
if [[ $TYPE == br ]]; then
LINK=shim-$NETWORK
if [[ -e $SYSTEM/$LINK ]]; then
- GW=$(ip -4 route show default dev $LINK | awk '{print $3;exit}')
- [[ -n $GW ]] && run ip -4 route del default via $GW dev $LINK
run ip addr flush dev $LINK
run ip link set $LINK down
run ip link del $LINK
fi
- else
+ elif [[ $TYPE != wlan ]]; then
VHOST=vhost${NETWORK//[^0-9.]/}
[[ -e $SYSTEM/$VHOST ]] && run ip addr flush dev $VHOST
fi
done
done
+ if [[ -e $SYSTEM/shim-wlan0 ]]; then
+ IPV4=$(ip -4 -br addr show scope global primary dev shim-wlan0 | awk '{print $3,$4,$5;exit}')
+ [[ -n $IPV4 ]] && run ip addr del $IPV4 dev shim-wlan0
+ fi
log "Network stopped."
}
@@ -549,7 +512,7 @@ docker_container_start(){
if [[ $OUT =~ "Error:" ]]; then
log "$CONTAINER: $OUT" &
else
- run container_add_route $CONTAINER
+ container_add_route $CONTAINER
log "$CONTAINER: started successfully!" &
if [[ $WAIT -gt 0 ]]; then
log "$CONTAINER: wait $WAIT seconds" &
diff --git a/etc/rc.d/rc.inet1 b/etc/rc.d/rc.inet1
index 6670cbbfc..835b8dd3e 100755
--- a/etc/rc.d/rc.inet1
+++ b/etc/rc.d/rc.inet1
@@ -7,7 +7,7 @@
# @(#)/etc/rc.d/rc.inet1 10.2 Sun Jul 24 12:45:56 PDT 2005 (pjv)
# LimeTech - modified for Unraid OS
-# Bergware - modified for Unraid OS, February 2025
+# Bergware - modified for Unraid OS, May 2025
# Adapted by Bergware for use in Unraid OS - April 2016
# - improved interface configuration
@@ -81,6 +81,9 @@
# - added metric value to interface IP assignment
# - fixed DNS entries get removed when configuring interface other then eth0
+# Adapted by Bergware for use in Unraid OS - May 2025
+# - improved metric value to interface IP assignment
+
###########
# LOGGING #
###########
@@ -122,22 +125,18 @@ done
# LOOPBACK FUNCTIONS #
######################
-# function to bring up loopback interface
+# bring up loopback interface
lo_up(){
if [[ -e $SYSTEM/lo ]]; then
- if ! ip -4 addr show lo | grep -qw 'inet'; then
- run ip -4 addr add 127.0.0.1/8 dev lo
- fi
- if ! ip -6 addr show lo | grep -qw 'inet6'; then
- run ip -6 addr add ::1/128 dev lo
- fi
+ [[ -z $(ip -4 -br addr show lo | awk '{print $3;exit}') ]] && run ip -4 addr add 127.0.0.1/8 dev lo
+ [[ -z $(ip -6 -br addr show lo | awk '{print $3;exit}') ]] && run ip -6 addr add ::1/128 dev lo
run ip link set lo up
else
[[ $DEBUG_ETH_UP == yes ]] && log "interface lo not present, can't bring up"
fi
}
-# function to take down loopback interface
+# take down loopback interface
lo_down(){
if [[ -e $SYSTEM/lo ]]; then
run ip link set lo down
@@ -150,12 +149,17 @@ lo_down(){
# INTERFACE FUNCTIONS #
#######################
-# function to get link mtu size
+# return interface index
+index(){
+ cat $SYSTEM/$1/ifindex 2>/dev/null
+}
+
+# get link mtu size
get_mtu(){
ip link show $1 | grep -Po 'mtu \K\d+'
}
-# function to set/reset link mtu size
+# set/reset link mtu size
set_mtu(){
if [[ -n ${MTU[$i]} ]]; then
# set MTU to specified value
@@ -166,8 +170,8 @@ set_mtu(){
fi
}
-# function to wait for carrier of interface
-carrier_up(){
+# wait for carrier of interface
+carrier(){
local n
for n in {1..10}; do
[[ $(cat $SYSTEM/$1/carrier 2>/dev/null) == 1 ]] && return 0 || sleep 1
@@ -175,7 +179,7 @@ carrier_up(){
return 1
}
-# function to create bond interface
+# create bond interface
bond_up(){
[[ -d /proc/net/bonding ]] || modprobe bonding mode=${BONDING_MODE[$i]} miimon=${BONDING_MIIMON[$i]}
run ip link add name ${BONDNAME[$i]} type bond mode ${BONDING_MODE[$i]} miimon ${BONDING_MIIMON[$i]}
@@ -194,7 +198,7 @@ bond_up(){
[[ -n $PRIMARY ]] && run ip link set name ${BONDNAME[$i]} type bond primary $PRIMARY
}
-# function to delete bond interface
+# delete bond interface
bond_down(){
if [[ -e $SYSTEM/${BONDNAME[$i]} ]]; then
# loop thru attached interfaces in bond
@@ -208,7 +212,7 @@ bond_down(){
fi
}
-# function to create bridge interface
+# create bridge interface
br_up(){
for ((j=0;j<${VLANS[$i]:-1};j++)); do
[[ $j -eq 0 ]] && BRIDGE=${BRNAME[$i]} || BRIDGE=${BRNAME[$i]}.${VLANID[$i,$j]}
@@ -235,7 +239,7 @@ br_up(){
done
}
-# function to delete bridge interface
+# delete bridge interface
br_down(){
for ((j=0;j<${VLANS[$i]:-1};j++)); do
# loop thru main bridge and bridge VLAN interfaces
@@ -253,7 +257,7 @@ br_down(){
done
}
-# function to create VLAN interfaces
+# create VLAN interfaces
vlan_up(){
for PORT in ${BRNICS[$i]:-${IFNAME[$i]}}; do
for ((j=1;j<${VLANS[$i]};j++)); do
@@ -265,7 +269,7 @@ vlan_up(){
done
}
-# function to delete VLAN interfaces
+# delete VLAN interfaces
vlan_down(){
for PORT in ${BRNICS[$i]:-${IFNAME[$i]}}; do
for VLAN in $(ls --indicator-style=none $SYSTEM | grep -Po "$PORT\.\d+"); do
@@ -275,7 +279,7 @@ vlan_down(){
done
}
-# function to create macvtap interfaces
+# create macvtap interfaces
macvtap_up(){
PARENT=${IFNAME[$i]}
[[ -n ${BONDNICS[$i]} ]] && PARENT=${BONDNAME[$i]}
@@ -283,16 +287,18 @@ macvtap_up(){
MAC=$(echo $(hostname)-$VTAP | md5sum | sed -r 's/^(..)(..)(..)(..)(..).*$/02:\1:\2:\3:\4:\5/')
run ip link add link $PARENT name $VTAP address $MAC type macvtap mode bridge
set_mtu $VTAP
+ echo 1 >$CONF6/$VTAP/disable_ipv6
run ip link set $VTAP up
for ((j=1;j<${VLANS[$i]:-0};j++)); do
VLAN=${VLANID[$i,$j]}
run ip link add link $PARENT.$VLAN name $VTAP.$VLAN address $MAC type macvtap mode bridge
set_mtu $VTAP.$VLAN
+ echo 1 >$CONF6/$VTAP.$VLAN/disable_ipv6
run ip link set $VTAP.$VLAN up
done
}
-# function to delete macvtap interfaces
+# delete macvtap interfaces
macvtap_down(){
PARENT=${IFNAME[$i]}
[[ -n ${BONDNICS[$i]} ]] && PARENT=${BONDNAME[$i]}
@@ -308,26 +314,26 @@ macvtap_down(){
run ip link del $VTAP
}
-# function to enable/disable ipv6 protocol per interface
+# enable/disable ipv6 protocol per interface
ipv6_up(){
[[ -d $CONF6/${IFACE/$1/$2} ]] && echo $4 >$CONF6/${IFACE/$1/$2}/disable_ipv6
[[ -d $CONF6/${IFACE/$1/$3} ]] && echo $4 >$CONF6/${IFACE/$1/$3}/disable_ipv6
}
-# function to enable/disable ipv6 assignment per interface
+# enable/disable ipv6 assignment per interface
ipv6_ra(){
echo $2 >$CONF6/$1/accept_ra
echo $2 >$CONF6/$1/accept_ra_defrtr
echo $3 >$CONF6/$1/autoconf
}
-# function to enable/disable ipv6 assignment per interface
+# enable/disable ipv6 assignment per interface
ipv6_conf(){
[[ -d $CONF6/${IFACE/$1/$2} ]] && ipv6_ra ${IFACE/$1/$2} $4 $5
[[ -d $CONF6/${IFACE/$1/$3} ]] && ipv6_ra ${IFACE/$1/$3} $4 $5
}
-# function to enable/disable ipv6 assignment per interface
+# enable/disable ipv6 assignment per interface
ipv6_addr(){
[[ -d $CONF6/$IFACE ]] && ipv6_ra $IFACE $1 $2
[[ -d $CONF6/$VHOST ]] && ipv6_ra $VHOST $1 $2
@@ -342,7 +348,7 @@ ipv6_addr(){
sleep 1
}
-# function to assign IP address
+# assign IP address
ipaddr_up(){
if [[ -z $RENEW ]]; then
# disable IPv6 per interface when IPv4 only
@@ -371,7 +377,7 @@ ipaddr_up(){
[[ $IP == ipv4 ]] && DHCP_OPTIONS="$DHCP_OPTIONS -4"
[[ $IP == ipv6 ]] && DHCP_OPTIONS="$DHCP_OPTIONS -6"
[[ $IP != ipv4 && -n $PRIV6 && -d $CONF6/$IFACE ]] && echo $PRIV6 >$CONF6/$IFACE/use_tempaddr
- if carrier_up $IFACE; then
+ if carrier $IFACE; then
# interface is UP
log "interface $IFACE is UP, polling up to 60 sec for DHCP $IP server"
if ! run timeout 60 dhcpcd -w $DHCP_OPTIONS $IFACE; then
@@ -385,21 +391,23 @@ ipaddr_up(){
fi
elif [[ $DHCP == no ]]; then
# bring up interface using static IP address
- if carrier_up $IFACE; then STATE="UP"; else STATE="DOWN"; fi
+ if carrier $IFACE; then STATE="UP"; else STATE="DOWN"; fi
log "interface $IFACE is $STATE, setting static $IP address"
ipv6_addr 0 1
+ INDEX=$(index $IFACE)
+ INDEX=$((1000 + ${INDEX:-$(($(index * | sort -n | tail -1) + 1))}))
if [[ $IP != ipv6 ]]; then
[[ $j -eq 0 ]] && ADDR=${IPADDR[$i]} || ADDR=${IPADDR[$i,$j]}
if [[ -n $ADDR ]]; then
[[ $j -eq 0 ]] && MASK=${NETMASK[$i]} || MASK=${NETMASK[$i,$j]}
- [[ -n $MASK ]] && run ip -4 addr add $(unzero $ADDR)/$MASK dev $IFACE metric ${DHCP_METRIC:-1}
+ [[ -n $MASK ]] && run ip -4 addr add $(unzero $ADDR)/$MASK metric $INDEX dev $IFACE
fi
fi
if [[ $IP != ipv4 ]]; then
[[ $j -eq 0 ]] && ADDR6=${IPADDR6[$i]} || ADDR6=${IPADDR6[$i,$j]}
if [[ -n $ADDR6 ]]; then
[[ $j -eq 0 ]] && MASK6=${NETMASK6[$i]} || MASK6=${NETMASK6[$i,$j]}
- [[ -n $MASK6 ]] && run ip -6 addr add $(unzero6 $ADDR6)/$MASK6 dev $IFACE metric ${DHCP_METRIC:-1}
+ [[ -n $MASK6 ]] && run ip -6 addr add $(unzero6 $ADDR6)/$MASK6 metric $INDEX dev $IFACE
[[ -n $PRIV6 && -d $CONF6/$IFACE ]] && echo 0 >$CONF6/$IFACE/use_tempaddr
fi
fi
@@ -410,7 +418,7 @@ ipaddr_up(){
fi
}
-# function to release IP addresses and routes
+# release IP addresses and routes
ipaddr_conf(){
if [[ -e $SYSTEM/${IFACE/$1/$2} ]]; then
run ip -$4 addr flush dev ${IFACE/$1/$2}
@@ -422,7 +430,7 @@ ipaddr_conf(){
fi
}
-# function to release IP addresses and routes
+# release IP addresses and routes
ipaddr_flush(){
run ip -$1 addr flush dev $IFACE
run ip -$1 route flush dev $IFACE
@@ -436,7 +444,7 @@ ipaddr_flush(){
fi
}
-# function to release IP addresses and routes
+# release IP addresses and routes
ipaddr_down(){
if [[ $DHCP == yes ]]; then
DHCP_OPTIONS="-q -k"
@@ -453,7 +461,7 @@ ipaddr_down(){
fi
}
-# function to bring up network interface
+# bring up network interface
if_up(){
# set index of INTERFACE in array
i=0
@@ -555,7 +563,7 @@ if_up(){
done
}
-# function to take down network interface
+# take down network interface
if_down(){
# set index of INTERFACE in array
i=0
@@ -612,7 +620,7 @@ if_down(){
# GATEWAY FUNCTIONS #
#####################
-# function to add default gateway per interface
+# add default gateway per interface
gateway_up(){
for GW in ${GATEWAY[@]}; do
[[ -z $GW ]] && continue
@@ -623,7 +631,7 @@ gateway_up(){
IP=${PROTOCOL[$x]:-ipv4}
AD=${METRIC[$x]}
[[ -n $AD ]] && AD="metric $AD"
- EXIST=$(ip -4 route show default via $(unzero $GW) dev $DEV | grep "$AD ")
+ EXIST=$(ip -4 route show to default via $(unzero $GW) dev $DEV | grep "$AD ")
[[ $IP != ipv6 && -z $EXIST ]] && run ip -4 route add default via $(unzero $GW) dev $DEV $AD
done
for GW6 in ${GATEWAY6[@]}; do
@@ -635,12 +643,12 @@ gateway_up(){
IP=${PROTOCOL[$x]:-ipv4}
AD6=${METRIC6[$x]}
[[ -n $AD6 ]] && AD6="metric $AD6"
- EXIST=$(ip -6 route show default via $(unzero6 $GW6) dev $DEV | grep "$AD6 ")
+ EXIST=$(ip -6 route show to default via $(unzero6 $GW6) dev $DEV | grep "$AD6 ")
[[ $IP != ipv4 && -z $EXIST ]] && run ip -6 route add default via $(unzero6 $GW6) dev $DEV $AD6
done
}
-# function to delete default gateway per interface
+# delete default gateway per interface
gateway_down(){
for GW in ${GATEWAY[@]}; do
[[ -z $GW ]] && continue
@@ -649,7 +657,7 @@ gateway_down(){
i=(${x/,/ })
[[ -z ${i[1]} ]] && DEV=${IFNAME[$i]} || DEV=${IFNAME[$i]}.${VLANID[$x]}
IP=${PROTOCOL[$x]:-ipv4}
- EXIST=$(ip -4 route show default dev $DEV)
+ EXIST=$(ip -4 route show to default dev $DEV)
[[ $IP != ipv6 && -n $EXIST ]] && run ip -4 route flush default dev $DEV
done
for GW6 in ${GATEWAY6[@]}; do
@@ -659,12 +667,12 @@ gateway_down(){
i=(${x/,/ })
[[ -z ${i[1]} ]] && DEV=${IFNAME[$i]} || DEV=${IFNAME[$i]}.${VLANID[$x]}
IP=${PROTOCOL[$x]:-ipv4}
- EXIST=$(ip -6 route show default dev $DEV)
+ EXIST=$(ip -6 route show to default dev $DEV)
[[ $IP != ipv4 && -n $EXIST ]] && run ip -6 route flush default dev $DEV
done
}
-# function to start network
+# start network
start(){
lo_up
for INTERFACE in ${IFNAME[@]}; do
@@ -673,7 +681,7 @@ start(){
gateway_up
}
-# function to stop network
+# stop network
stop(){
gateway_down
for INTERFACE in ${IFNAME[@]}; do
@@ -682,7 +690,7 @@ stop(){
lo_down
}
-# function to show network status
+# show network status
status(){
echo "INTERFACE STATE INFORMATION"
echo "========================================================================"
@@ -693,38 +701,38 @@ status(){
# STATIC ROUTE FUNCTIONS #
##########################
-# function to add static route
+# add static route
route_up(){
[[ -n $3 ]] && METRIC="metric $3" || METRIC=
if [[ $2 == default ]]; then
# determine IP protocol & optional device
[[ -n ${1##*:*} ]] && IP=-4 || IP=-6
[[ -z ${1##*-*} ]] && DEV="dev ${1#*-}" || DEV=
- EXIST=$(ip $IP route show default via ${1%-*} $DEV | grep "$METRIC ")
+ EXIST=$(ip $IP route show to default via ${1%-*} $DEV | grep "$METRIC ")
[[ -z $EXIST ]] && run ip $IP route add default via ${1%-*} $DEV $METRIC
elif [[ -n $2 ]]; then
# determine IP protocol & gateway syntax
[[ -n ${2##*:*} ]] && IP=-4 || IP=-6
[[ -e $SYSTEM/$1 ]] && GW="dev $1" || GW="via $1"
- EXIST=$(ip $IP route show $2 $GW | grep "$METRIC ")
+ EXIST=$(ip $IP route show to $2 $GW | grep "$METRIC ")
[[ -z $EXIST ]] && run ip $IP route add $2 $GW $METRIC
fi
}
-# function to delete static route
+# delete static route
route_down(){
[[ -n $3 ]] && METRIC="metric $3" || METRIC=
if [[ $2 == default ]]; then
# determine IP protocol & optional device
[[ -n ${1##*:*} ]] && IP=-4 || IP=-6
[[ -z ${1##*-*} ]] && DEV="dev ${1#*-}" || DEV=
- EXIST=$(ip $IP route show default via ${1%-*} $DEV)
+ EXIST=$(ip $IP route show to default via ${1%-*} $DEV)
[[ -n $EXIST ]] && run ip $IP route del default via ${1%-*} $DEV $METRIC
elif [[ -n $2 ]]; then
# determine IP protocol & gateway syntax
[[ -n ${2##*:*} ]] && IP=-4 || IP=-6
[[ -e $SYSTEM/$1 ]] && GW="dev $1" || GW="via $1"
- EXIST=$(ip $IP route show $2 $GW)
+ EXIST=$(ip $IP route show to $2 $GW)
[[ -n $EXIST ]] && run ip $IP route del $2 $GW $METRIC
fi
}
diff --git a/etc/rc.d/rc.library.source b/etc/rc.d/rc.library.source
index 99861be3b..fb5f5f9d0 100644
--- a/etc/rc.d/rc.library.source
+++ b/etc/rc.d/rc.library.source
@@ -5,7 +5,7 @@
# Library used by nfsd, ntpd, rpc, samba, nginx, sshd, avahidaemon, show_interfaces
#
# Bergware - created for Unraid OS, December 2023
-# Bergware - updated January 2025
+# Bergware - updated May 2025
WIREGUARD="/etc/wireguard"
NETWORK_INI="/var/local/emhttp/network.ini"
@@ -56,8 +56,8 @@ good(){
show(){
case $# in
- 1) ip -br addr show scope global -temporary -deprecated to $1 2>/dev/null | awk '{gsub("@.+","",$1);print $1;exit}' ;;
- 2) ip -br addr show scope global -temporary -deprecated $1 $2 2>/dev/null | awk '{$1=$2="";print;exit}' | sed -r 's/ metric [0-9]+//g' ;;
+ 1) ip -br addr show scope global primary -deprecated to $1 2>/dev/null | awk '{gsub("@.+","",$1);print $1;exit}' ;;
+ 2) ip -br addr show scope global primary -deprecated $1 $2 2>/dev/null | awk '{$1=$2="";print;exit}' | sed -r 's/ metric [0-9]+//g' ;;
esac
}
@@ -160,7 +160,7 @@ check(){
fi
[[ $(ipv $ADDR) == 4 ]] && IPV4=yes || IPV6=yes
done
- done <<< $(ip -br addr show scope global -temporary -deprecated | awk '$1~"^(br|bond|eth|wlan|wg)[0-9]+(.[0-9]+)?" && $3!="" {gsub("@.+","",$1);$2="";print}' | sed -r 's/ metric [0-9]+//g' | sort)
+ done <<< $(ip -br addr show scope global primary -deprecated | awk '$1~"^(br|bond|eth|wlan|wg)[0-9]+(.[0-9]+)?" && $3!="" {gsub("@.+","",$1);$2="";print}' | sed -r 's/ metric [0-9]+//g' | sort)
# add loopback interface
if [[ "smb nfs" =~ "$CALLER" ]]; then
[[ $IPV4 == yes ]] && BIND+=(127.0.0.1)
diff --git a/etc/rc.d/rc.local b/etc/rc.d/rc.local
index af02d607c..b43f02b5c 100755
--- a/etc/rc.d/rc.local
+++ b/etc/rc.d/rc.local
@@ -80,9 +80,14 @@ fi
/usr/local/emhttp/webGui/scripts/notify smtp-init
/usr/local/emhttp/webGui/scripts/notify cron-init
+# start interface state monitoring
+if [[ -x /usr/local/sbin/monitor_interface ]]; then
+ /usr/local/sbin/monitor_interface &>/dev/null
+fi
+
# start nchan monitoring -> stop all running nchan processes when no subscribers are connected
if [[ -x /usr/local/sbin/monitor_nchan ]]; then
- /usr/local/sbin/monitor_nchan
+ /usr/local/sbin/monitor_nchan &>/dev/null
fi
# First boot following Unraid Server OS update: delete plugin file
@@ -184,7 +189,7 @@ else
if [[ -f "$PRIORITY_PLUGIN_PATH" ]]; then
/usr/local/sbin/plugin install "$PRIORITY_PLUGIN_PATH" | log
fi
- done
+ done
# Install remaining plugins
shopt -s nullglob
for PLUGIN in $CONFIG/plugins/*.plg; do
diff --git a/etc/rc.d/rc.nginx b/etc/rc.d/rc.nginx
index 0f903a569..c9298385e 100755
--- a/etc/rc.d/rc.nginx
+++ b/etc/rc.d/rc.nginx
@@ -6,7 +6,7 @@
# Written for Slackware Linux by Cherife Li .
# LimeTech - modified for Unraid OS
-# Bergware - modified for Unraid OS, October 2023
+# Bergware - modified for Unraid OS, May 2025
# reference:
# LANNAME 'tower'
@@ -537,12 +537,12 @@ build_ssl(){
# fetch LAN IP address (read management interface eth0)
[[ -e $SYSTEM/bond0 ]] && DEV=bond0 || DEV=eth0
[[ -e $SYSTEM/br0 ]] && DEV=br0
- LANIP=$(ip -4 -br addr show $DEV scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}')
- LANIP6=$(ip -6 -br addr show $DEV scope global -temporary -deprecated | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}')
+ LANIP=$(ip -4 -br addr show scope global primary dev $DEV | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
+ LANIP6=$(ip -6 -br addr show scope global primary -deprecated dev $DEV | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
# try wireless connection if no IP address on interface eth0
- [[ -z $LANIP && -e $SYSTEM/wlan0 ]] && LANIP=$(ip -4 -br addr show wlan0 scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}')
- [[ -z $LANIP6 && -e $SYSTEM/wlan0 ]] && LANIP6=$(ip -6 -br addr show wlan0 scope global -temporary -deprecated | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}')
+ [[ -z $LANIP && -e $SYSTEM/wlan0 ]] && LANIP=$(ip -4 -br addr show scope global primary dev wlan0 | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
+ [[ -z $LANIP6 && -e $SYSTEM/wlan0 ]] && LANIP6=$(ip -6 -br addr show scope global primary -deprecated dev wlan0 | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
# regenerate self-signed cert if local TLD changes */
SELFCERTPATH=$SSL/certs/${LANNAME}_unraid_bundle.pem
@@ -716,7 +716,7 @@ nginx_start(){
# side-load unraid-api
unraid_api_control start
# resume nchan publishers
- /usr/local/sbin/monitor_nchan start
+ /usr/local/sbin/monitor_nchan start
rm -f /tmp/publishPaused
if nginx_running; then REPLY="Started"; else REPLY="Failed"; fi
diff --git a/etc/rc.d/rc.wireless b/etc/rc.d/rc.wireless
index c2f419784..8e16249ce 100755
--- a/etc/rc.d/rc.wireless
+++ b/etc/rc.d/rc.wireless
@@ -10,8 +10,10 @@ DAEMON="WiFi network"
CALLER="wifi"
INI="/var/local/emhttp/wireless.ini"
CFG="/boot/config/wireless.cfg"
+DOCKER="/boot/config/docker.cfg"
OPENSSL="/usr/local/emhttp/webGui/scripts/open_ssl"
STARTWIFI="/usr/local/emhttp/webGui/scripts/wireless"
+SERVICES="/usr/local/emhttp/webGui/scripts/update_services"
WPA="/etc/wpa_supplicant.conf"
# system network references
@@ -28,6 +30,16 @@ CONF6="/proc/sys/net/ipv6/conf"
[[ -r $INI ]] && . $INI
PORT=${PORT:-wlan0}
+# return variable value from file
+var(){
+ [[ -r "$2" ]] && grep -Pom1 "^$1=\"\K[^\"]+" "$2"
+}
+
+# return interface index
+index(){
+ cat $SYSTEM/$1/ifindex 2>/dev/null
+}
+
# translate security to informational text
trans(){
case "$1" in
@@ -126,13 +138,21 @@ ipaddr_up(){
if carrier $PORT; then STATE="UP"; else STATE="DOWN"; fi
log "interface $PORT is $STATE, setting static $IP address"
ipv6_addr $PORT 0 1
+ INDEX=$(index $PORT)
+ INDEX=$((3000 + ${INDEX:-$(($(index * | sort -n | tail -1) + 1))}))
if [[ $IP == ipv4 ]]; then
- [[ -n $IP4 && -n $MASK4 ]] && run ip -4 addr add $(unzero $IP4)/$MASK4 dev $PORT metric 3004
- [[ -n $GATEWAY4 ]] && run ip -4 route add default via $GATEWAY4 dev $PORT metric 3004
+ if [[ -n $IP4 && -n $MASK4 ]]; then
+ run ip -4 addr add $(unzero $IP4)/$MASK4 metric $INDEX dev $PORT
+ # re-add IPv4 address of parent (if docker is running)
+ if [[ $(var DOCKER_ALLOW_ACCESS $DOCKER) == yes && -S /var/run/docker.sock ]]; then
+ ip addr add $(unzero $IP4)/$MASK4 metric $(($INDEX - 1)) dev shim-$PORT
+ fi
+ fi
+ [[ -n $GATEWAY4 ]] && run ip -4 route add default via $GATEWAY4 metric $INDEX dev $PORT
fi
if [[ $IP == ipv6 ]]; then
- [[ -n $IP6 && -n $MASK6 ]] && run ip -6 addr add $(unzero6 $IP6)/$MASK6 dev $PORT metric 3004
- [[ -n $GATEWAY6 ]] && run ip -6 route add default via $GATEWAY6 dev $PORT metric 3004
+ [[ -n $IP6 && -n $MASK6 ]] && run ip -6 addr add $(unzero6 $IP6)/$MASK6 metric $INDEX dev $PORT
+ [[ -n $GATEWAY6 ]] && run ip -6 route add default via $GATEWAY6 metric $INDEX dev $PORT
fi
fi
if [[ $DNS == yes ]]; then
@@ -148,17 +168,13 @@ ipaddr_up(){
ipaddr_down(){
if [[ $DHCP == yes ]]; then
# release DHCP assigned address and default route
- OPTIONS="-q -k"
+ OPTIONS="-q -k -$1"
[[ $DNS == yes ]] && OPTIONS="$OPTIONS -C resolv.conf"
- [[ $IP == ipv4 ]] && OPTIONS="$OPTIONS -4"
- [[ $IP == ipv6 ]] && OPTIONS="$OPTIONS -6"
run dhcpcd $OPTIONS $PORT
elif [[ $DHCP == no ]]; then
# release static assigned address and default route
- [[ $IP == ipv4 ]] && run ip -4 addr flush dev $PORT
- [[ $IP == ipv4 ]] && run ip -4 route flush default dev $PORT
- [[ $IP == ipv6 ]] && run ip -6 addr flush dev $PORT
- [[ $IP == ipv6 ]] && run ip -6 route flush default dev $PORT
+ run ip -$1 addr flush dev $PORT
+ run ip -$1 route flush default dev $PORT
fi
}
@@ -253,19 +269,25 @@ wifi_stop(){
log "$DAEMON... No Wifi present."
return
fi
- IP=ipv4
DHCP=$DHCP4
DNS=$DNS4
- ipaddr_down
+ SRV4=$DNS
+ SRV6=
+ ipaddr_down 4
if [[ -n $DHCP6 ]]; then
- IP=ipv6
DHCP=$DHCP6
DNS=$DNS6
- ipaddr_down
+ SRV6=$DNS
+ ipaddr_down 6
fi
+ IPV4=$(ip -4 -br addr show scope global primary dev shim-$PORT | awk '{print $3,$4,$5;exit}')
+ [[ -n $IPV4 ]] && run ip addr del $IPV4 dev shim-$PORT
+ run ip addr flush dev $PORT
run pkill wpa_supplicant
run iw dev $PORT disconnect
run rm -f $INI
+ # restart services when static assignments
+ [[ $SRV4 == no && (-z $SRV6 || $SRV6 == no) ]] && $SERVICES 5
if ! wifi_running; then REPLY="Stopped"; else REPLY="Failed"; fi
log "$DAEMON... $REPLY."
}
@@ -309,6 +331,8 @@ wifi_join(){
IP=ipv4
DHCP=$DHCP4
DNS=$DNS4
+ SRV4=$DNS
+ SRV6=
ipaddr_up
# IPv6 address assignment (if enabled)
if [[ -n $DHCP6 ]]; then
@@ -316,10 +340,13 @@ wifi_join(){
IP=ipv6
DHCP=$DHCP6
DNS=$DNS6
+ SRV6=$DNS
ipaddr_up
else
echo 1 >$CONF6/$PORT/disable_ipv6
fi
+ # restart services when static assignments
+ [[ $SRV4 == no && (-z $SRV6 || $SRV6 == no) ]] && $SERVICES 5
if wifi_running; then
if [[ -z $CC ]]; then
CC=($(iw reg get | grep -Po '^country \K..'))
diff --git a/sbin/create_network_ini b/sbin/create_network_ini
index afe6c39e1..c2f9e55e5 100755
--- a/sbin/create_network_ini
+++ b/sbin/create_network_ini
@@ -13,23 +13,42 @@
[[ (-z $reason && -z $1) || (-n $reason && ! "BOUND6 IPV4LL EXPIRE" =~ $reason) ]] && exit 0
-INI=/var/local/emhttp/network.ini.new
-CFG=/boot/config/network.cfg
-SYS=/sys/class/net
+INI="/var/local/emhttp/network.ini.new"
+STA="/var/local/emhttp/statics.ini.new"
+CFG="/boot/config/network.cfg"
+DOCKER="/boot/config/docker.cfg"
+SYSTEM="/sys/class/net"
declare -A VLANID USE_DHCP IPADDR NETMASK GATEWAY METRIC USE_DHCP6 IPADDR6 NETMASK6 GATEWAY6 PRIVACY6 METRIC6 DESCRIPTION PROTOCOL
# run & log functions
. /etc/rc.d/rc.runlog
-mask(){
- [[ -z $1 ]] && return
- # convert prefix to netmask
- set -- $((5-($1/8))) 255 255 255 255 $(((255<<(8-($1%8)))&255)) 0 0 0
- [[ $1 -gt 1 ]] && shift $1 || shift
- echo $1.$2.$3.$4
+# return variable value from file
+var(){
+ [[ -r "$2" ]] && grep -Pom1 "^$1=\"\K[^\"]+" "$2"
}
+# return interface index
+index(){
+ cat $SYSTEM/$1/ifindex 2>/dev/null
+}
+
+# convert netmask to prefix
+mask2cidr(){
+ [[ -z $1 ]] && return
+ local MASK=$(eval eval echo "'\$((('{"${1//./,}"}'>>'{7..0}')%2))'")
+ eval echo '$(('"${MASK// /+}"'))'
+}
+
+# convert prefix to netmask
+cidr2mask(){
+ [[ -z $1 ]] && return
+ local MASK=$(eval echo '$(((1<<32)-1<<32-$1>>'{3..0}'*8&255))')
+ echo "${MASK// /.}"
+}
+
+# return dns nameserver entry
dns() {
[[ $1 == 4 ]] && ADDR='(\d{1,3}\.){3}\d+' || ADDR='([0-9a-fA-F]{1,4}::?){1,7}[0-9a-fA-F]*'
grep -Po "^nameserver \K$ADDR" /etc/resolv.conf
@@ -48,8 +67,10 @@ else
BONDING=yes
BRIDGING=yes
fi
+
# prepare empty file
echo -n >$INI
+echo -n >$STA
# loop thru all defined interfaces (=1 in case of legacy)
for ((i=0; i<${SYSNICS:-1}; i++)); do
@@ -57,7 +78,7 @@ for ((i=0; i<${SYSNICS:-1}; i++)); do
ETH=${IFACE/#bond/eth}
ETH=${ETH/#br/eth}
# don't store when non-existing
- [[ -e $SYS/$ETH ]] || continue
+ [[ -e $SYSTEM/$ETH ]] || continue
echo "[$ETH]" >>$INI
if [[ $i -eq 0 ]]; then
# process legacy settings
@@ -125,12 +146,13 @@ for ((i=0; i<${SYSNICS:-1}; i++)); do
echo "PROTOCOL:0=\"${PROTOCOL[$i]}\"" >>$INI
echo "USE_DHCP:0=\"${USE_DHCP[$i]}\"" >>$INI
echo "USE_GW4:0=\"${USE_GW4[$i]}\"" >>$INI
+ DATA=
if [[ ${USE_DHCP[$i]} == yes ]]; then
# get dhcp assigned ipv4 address & mask
- NET=($(ip -4 -br addr show $IFACE | awk '{sub("/"," ",$3);print $3;exit}'))
- GW=$(ip -4 route show default dev $IFACE | awk '{print $3;exit}')
+ NET=($(ip -4 -br addr show scope global primary dev $IFACE | awk '{sub("/"," ",$3);print $3;exit}'))
+ GW=$(ip -4 route show to default dev $IFACE | awk '{print $3;exit}')
echo "IPADDR:0=\"${NET[0]}\"" >>$INI
- echo "NETMASK:0=\"$(mask ${NET[1]})\"" >>$INI
+ echo "NETMASK:0=\"$(cidr2mask ${NET[1]})\"" >>$INI
echo "GATEWAY:0=\"$GW\"" >>$INI
echo "METRIC:0=\"${METRIC[$i]}\"" >>$INI
else
@@ -139,13 +161,15 @@ for ((i=0; i<${SYSNICS:-1}; i++)); do
echo "NETMASK:0=\"${NETMASK[$i]}\"" >>$INI
echo "GATEWAY:0=\"${GATEWAY[$i]}\"" >>$INI
echo "METRIC:0=\"${METRIC[$i]}\"" >>$INI
+ # store static ipv4 assignment
+ [[ -n ${IPADDR[$i]} ]] && DATA="$IFACE ${IPADDR[$i]}/$(mask2cidr ${NETMASK[$i]}) ${METRIC[$i]:-0}"
fi
echo "USE_DHCP6:0=\"${USE_DHCP6[$i]}\"" >>$INI
echo "USE_GW6:0=\"${USE_GW6[$i]}\"" >>$INI
if [[ ${USE_DHCP6[$i]} == yes ]]; then
# get auto assigned ipv6 address & prefix
- NET6=($(ip -6 -br addr show $IFACE scope global -temporary -deprecated | awk '{sub("/"," ",$3);print $3;exit}'))
- GW6=$(ip -6 route show default dev $IFACE | awk '{print $3;exit}')
+ NET6=($(ip -6 -br addr show scope global primary -deprecated dev $IFACE | awk '{sub("/"," ",$3);print $3;exit}'))
+ GW6=$(ip -6 route show to default dev $IFACE | awk '{print $3;exit}')
echo "IPADDR6:0=\"${NET6[0]}\"" >>$INI
echo "NETMASK6:0=\"${NET6[1]}\"" >>$INI
echo "GATEWAY6:0=\"$GW6\"" >>$INI
@@ -158,7 +182,10 @@ for ((i=0; i<${SYSNICS:-1}; i++)); do
echo "GATEWAY6:0=\"${GATEWAY6[$i]}\"" >>$INI
echo "METRIC6:0=\"${METRIC6[$i]}\"" >>$INI
echo "PRIVACY6:0=\"\"" >>$INI
+ # store static ipv4 assignment
+ [[ -n ${IPADDR6[$i]} ]] && DATA="${DATA:-$IFACE} ${IPADDR6[$i]}/${NETMASK6[$i]} ${METRIC6[$i]:-0}"
fi
+ [[ -n $DATA ]] && echo "$DATA" >>$STA
echo "USE_MTU=\"${USE_MTU[$i]}\"" >>$INI
echo "MTU=\"${MTU[$i]}\"" >>$INI
if [[ -n ${VLANS[$i]} ]]; then
@@ -170,13 +197,14 @@ for ((i=0; i<${SYSNICS:-1}; i++)); do
echo "PROTOCOL:$j=\"${PROTOCOL[$i,$j]}\"" >>$INI
echo "USE_DHCP:$j=\"${USE_DHCP[$i,$j]}\"" >>$INI
echo "USE_GW4:$j=\"${USE_GW4[$i,$j]}\"" >>$INI
+ DATA=
if [[ ${USE_DHCP[$i,$j]} == yes ]]; then
DEV=$IFACE.${VLANID[$i,$j]}
- # get dhcp assigned ipv4 address & mask
- NET=($(ip -4 -br addr show $DEV | awk '{sub("/"," ",$3);print $3;exit}'))
- GW=$(ip -4 route show default dev $DEV | awk '{print $3;exit}')
+ # get dhcp assigned ipv4 address & cidr2mask
+ NET=($(ip -4 -br addr show scope global primary dev $DEV | awk '{sub("/"," ",$3);print $3;exit}'))
+ GW=$(ip -4 route show to default dev $DEV | awk '{print $3;exit}')
echo "IPADDR:$j=\"${NET[0]}\"" >>$INI
- echo "NETMASK:$j=\"$(mask ${NET[1]})\"" >>$INI
+ echo "NETMASK:$j=\"$(cidr2mask ${NET[1]})\"" >>$INI
echo "GATEWAY:$j=\"$GW\"" >>$INI
echo "METRIC:$j=\"${METRIC[$i,$j]}\"" >>$INI
else
@@ -185,14 +213,16 @@ for ((i=0; i<${SYSNICS:-1}; i++)); do
echo "NETMASK:$j=\"${NETMASK[$i,$j]}\"" >>$INI
echo "GATEWAY:$j=\"${GATEWAY[$i,$j]}\"" >>$INI
echo "METRIC:$j=\"${METRIC[$i,$j]}\"" >>$INI
+ # store static ipv6 assignment
+ [[ -n ${IPADDR[$i,$j]} ]] && DATA="$DEV ${IPADDR[$i,$j]}/$(mask2cidr ${NETMASK[$i,$j]}) ${METRIC[$i,$j]:-0}"
fi
echo "USE_DHCP6:$j=\"${USE_DHCP6[$i,$j]}\"" >>$INI
echo "USE_GW6:$j=\"${USE_GW6[$i,$j]}\"" >>$INI
if [[ ${USE_DHCP6[$i,$j]} == yes ]]; then
DEV=$IFACE.${VLANID[$i,$j]}
# get auto assigned ipv6 address & prefix
- NET6=($(ip -6 -br addr show $DEV scope global -temporary -deprecated | awk '{sub("/"," ",$3);print $3;exit}'))
- GW6=$(ip -6 route show default dev $DEV | awk '{print $3;exit}')
+ NET6=($(ip -6 -br addr show scope global primary -deprecated dev $DEV | awk '{sub("/"," ",$3);print $3;exit}'))
+ GW6=$(ip -6 route show to default dev $DEV | awk '{print $3;exit}')
echo "IPADDR6:$j=\"${NET6[0]}\"" >>$INI
echo "NETMASK6:$j=\"${NET6[1]}\"" >>$INI
echo "GATEWAY6:$j=\"$GW6\"" >>$INI
@@ -205,7 +235,11 @@ for ((i=0; i<${SYSNICS:-1}; i++)); do
echo "GATEWAY6:$j=\"${GATEWAY6[$i,$j]}\"" >>$INI
echo "METRIC6:$j=\"${METRIC6[$i,$j]}\"" >>$INI
echo "PRIVACY6:$j=\"\"" >>$INI
+ # store static ipv6 assignment
+ [[ -n ${IPADDR6[$i,$j]} ]] && DATA="${DATA:-$DEV} ${IPADDR6[$i,$j]}/${NETMASK6[$i,$j]} ${METRIC6[$i,$j]:-0}"
fi
+ # static IP assignments handled by rc.monitor
+ [[ -n $DATA ]] && echo "$DATA" >>$STA
done
else
# interface without VLANs
@@ -214,6 +248,25 @@ for ((i=0; i<${SYSNICS:-1}; i++)); do
done
# atomically update file
mv $INI ${INI%.*}
+mv $STA ${STA%.*}
+
+# add or remove IPv4 assignment from attached interface when Docker custom network access is allowed
+[[ ${interface:0:2} == br || $interface == wlan0 ]] && LINK=shim-$interface || LINK=vhost${interface//[^0-9.]/}
+if [[ -e $SYSTEM/$LINK && $(var DOCKER_ALLOW_ACCESS $DOCKER) == yes ]]; then
+ IPV4=$(ip -4 -br addr show scope global primary dev $interface | awk '{print $3;exit}')
+ [[ $interface == wlan0 ]] && INDEX=3000 || INDEX=1000
+ INDEX=$(($INDEX - 1 + $(index $interface)))
+ case $reason in
+ 'BOUND' | 'BOUND6')
+ # re-add IPv4 address of parent (if docker is running)
+ [[ -S /var/run/docker.sock ]] && ip addr add $IPV4 metric $INDEX dev $LINK
+ ;;
+ 'EXPIRE')
+ # remove IPv4 address of parent
+ ip addr del $IPV4 metric $INDEX dev $LINK
+ ;;
+ esac
+fi
log "interface=${interface:-$1}, reason=$reason, protocol=$protocol"
# delayed execution
@@ -224,19 +277,19 @@ if [[ -z $interface || "eth0 br0 bond0 wlan0" =~ $interface ]]; then
. /etc/unraid-version
echo -e "Unraid Server OS version: $version" >/etc/issue
# find management interface
- [[ -e $SYS/bond0 ]] && dev=bond0 || dev=eth0
- [[ -e $SYS/br0 ]] && dev=br0
- IPv4=$(ip -4 -br addr show $dev scope global | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
- IPv6=$(ip -6 -br addr show $dev scope global -temporary -deprecated | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
+ [[ -e $SYSTEM/bond0 ]] && DEV=bond0 || DEV=eth0
+ [[ -e $SYSTEM/br0 ]] && DEV=br0
+ IPV4=$(ip -4 -br addr show scope global primary dev $DEV | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
+ IPV6=$(ip -6 -br addr show scope global primary -deprecated dev $DEV | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
# show current IP assignment
- [[ -n $IPv4 ]] && echo " IPv4 address: $IPv4" >>/etc/issue || echo " IPv4 address: not set" >>/etc/issue
- [[ -n $IPv6 ]] && echo " IPv6 address: $IPv6" >>/etc/issue || echo " IPv6 address: not set" >>/etc/issue
- if [[ -e $SYS/wlan0 ]]; then
+ [[ -n $IPV4 ]] && echo " IPv4 address: $IPV4" >>/etc/issue || echo " IPv4 address: not set" >>/etc/issue
+ [[ -n $IPV6 ]] && echo " IPv6 address: $IPV6" >>/etc/issue || echo " IPv6 address: not set" >>/etc/issue
+ if [[ -e $SYSTEM/wlan0 ]]; then
echo "Wireless network:" >>/etc/issue
- IPv4=$(ip -4 -br addr show wlan0 scope global | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
- IPv6=$(ip -6 -br addr show wlan0 scope global -temporary -deprecated | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
- [[ -n $IPv4 ]] && echo " IPv4 address: $IPv4" >>/etc/issue || echo " IPv4 address: not set" >>/etc/issue
- [[ -n $IPv6 ]] && echo " IPv6 address: $IPv6" >>/etc/issue || echo " IPv6 address: not set" >>/etc/issue
+ IPV4=$(ip -4 -br addr show scope global primary dev wlan0 | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
+ IPV6=$(ip -6 -br addr show scope global primary -deprecated dev wlan0 | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
+ [[ -n $IPV4 ]] && echo " IPv4 address: $IPV4" >>/etc/issue || echo " IPv4 address: not set" >>/etc/issue
+ [[ -n $IPV6 ]] && echo " IPv6 address: $IPV6" >>/etc/issue || echo " IPv6 address: not set" >>/etc/issue
fi
echo >>/etc/issue
fi
diff --git a/sbin/monitor_interface b/sbin/monitor_interface
new file mode 100755
index 000000000..041f02856
--- /dev/null
+++ b/sbin/monitor_interface
@@ -0,0 +1,70 @@
+#!/bin/bash
+#
+# script: monitor_interface
+#
+# Monitors a given list of interfaces and add or remove static IP addresses to these interfaces.
+# The list of interfaces is provided in the file '/var/local/emhttp/statics.ini'
+# This file is maintained by the script 'create_network_ini' which keep track of all IP assignments.
+#
+# By removing static IP addresses from inactive interfaces, these interfaces do not longer interfere with wireless.
+# In other words the wired connection can be pulled without consequences.
+#
+# Bergware - modified for Unraid OS, May 2025
+
+FILE=/var/local/emhttp/statics.ini
+SYSTEM=/sys/class/net
+
+state(){
+ cat $SYSTEM/$1/operstate 2>/dev/null
+}
+
+md5(){
+ [[ -r $FILE ]] && md5sum $FILE | awk '{print $1;exit}'
+}
+
+switch(){
+ local n
+ [[ -z $1 ]] && return 1
+ # state change should stay stable for at least 5 seconds
+ for n in {1..5}; do
+ [[ $(state $1) == $2 ]] && return 1 || sleep 1
+ done
+ return 0
+}
+
+init(){
+ PORT=(); IPV4=(); METRIC4=(); IPV6=(); METRIC6=(); STATE=();
+ if [[ -r $FILE ]]; then
+ # initialize values from file, maintained by 'create_network_ini'
+ while IFS=$'\n' read -r ROW; do
+ ROW=($ROW)
+ PORT+=(${ROW[0]})
+ IPV4+=(${ROW[1]:--})
+ METRIC4+=(${ROW[2]:--})
+ IPV6+=(${ROW[3]:--})
+ METRIC6+=(${ROW[4]:--})
+ STATE+=($(state ${ROW[0]}))
+ done <$FILE
+ fi
+ MD5=$(md5)
+}
+
+while :; do
+ # monitor file content changes
+ [[ $MD5 != $(md5) ]] && init
+ for i in ${!PORT[@]}; do
+ # did interface state change?
+ if switch ${PORT[$i]} ${STATE[$i]}; then
+ STATE[$i]=$(state ${PORT[$i]})
+ if [[ ${STATE[$i]} == up ]]; then
+ [[ ${IPV4[$i]} != '-' ]] && ip addr add ${IPV4[$i]} metric ${METRIC4[$i]} dev ${PORT[$i]}
+ [[ ${IPV6[$i]} != '-' ]] && ip addr add ${IPV6[$i]} metric ${METRIC6[$i]} dev ${PORT[$i]}
+ elif [[ ${STATE[$i]} == down ]]; then
+ ip addr flush scope global dev ${PORT[$i]}
+ fi
+ fi
+ done
+ # check every 3 seconds
+ sleep 3
+done &
+disown %%