New vhost network for both containers and VMs

User can enable / disable bridging to change network behavior.
Containers and VMs are automatically updated to the new network settings
This commit is contained in:
bergware
2023-08-09 19:36:00 +02:00
parent a734929342
commit f8d6560b79
7 changed files with 146 additions and 74 deletions
@@ -254,14 +254,6 @@ _(Template Authoring Mode)_:
:docker_authoring_mode_help:
_(Docker custom network type)_:
: <select name="DOCKER_NETWORK_TYPE">
<?=mk_option(_var($dockercfg,'DOCKER_NETWORK_TYPE'), '1', _('ipvlan'))?>
<?=mk_option(_var($dockercfg,'DOCKER_NETWORK_TYPE'), '', _('macvlan'))?>
</select>&nbsp;_(Please read the Help carefully)_. _(Misconfiguration can cause problems)_.
:docker_custom_network_type_help:
_(Host access to custom networks)_:
: <select name="DOCKER_ALLOW_ACCESS">
<?=mk_option(_var($dockercfg,'DOCKER_ALLOW_ACCESS'), '', _('Disabled'))?>
@@ -460,11 +452,6 @@ _(Docker LOG rotation)_:
:docker_log_rotation_active_help:
_(Docker custom network type)_:
: <?=_var($dockercfg,'DOCKER_NETWORK_TYPE')=='1' ? _('ipvlan') : _('macvlan')?>
:docker_custom_network_type_help:
_(Host access to custom networks)_:
: <?=_var($dockercfg,'DOCKER_ALLOW_ACCESS')=='yes' ? _('Enabled') : _('Disabled')?>
@@ -46,8 +46,16 @@ $host = ipaddr($ethX);
// get network drivers
$driver = DockerUtil::driver();
// determine active port name
$port = file_exists('/sys/class/net/br0') ? 'BR' : (file_exists('/sys/class/net/bond0') ? 'BOND' : 'ETH');
// Docker configuration file - guaranteed to exist
$docker_cfgfile = '/boot/config/docker.cfg';
if (file_exists($docker_cfgfile) && exec("grep -Pom1 '_{$port}[0-9]+(_[0-9]+)?=' $docker_cfgfile")=='') {
# interface has changed, update configuration
exec("sed -ri 's/_(BR|BOND|ETH)([0-9]+(_[0-9]+)?)=/_{$port}\\2=/' $docker_cfgfile");
}
$defaults = @parse_ini_file("$docroot/plugins/dynamix.docker.manager/default.cfg") ?: [];
$dockercfg = array_replace_recursive($defaults, @parse_ini_file($docker_cfgfile) ?: []);
@@ -661,23 +661,36 @@
$netmodel = $nic['model'] ?: 'virtio-net';
$net_res =$this->libvirt_get_net_res($this->conn, $nic['network']);
exec("brctl show | cut -f1| awk NF | sed -n '1!p'", $br);
$vhost = file_exists("/boot/config/network.cfg") && exec("grep -Po '^BRNICS\[0\]=\"\K[^\"]+' /boot/config/network.cfg")=='';
if ($vhost) {
exec("ip -br a|grep -Po '^vhost[0-9][^@]*'",$br);
} else {
exec("brctl show | cut -f1| awk NF | sed -n '1!p'", $br);
}
if ($nic["boot"] != NULL) $nicboot = "<boot order='".$nic["boot"]."'/>" ; else $nicboot = "" ;
if($net_res) {
$netstr .= "<interface type='network'>
$netstr .= "<interface type='network'>
<mac address='{$nic['mac']}'/>
<source network='" . htmlspecialchars($nic['network'], ENT_QUOTES | ENT_XML1) . "'/>
<model type='$netmodel'/>
$nicboot
</interface>" ;
</interface>";
} elseif(in_array($nic['network'], $br)) {
$netstr .= "<interface type='bridge'>
if ($vhost) {
$netstr .= "<interface type='direct'>
<mac address='{$nic['mac']}'/>
<source dev='" . htmlspecialchars($nic['network'], ENT_QUOTES | ENT_XML1) . "' mode='bridge'/>
<model type='$netmodel'/>
$nicboot
</interface>";
} else {
$netstr .= "<interface type='bridge'>
<mac address='{$nic['mac']}'/>
<source bridge='" . htmlspecialchars($nic['network'], ENT_QUOTES | ENT_XML1) . "'/>
<model type='$netmodel'/>
$nicboot
</interface>";
}
} else {
continue;
}
@@ -1066,8 +1066,11 @@ private static $encoding = 'UTF-8';
function getValidNetworks() {
global $lv;
$arrValidNetworks = [];
exec("brctl show|grep -Po '^(vir)?br\d\S*'", $arrBridges);
if (file_exists("/boot/config/network.cfg") && exec("grep -Po '^BRNICS\[0\]=\"\K[^\"]+' /boot/config/network.cfg")=='') {
exec("ip -br a|grep -Po '^(virbr|vhost)[0-9][^@ ]*'",$arrBridges);
} else {
exec("brctl show|grep -Po '^(vir)?br\d\S*'", $arrBridges);
}
if (!is_array($arrBridges)) {
$arrBridges = [];
}
@@ -1082,7 +1085,7 @@ private static $encoding = 'UTF-8';
$arrValidNetworks['bridges'] = array_values($arrBridges);
// This breaks VMSettings.page if libvirt is not running
if ($libvirt_running == "yes") {
if ($libvirt_running == "yes") {
$arrVirtual = $lv->libvirt_get_net_list($lv->get_connection());
if (($key = array_search('default', $arrVirtual)) !== false) {
+62 -48
View File
@@ -20,15 +20,28 @@ DOCKER=/usr/bin/$DOCKERD
DOCKER_PIDFILE=/var/run/$DOCKERD.pid
DOCKER_LOG=/var/log/docker.log
DOCKER_ROOT=/var/lib/docker
DOCKER_CFG=/boot/config/docker.cfg
STOCK="eth br bond"
# network file references
INI=/var/local/emhttp/network.ini
TMP=/var/tmp/network.tmp
# determine active port name
[[ -e $SYSTEM/bond0 ]] && PORT=bond0 || PORT=eth0
[[ -e $SYSTEM/br0 ]] && PORT=br0
# Set defaults used by the docker daemon
# Read unRAID docker configuration file
[[ -f /boot/config/docker.cfg ]] && . /boot/config/docker.cfg
if [[ -f $DOCKER_CFG ]]; then
INT=${PORT:0:-1}
if ! grep -qPm1 "_${INT^^}[0-9]+(_[0-9]+)?=" $DOCKER_CFG; then
# interface has changed, update configuration
sed -ri "s/_(BR|BOND|ETH)([0-9]+(_[0-9]+)?)=/_${INT^^}\2=/" $DOCKER_CFG
sed -ri "s/(br|bond|eth)([0-9]+\.[0-9]+ )/$INT\2/g" $DOCKER_CFG
fi
# Read (updated) unRAID docker configuration file
. $DOCKER_CFG
fi
# Set storage driver appropriate for backing filesystem, override user setting
BACKINGFS=$(findmnt --output FSTYPE --noheadings $DOCKER_ROOT)
@@ -50,14 +63,6 @@ if [[ $DOCKER_LOG_ROTATION == yes ]]; then
DOCKER_OPTS="--log-opt max-size=$DOCKER_LOG_SIZE --log-opt max-file=$DOCKER_LOG_FILES $DOCKER_OPTS"
fi
# determine port name
if [[ -e $SYSTEM/br0 ]]; then
PORT=br0
elif [[ -e $SYSTEM/bond0 ]]; then
PORT=bond0
else
PORT=eth0
fi
# Adjust MTU size if non-default
MTU=$(ip link show $PORT|grep -Po 'mtu \K\d+')
[[ -n $MTU && $MTU -ne 1500 ]] && DOCKER_OPTS="--mtu=$MTU $DOCKER_OPTS"
@@ -73,15 +78,9 @@ else
[[ -e $SYSTEM/docker0 ]] && echo 1 > $CONF6/docker0/disable_ipv6
fi
if [[ -z $DOCKER_NETWORK_TYPE ]]; then
DETACH='ipvlan'
ATTACH='macvlan'
MODE='bridge'
else
DETACH='macvlan'
ATTACH='ipvlan'
MODE='l2 bridge'
fi
DETACH='ipvlan'
ATTACH='macvlan'
MODE='bridge'
export DOCKER_RAMDISK=true
@@ -231,33 +230,45 @@ start_network(){
wait_daemon
if ! is_docker_running; then return 1; fi
# get container settings for custom networks to reconnect later
declare -A NETRESTORE
declare -A NETRESTORE CTRESTORE
CONTAINERS=$(docker container ls -a --format='{{.Names}}'|tr '\n' ' ')
for CONTAINER in $CONTAINERS; 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
THIS_NETWORK=
THIS_IP=
REBUILD=
# update custom network reference (if changed)
REF=$(grep -Pom1 '<Network>\K(br|bond|eth)[0-9]' $XMLFILE)
if [[ -n $REF && $REF != $PORT ]]; then
sed -ri "s/<Network>(br|bond|eth)([0-9]+(.[0-9]+)?)<\/Network>/<Network>${PORT:0:-1}\2<\/Network>/" $XMLFILE
# flag container for later rebuild
REBUILD=1
fi
MY_NETWORK= MY_IP=
while read_dom; do
[[ $ENTITY == Network ]] && THIS_NETWORK=$CONTENT
[[ $ENTITY == MyIP ]] && THIS_IP=${CONTENT// /,} && THIS_IP=$(echo "${THIS_IP}" | tr -s "," ";")
[[ $ENTITY == Network ]] && MY_NETWORK=$CONTENT
[[ $ENTITY == MyIP ]] && MY_IP=${CONTENT// /,} && MY_IP=$(echo "$MY_IP" | tr -s "," ";")
done <$XMLFILE
# only restore valid networks
if [[ -n $THIS_NETWORK ]]; then
THIS_ID=$(docker inspect --format='{{.ID}}' $CONTAINER)
NETRESTORE[$THIS_NETWORK]="$THIS_ID,$THIS_IP ${NETRESTORE[$THIS_NETWORK]}"
if [[ -n $MY_NETWORK ]]; then
NETRESTORE[$MY_NETWORK]="$CONTAINER,$MY_IP ${NETRESTORE[$MY_NETWORK]}"
# save container name for later rebuild
CTRESTORE[$MY_NETWORK]=$REBUILD
fi
fi
# restore user defined networks
USER_NETWORKS=$(docker inspect --format='{{range $key, $value := .NetworkSettings.Networks}}{{$key}};{{if $value.IPAMConfig}}{{if $value.IPAMConfig.IPv4Address}}{{$value.IPAMConfig.IPv4Address}}{{end}}{{if $value.IPAMConfig.IPv6Address}}{{$value.IPAMConfig.IPv6Address}}{{end}}{{end}} {{end}}' $CONTAINER)
for UN in $USER_NETWORKS; do
USER_NETWORK=${UN%;*}
USER_IP=${UN#*;}
if [[ -n $USER_NETWORK && $USER_NETWORK != $THIS_NETWORK ]]; then
logger -t $(basename $0) "container $CONTAINER has an additional network that will be restored: $UN"
USER_ID=$(docker inspect --format='{{.ID}}' $CONTAINER)
NETRESTORE[$USER_NETWORK]="$USER_ID,$USER_IP ${NETRESTORE[$USER_NETWORK]}"
USER_NETWORKS=$(docker inspect --format='{{range $key, $value := .NetworkSettings.Networks}}{{$key}};{{if $value.IPAMConfig}}{{if $value.IPAMConfig.IPv4Address}}{{$value.IPAMConfig.IPv4Address}}{{end}}{{if $value.IPAMConfig.IPv6Address}},{{$value.IPAMConfig.IPv6Address}}{{end}}{{end}} {{end}}' $CONTAINER)
for ROW in $USER_NETWORKS; do
ROW=(${ROW/;/ })
MY_NETWORK=${ROW[0]}
MY_IP=${ROW[1]/,/;}
if [[ -n $MY_NETWORK && $MY_NETWORK != $MY_NETWORK ]]; then
LABEL=${MY_NETWORK//[0-9.]/}
if [[ "br bond eth" =~ $LABEL && $LABEL != ${PORT:0:-1} ]]; then
MY_NETWORK=${MY_NETWORK/$LABEL/${PORT:0:-1}}
fi
logger -t $(basename $0) "container $CONTAINER has an additional network that will be restored: $MY_NETWORK"
NETRESTORE[$MY_NETWORK]="$CONTAINER,$MY_IP ${NETRESTORE[$MY_NETWORK]}"
fi
done
done
@@ -404,16 +415,23 @@ start_network(){
[[ -n $RANGE ]] && SERVER="--aux-address=server=${R4%/*}" || SERVER="--aux-address=server=${SHIM_HIGH%/*}"
fi
fi
docker network create -d $ATTACH $SUBNET $GATEWAY $SERVER $RANGE $SUBNET6 $GATEWAY6 $SERVER6 $RANGE6 -o parent=$NETWORK $NETWORK | xargs docker network inspect -f 'created network {{.Name}} with subnets: {{range .IPAM.Config}}{{.Subnet}}; {{end}}' 2>/dev/null | logger -t $(basename $0)
VHOST=vhost${NETWORK//[^0-9.]/}
docker network create -d $ATTACH $SUBNET $GATEWAY $SERVER $RANGE $SUBNET6 $GATEWAY6 $SERVER6 $RANGE6 -o parent=$VHOST $NETWORK | xargs docker network inspect -f 'created network {{.Name}} with subnets: {{range .IPAM.Config}}{{.Subnet}}; {{end}}' 2>/dev/null | logger -t $(basename $0)
# connect containers to this new network
for CONNECT in ${NETRESTORE[$NETWORK]}; do
THIS_ID=${CONNECT%,*}
THIS_TT=${CONNECT#*,}
THIS_IP=
for IP in ${THIS_TT//;/ }; do
[[ $IP =~ ':' ]] && THIS_IP="$THIS_IP --ip6 $IP" || THIS_IP="$THIS_IP --ip $IP"
CONTAINER=${CONNECT%,*}
MY_TT=${CONNECT#*,}
MY_IP=
for IP in ${MY_TT//;/ }; do
[[ $IP =~ ':' ]] && MY_IP="$MY_IP --ip6 $IP" || MY_IP="$MY_IP --ip $IP"
done
docker network connect $THIS_IP $NETWORK $THIS_ID >/dev/null
logger -t $(basename $0) "connecting $CONTAINER to network $NETWORK"
docker network connect $MY_IP $NETWORK $CONTAINER >/dev/null
if [[ -n ${CTRESTORE[$NETWORK]} ]]; then
# rebuild the container before connecting to network
logger -t $(basename $0) "rebuild container $CONTAINER"
/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container $CONTAINER
fi
done
LINK=shim-$NETWORK
# hack to let containers talk to host
@@ -460,10 +478,9 @@ shim_network(){
# Remove custom networks
stop_network(){
for NETWORK in $(network $ATTACH); do
[[ $STOCK =~ ${NETWORK%%[0-9]*} || $DOCKER_USER_NETWORKS != preserve ]] && echo "stopping $DOCKERD network $NETWORK ..." && docker network rm $NETWORK >/dev/null
[[ $STOCK =~ ${NETWORK%%[0-9]*} || $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK >/dev/null
done
for LINK in $(ls --indicator-style=none $SYSTEM|grep '^shim-'); do
echo "stopping $DOCKERD shim $LINK ..."
ip -4 addr flush dev $LINK
ip -4 route flush dev $LINK
ip -6 addr flush dev $LINK
@@ -500,7 +517,7 @@ start_containers(){
# Stop containers
stop_containers(){
[[ ! -z $(running_containers) ]] && echo "stopping $DOCKERD containers ..." && docker stop $(running_containers) >/dev/null
[[ ! -z $(running_containers) ]] && docker stop $(running_containers) >/dev/null
}
# Start docker
@@ -542,15 +559,12 @@ stop_docker(){
ip link set docker0 down
ip link del docker0
fi
echo "$DOCKERD stopped"
return
fi
echo "waiting for docker to die ..."
done
echo "docker will not die!"
exit 1
else
echo "$DOCKER is not running ($DOCKER_PIDFILE does not exist)"
fi
}
+42 -3
View File
@@ -51,7 +51,9 @@
# - reverted iptables and ip6tables and arp-tables inclusion to bridge interfaces
# - removed promiscuous mode setting for bridge interfaces
# - added persistent option to dhcpcd
#
# Adapted by Bergware for use in unRAID - August 2023
# - added macvlan network creation
############################
# READ NETWORK CONFIG FILE #
@@ -244,12 +246,44 @@ vlan_up(){
vlan_down(){
for PORT in ${BRNICS[$i]:-${IFNAME[$i]}}; do
for VLAN in $(ls --indicator-style=none $SYSTEM|grep -Po "$PORT\.\d+"); do
run ip link set $VLAN down
run ip link del $VLAN
run ip link set $VLAN down 2>/dev/null
run ip link del $VLAN 2>/dev/null
done
done
}
# function to create macvlan interfaces
macvlan_up(){
PARENT=${IFNAME[$i]}
[[ -n ${BONDNICS[$i]} ]] && PARENT=${BONDNAME[$i]}
[[ -n ${BRNICS[$i]} ]] && PARENT=${BRNAME[$i]}
VHOST=vhost${PARENT//[^0-9]/}
run ip link add link $PARENT name $VHOST type macvlan mode bridge
set_mtu $VHOST
run ip link set $VHOST up 2>/dev/null
for ((j=1;j<${VLANS[$i]:-0};j++)); do
VLAN=${VLANID[$i,$j]}
run ip link add link $PARENT.$VLAN name $VHOST.$VLAN type macvlan mode bridge
set_mtu $VHOST.$VLAN
run ip link set $VHOST.$VLAN up 2>/dev/null
done
}
# function to delete macvlan interfaces
macvlan_down(){
PARENT=${IFNAME[$i]}
[[ -n ${BONDNICS[$i]} ]] && PARENT=${BONDNAME[$i]}
[[ -n ${BRNICS[$i]} ]] && PARENT=${BRNAME[$i]}
VHOST=vhost${PARENT//[^0-9]/}
for ((j=1;j<${VLANS[$i]:-0};j++)); do
VLAN=${VLANID[$i,$j]}
run ip link set $VHOST.$VLAN down 2>/dev/null
run ip link del $VHOST.$VLAN 2>/dev/null
done
run ip link set $VHOST down 2>/dev/null
run ip link del $VHOST 2>/dev/null
}
# function to enable/disable ipv6 protocol per interface
ipv6_up(){
[[ -d $CONF6/${IFACE/$1/$2} ]] && echo $4 >$CONF6/${IFACE/$1/$2}/disable_ipv6
@@ -272,6 +306,7 @@ ipv6_conf(){
# function to enable/disable ipv6 assignment per interface
ipv6_addr(){
ipv6_ra $IFACE $1 $2
ipv6_ra vhost${IFACE//[^0-9.]} $1 $2
# repeat action on related interfaces
if [[ ${IFACE:0:4} == bond ]]; then
ipv6_conf bond br eth $1 $2
@@ -288,6 +323,7 @@ ipaddr_up(){
# disable IPv6 per interface when IPv4 only
[[ $IP == ipv4 ]] && DISABLE6=1 || DISABLE6=0
echo $DISABLE6 >$CONF6/$IFACE/disable_ipv6
echo $DISABLE6 >$CONF6/vhost${IFACE//[^0-9.]}/disable_ipv6
# repeat action on related interfaces
if [[ ${IFACE:0:4} == bond ]]; then
ipv6_up bond br eth $DISABLE6
@@ -356,6 +392,7 @@ ipaddr_conf(){
ipaddr_flush(){
run ip -$1 addr flush dev $IFACE
run ip -$1 route flush dev $IFACE
run ip -$1 addr flush dev vhost${IFACE//[^0-9.]}
if [[ ${IFACE:0:4} == bond ]]; then
ipaddr_conf bond br eth $1
elif [[ ${IFACE:0:2} == br ]]; then
@@ -394,6 +431,7 @@ if_up(){
[[ -n ${BONDNICS[$i]} ]] && bond_up # create interface as bond
[[ -n ${VLANS[$i]} ]] && vlan_up # create interface VLANs
[[ -n ${BRNICS[$i]} ]] && br_up # create interface as bridge
macvlan_up # create macvlan interfaces
# if the interface isn't in the kernel yet
# but there's an alias for it in modules.conf
# then it should be loaded first
@@ -492,6 +530,7 @@ if_down(){
[[ $DEBUG_ETH_UP == yes ]] && log "interface $IFACE not present, can't take down"
fi
done
macvlan_down # delete macvlan interfaces
[[ -n ${BRNICS[$i]} ]] && br_down # delete interface as bridge
[[ -n ${VLANS[$i]} ]] && vlan_down # delete interface VLANs
[[ -n ${BONDNICS[$i]} ]] && bond_down # delete interface as bond
+10 -2
View File
@@ -168,9 +168,17 @@ start_libvirtd() {
exit 1
fi
# convert libvirt 1.3.1 w/ eric's hyperv vendor id patch to how libvirt does it in libvirt 1.3.3+
sed -i -e "s/<vendor id='none'\/>/<vendor_id state='on' value='none'\/>/g" /etc/libvirt/qemu/*.xml &> /dev/null
sed -ri "s/<vendor id='none'\/>/<vendor_id state='on' value='none'\/>/g" /etc/libvirt/qemu/*.xml &> /dev/null
# remove <locked/> from xml because libvirt + virlogd + virlockd has an issue with locked
sed -i -e "s/<locked\/>//g" /etc/libvirt/qemu/*.xml &> /dev/null
sed -ri "s/<locked\/>//g" /etc/libvirt/qemu/*.xml &> /dev/null
# update interface section of VM configuration files
if [[ -f /boot/config/network.cfg && -z $(grep -Po '^BRNICS\[0\]="\K[^"]+' /boot/config/network.cfg) ]]; then
# bridge interface to macvlan interface
sed -ri "s/<interface type='bridge'>/<interface type='direct'>/;s/<source bridge='br([0-9]+(.[0-9]+)?)'\/>/<source dev='vhost\1' mode='bridge'\/>/" /etc/libvirt/qemu/*.xml &> /dev/null
else
# macvlan interface to bridge interface
sed -ri "s/<interface type='direct'>/<interface type='bridge'>/;s/<source dev='vhost([0-9]+(.[0-9]+)?)' mode='bridge'\/>/<source bridge='br\1'\/>/" /etc/libvirt/qemu/*.xml &> /dev/null
fi
# copy any new conf files we dont currently have
cp -n /etc/libvirt-/*.conf /etc/libvirt &> /dev/null
# ensure tpm-states path exists