|
#!/bin/bash |
|
set -euo pipefail |
|
|
|
#============== Configurable Parameters ================================= |
|
NETNS=demo-ns # Name of the network namespace to create |
|
SUBNET=192.168.100.0/24 |
|
GW_IP=192.168.100.1 # Default gateway visible to the netns |
|
NS_IP=192.168.100.2 # IP address assigned inside the netns |
|
PHYS_IF=eth0 # Physical interface connected to the internet |
|
IPVLAN_HOST=ipvlan-host |
|
IPVLAN_NS=ipvlan-ns |
|
#======================================================================= |
|
|
|
HOST_GW=$(ip route | awk '/^default/ && /dev '"${PHYS_IF}"'/ {print $3}') |
|
|
|
############################################################ |
|
# Subcommand: setup |
|
# Creates netns, configures ipvlan L3, sets NAT, and verifies connectivity |
|
############################################################ |
|
setup() { |
|
echo "[0] Create resolv.conf for DNS resolution" |
|
mkdir -p /etc/netns/${NETNS} |
|
echo "nameserver ${HOST_GW}" > "/etc/netns/${NETNS}/resolv.conf" |
|
|
|
echo "[1] Create network namespace" |
|
ip netns add "${NETNS}" |
|
|
|
echo "[2] Create host-side ipvlan (L3 mode)" |
|
ip link add "${IPVLAN_HOST}" link "${PHYS_IF}" type ipvlan mode l3 |
|
ip addr add "${GW_IP}/${SUBNET#*/}" dev "${IPVLAN_HOST}" |
|
ip link set "${IPVLAN_HOST}" up |
|
|
|
echo "[3] Create netns-side ipvlan and move it to the namespace" |
|
ip link add "${IPVLAN_NS}" link "${PHYS_IF}" type ipvlan mode l3 |
|
ip link set "${IPVLAN_NS}" netns "${NETNS}" |
|
|
|
echo "[4] Configure IP and default route inside netns" |
|
ip netns exec "${NETNS}" bash -c " |
|
ip addr add ${NS_IP}/${SUBNET#*/} dev ${IPVLAN_NS} |
|
ip link set ${IPVLAN_NS} up |
|
ip route add default via ${GW_IP} |
|
" |
|
|
|
echo "[5] Enable IPv4 forwarding and adjust rp_filter" |
|
sysctl -w net.ipv4.ip_forward=1 |
|
sysctl -w net.ipv4.conf.all.rp_filter=0 |
|
sysctl -w net.ipv4.conf.${PHYS_IF}.rp_filter=0 |
|
|
|
echo "[6] Set up NAT (using iptables-nft)" |
|
iptables -t nat -C POSTROUTING -s "${SUBNET}" -o "${PHYS_IF}" -j MASQUERADE 2>/dev/null \ |
|
|| iptables -t nat -A POSTROUTING -s "${SUBNET}" -o "${PHYS_IF}" -j MASQUERADE |
|
|
|
echo "[7] Test connectivity from netns (ping)" |
|
ip netns exec "${NETNS}" ping -c 3 www.google.com |
|
|
|
echo "=== setup complete ===" |
|
} |
|
|
|
############################################################ |
|
# Subcommand: down |
|
# Cleans up all resources created by setup |
|
############################################################ |
|
down() { |
|
echo "[a] Kill ping processes if any (dummy step for idempotency)" |
|
|
|
echo "[b] Remove NAT settings" |
|
if iptables -t nat -C POSTROUTING -s "${SUBNET}" -o "${PHYS_IF}" -j MASQUERADE 2>/dev/null; then |
|
iptables -t nat -D POSTROUTING -s "${SUBNET}" -o "${PHYS_IF}" -j MASQUERADE |
|
fi |
|
|
|
echo "[c] Delete host-side ipvlan" |
|
ip link del "${IPVLAN_HOST}" 2>/dev/null || true |
|
|
|
echo "[d] Delete the netns (ipvlan inside it is also removed)" |
|
ip netns delete "${NETNS}" 2>/dev/null || true |
|
|
|
echo "[e] Remove resolv.conf directory for the namespace" |
|
rm -rf "/etc/netns/${NETNS}" |
|
|
|
echo "[f] Optionally reset sysctl settings (skip if shared)" |
|
sysctl -w net.ipv4.ip_forward=0 |
|
sysctl -w net.ipv4.conf.all.rp_filter=1 |
|
sysctl -w net.ipv4.conf.${PHYS_IF}.rp_filter=1 |
|
|
|
echo "=== down complete ===" |
|
} |
|
|
|
############################################################ |
|
# Entrypoint |
|
############################################################ |
|
usage() { |
|
cat <<EOF |
|
Usage: $(basename "$0") {setup|down} |
|
|
|
setup : Set up network namespace with ipvlan and NAT |
|
down : Tear down all the resources and restore settings |
|
EOF |
|
} |
|
|
|
main() { |
|
[[ $# -eq 1 ]] || { usage; exit 1; } |
|
|
|
case "$1" in |
|
setup) setup ;; |
|
down) down ;; |
|
*) usage; exit 1 ;; |
|
esac |
|
} |
|
|
|
main "$@" |