@@ -729,11 +729,21 @@ cmd_shutdown() {
729729 ip netns exec " ${NAMESPACE} " iptables -t filter -F " ${fchain} " 2> /dev/null || true
730730 ip netns exec " ${NAMESPACE} " iptables -t filter -X " ${fchain} " 2> /dev/null || true
731731
732- # Remove public veth pairs tracked under the VPC/net state dir
732+ # Remove public veth pairs owned by THIS tier only (guarded by .tier file).
733+ # IPs owned by other tiers are left untouched so those tiers keep working.
734+ # Backward compat: if no .tier file exists assume the IP belongs here.
733735 local vsd; vsd=$( _vpc_state_dir)
734736 if [ -d " ${vsd} /ips" ]; then
735737 for f in " ${vsd} /ips/" * .pvlan; do
736738 [ -f " ${f} " ] || continue
739+ local tier_f; tier_f=" ${f% .pvlan} .tier"
740+ if [ -f " ${tier_f} " ]; then
741+ local owner_tier; owner_tier=$( cat " ${tier_f} " 2> /dev/null || true)
742+ if [ -n " ${owner_tier} " ] && [ " ${owner_tier} " != " ${NETWORK_ID} " ]; then
743+ log " shutdown: skipping veth for $( basename " ${f% .pvlan} " ) (owned by tier ${owner_tier} )"
744+ continue
745+ fi
746+ fi
737747 local pvlan pveth_h
738748 pvlan=$( cat " ${f} " )
739749 pveth_h=$( pub_veth_host_name " ${pvlan} " " ${CHOSEN_ID} " )
@@ -748,8 +758,16 @@ cmd_shutdown() {
748758 ip link del " ${veth_h} " 2> /dev/null || true
749759 log " shutdown: removed guest veth ${veth_h} "
750760
751- # Clean transient VPC-wide public IP state
752- rm -rf " ${vsd} /ips" " ${vsd} /static-nat" " ${vsd} /port-forward"
761+ # Clean transient public IP state.
762+ # For isolated networks the state dir is per-network, so wipe it entirely.
763+ # For VPC networks the ips/ dir is shared across all tiers; only remove the
764+ # per-tier rule directories so other tiers are not affected.
765+ if [ -z " ${VPC_ID} " ]; then
766+ rm -rf " ${vsd} /ips" " ${vsd} /static-nat" " ${vsd} /port-forward"
767+ else
768+ rm -rf " ${STATE_DIR} /network-${NETWORK_ID} /static-nat" \
769+ " ${STATE_DIR} /network-${NETWORK_ID} /port-forward" 2> /dev/null || true
770+ fi
753771
754772 # Stop per-network services (dnsmasq, haproxy, apache2, passwd-server)
755773 _svc_stop_dnsmasq
@@ -907,6 +925,16 @@ cmd_assign_ip() {
907925 ip route show | grep -q " ^${PUBLIC_IP} " || \
908926 ip route add " ${PUBLIC_IP} /32" dev " ${pveth_h} " 2> /dev/null || true
909927
928+ # ---- Gratuitous ARP to flush upstream gateway's ARP cache ----
929+ # After a network restart the veth is recreated with a new MAC address.
930+ # Without a gratuitous ARP the upstream gateway retains the stale ARP entry
931+ # for the old MAC and packets cannot reach the new veth.
932+ if command -v arping > /dev/null 2>&1 ; then
933+ ip netns exec " ${NAMESPACE} " arping -c 3 -U -I " ${pveth_n} " " ${PUBLIC_IP} " \
934+ > /dev/null 2>&1 || true
935+ log " assign-ip: sent gratuitous ARP for ${PUBLIC_IP} on ${pveth_n} "
936+ fi
937+
910938 # ---- Default route inside namespace toward upstream gateway ----
911939 if [ -n " ${PUBLIC_GATEWAY} " ]; then
912940 ip netns exec " ${NAMESPACE} " ip route replace default \
0 commit comments