make it actually work

This commit is contained in:
Cyberes 2023-06-13 14:14:38 -06:00
parent 059c7d6e58
commit d1ce9211de
Signed by: cyberes
GPG Key ID: 6B4A33836A9500FE
10 changed files with 400 additions and 110 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
config.sh
.idea
config/config.sh
# ---> Python
# Byte-compiled / optimized / DLL files

View File

@ -6,17 +6,17 @@
# Config
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
while [ -L "$SOURCE" ]; do
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE
done
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
if [[ -f "$DIR/config.sh" ]]; then
source "$DIR/config.sh"
if [[ -f "$DIR/config/config.sh" ]]; then
source "$DIR/config/config.sh"
else
echo "config.sh missing!"
echo "$DIR/config/config.sh missing!"
exit 1
fi
@ -25,12 +25,35 @@ fi
# Must be run as root
if [ "$(id -u)" -ne 0 ]; then
echo 'This script must be run as root.' >&2
exit 1
echo 'This script must be run as root.' >&2
exit 1
fi
# Reset interfaces
iptables -X
iptables -F
iptables -t nat -X
iptables -t nat -F
echo "Cleared iptables"
ifconfig $WLAN_IFACE down
ifconfig $WLAN_IFACE hw ether $(ethtool -P $WLAN_IFACE | awk '{print $3}')
ifconfig $WLAN_IFACE up
echo "Reset $WLAN_IFACE"
while true; do
WLAN_IFACE_IP=$(ip -4 -br addr show $WLAN_IFACE | grep -Po "\\d+\\.\\d+\\.\\d+\\.\\d+")
if [ -n "${WLAN_IFACE_IP}" ]; then
echo "Got it!"
break
fi
echo "Waiting for $WLAN_IFACE to get an IP..."
sleep 5
done
# We only need to get the $WLAN_IFACE IP address and will copy it over to $ETH_IFACE later
WLAN_IFACE_IP=$(ip -4 -br addr show $WLAN_IFACE | grep -Po "\\d+\\.\\d+\\.\\d+\\.\\d+")
WLAN_NETMASK=$(ip addr show $WLAN_IFACE | grep -w inet | awk '{print $2}' | cut -d'/' -f2)
WLAN_NETMASK_CIDR=$(ip addr show $WLAN_IFACE | grep -w inet | awk '{print $2}' | cut -d'/' -f2)
if $NON_INTERACTIVE; then
NON_INTERACTIVE_APT="-y"
@ -41,16 +64,18 @@ fi
# ==============================================================================
# Install stuff
echo "# INSTALL THINGS #"
echo -e "\n# INSTALL THINGS #"
echo -e "\nUpdating...\n\n"
echo -e "Updating...\n\n"
service systemd-resolved start
sudo systemctl stop dnsmasq
apt-get update
apt-get upgrade $NON_INTERACTIVE_APT
echo -e "\n"
THINGS_TO_INSTALL="parprouted dhcp-helper net-tools"
THINGS_TO_INSTALL="parprouted dhcp-helper net-tools ethtool dnsmasq"
if ! $NON_INTERACTIVE; then
echo "Going to install: $THINGS_TO_INSTALL"
@ -64,7 +89,9 @@ apt-get install $NON_INTERACTIVE_APT $THINGS_TO_INSTALL
echo -e "\n\nSetting up services...\n"
systemctl stop dhcp-helper
systemctl enable dhcp-helper
systemctl disable dhcp-helper # remember to enable it later
sudo systemctl stop dnsmasq
sudo systemctl disable dnsmasq
if ! $NON_INTERACTIVE; then
echo -e "\n\nGoing to replace networking with systemd-networkd."
@ -77,10 +104,10 @@ apt-get --autoremove $NON_INTERACTIVE_APT purge ifupdown dhcpcd5 isc-dhcp-client
echo -e "\n\nConnecting to WiFi..."
WPA_SUPP_FILE="/etc/wpa_supplicant/wpa_supplicant-$WLAN_IFACE.conf"
cat > "$WPA_SUPP_FILE" <<EOF
cat >"$WPA_SUPP_FILE" <<EOF
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US
country=USifconfig $WLAN_IFACE
network={
ssid="$WIFI_SSID"
scan_ssid=1
@ -101,11 +128,10 @@ systemctl status --no-pager wpa_supplicant@$WLAN_IFACE.service
echo ""
ifconfig $WLAN_IFACE
echo -e "\n\n"https://raspberrypi.stackexchange.com/questions/88954/workaround-for-a-wifi-bridge-on-a-raspberry-pi-with-proxy-arp
echo -e "\n\n"
ifconfig $WLAN_IFACE
NET_CONF_FILE="/etc/systemd/network/08-$WLAN_IFACE.network"
cat > "$NET_CONF_FILE" <<EOF
cat >"$NET_CONF_FILE" <<EOF
[Match]
Name=$WLAN_IFACE
[Network]
@ -119,106 +145,22 @@ EOF
echo "Created network config: $WLAN_IFACE"
echo -e "Finishing systemd-networkd install...\n\n"
apt-get install $NON_INTERACTIVE_APT libnss-resolve
systemctl enable --now systemd-resolved.service
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
systemctl enable --now systemd-networkd.service
systemctl enable --now systemd-resolved.service
systemctl restart systemd-networkd.service
echo -e "\n\nInstall complete! Ignore any service errors for now..."
# ==============================================================================
# Create bridge interface
echo -e "\n# BRIDGE $WLAN_IFACE TO $ETH_IFACE #"
# IP="128.198.192.233"
# MAC="f0:1f:af:6d:fd:54"
# ebtables -F
# ebtables -F -t nat
# ebtables -t nat -A PREROUTING -i $WLAN_IFACE -j redirect --redirect-target ACCEPT
# ebtables -t nat -A PREROUTING -i $ETH_IFACE -j redirect --redirect-target ACCEPT
# This works but nat and is unreliable and breaks DHCP
# IP_OTHR="128.198.192.233"
# iptables -F
# iptables -F -t nat
# iptables -t nat -A PREROUTING -d $IP_OTHR -i $ETH_IFACE -j DNAT --to-destination $WLAN_IFACE_IP
# iptables -t nat -A PREROUTING -d $WLAN_IFACE_IP -i $WLAN_IFACE -j DNAT --to-destination $IP_OTHR
# iptables -t nat -A POSTROUTING -s $WLAN_IFACE_IP -j SNAT --to-source $IP_OTHR
# iptables -t nat -A POSTROUTING -s $IP_OTHR -j SNAT --to-source $WLAN_IFACE_IP
# ==============================================================================
# Set up configs
echo -e "\n# SET UP CONFIG FILES #"
sed -i'' 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf
sysctl -p /etc/sysctl.conf
echo "Set net.ipv4.ip_forward=1 in /etc/sysctl.conf"
DHCP_HELPER_CONF="/etc/default/dhcp-helper"
for ip in $DHCP_SERVERS; do
DHCP_SERVERS_CONF+=" -s $ip"
done
cat > "$DHCP_HELPER_CONF" <<EOF
# Relay DHCP requests as broadcast to $WLAN_IFACE
DHCPHELPER_OPTS="$DHCP_SERVERS_CONF"
EOF
echo "Created dhcp-helper config: $DHCP_HELPER_CONF"
# Enable IP forwarding for $WLAN_IFACE if it's not already enabled.
# grep '^option ip-forwarding 1$' /etc/dhcpcd.conf || printf "option ip-forwarding 1\n" >> /etc/dhcpcd.conf
# Disable dhcpcd control of $ETH_IFACE.
# grep "^denyinterfaces ${ETH_IFACE}\$" /etc/dhcpcd.conf || printf "denyinterfaces $ETH_IFACE\n" >> /etc/dhcpcd.conf
# Enable avahi reflector if it's not already enabled.
sed -i'' 's/#enable-reflector=no/enable-reflector=yes/' /etc/avahi/avahi-daemon.conf
grep '^enable-reflector=yes$' /etc/avahi/avahi-daemon.conf || {
printf "something went wrong...\n\n"
printf "Manually set 'enable-reflector=yes in /etc/avahi/avahi-daemon.conf'\n"
}
echo "Enabled avahi reflector."
PARPROUTED_SERVICE="/etc/systemd/system/parprouted.service"
cat > "$PARPROUTED_SERVICE" <<EOF
[Unit]
Description=proxy arp routing service
Documentation=https://raspberrypi.stackexchange.com/q/88954/79866
Requires=sys-subsystem-net-devices-$WLAN_IFACE.device
# dhcpcd.service
After=sys-subsystem-net-devices-$WLAN_IFACE.device
# dhcpcd.service
[Service]
Type=forking
# Restart until $WLAN_IFACE gained carrier
Restart=on-failure
RestartSec=5
TimeoutStartSec=30
# clone the dhcp-allocated IP to $ETH_IFACE so dhcp-helper will relay for the correct subnet
ExecStartPre=/bin/bash -c '/sbin/ip addr add $WLAN_IFACE_IP/32 dev $ETH_IFACE'
ExecStartPre=/sbin/ip link set dev $ETH_IFACE up
ExecStartPre=/sbin/ip link set $WLAN_IFACE promisc on
ExecStart=-/usr/sbin/parprouted $ETH_IFACE $WLAN_IFACE
ExecStopPost=/sbin/ip link set $WLAN_IFACE promisc off
ExecStopPost=/sbin/ip link set dev $ETH_IFACE down
ExecStopPost=/bin/bash -c '/sbin/ip addr del $WLAN_IFACE_IP/32 dev $ETH_IFACE'
[Install]
WantedBy=wpa_supplicant.service
EOF
echo "Created systemd service: $PARPROUTED_SERVICE"
systemctl daemon-reload
systemctl enable --now parprouted dhcp-helper
systemctl restart parprouted dhcp-helper
echo -e "Enabled and started parprouted and dhcp-helper"
echo -e "Waiting 5 seconds...\n"
sleep 5
systemctl status --no-pager parprouted
systemctl status --no-pager dhcp-helper
mkdir -p /var/lib/dnsmasq/
echo "Created /var/lib/dnsmasq/"
echo -e "\n\n\n==============\nDone!\nNow reboot!"

103
bridge/bridge-lan.sh Executable file
View File

@ -0,0 +1,103 @@
#!/bin/bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
if [[ -f "$DIR/../config/config.sh" ]]; then
. "$DIR/../config/config.sh"
else
echo "$DIR/../config/config.sh missing!"
exit 1
fi
if [ "$(id -u)" -ne 0 ]; then
echo 'This script must be run as root.' >&2
exit 1
fi
. "$DIR/get-dhcp-dns.sh"
# ==============================================================================
PRIVATE_LAN_IP="192.168.2.1"
BRIDGED_CLIENT_IP="192.168.2.2"
# Configure the wired interface with the bridge IP address
ifconfig $ETH_IFACE $PRIVATE_LAN_IP netmask 255.255.255.0 up
# Mirror the DNS servers to the private LAN
DHCP_DNS=($(get_dns_servers "$WLAN_IFACE"))
if [ -n "$DHCP_DNS" ]; then
dns_servers_config=""
for server in "${DHCP_DNS[@]}"; do
dns_servers_config+="server=$server"$'\n'
done
dhcp_opt_6_config="dhcp-option=6"
for server in "${DHCP_DNS[@]}"; do
dhcp_opt_6_config+=",$server"
done
echo "Mirrored WLAN DHCP DNS servers: ${DHCP_DNS[*]}"
else
dns_servers_config="""server=1.1.1.1
server=1.0.0.1"""
dhcp_opt_6_config=""
fi
# Also mirror DNS domain
DHCP_DNS_DOMAIN=$(get_dns_domain $WLAN_IFACE)
if [ -n "$DHCP_DNS_DOMAIN" ]; then
dns_domain_config="domain=$DHCP_DNS_DOMAIN"
echo "Mirrored WLAN DHCP DNS domain: $DHCP_DNS_DOMAIN"
else
dns_domain_config=""
fi
# Create the dnsmasq.conf file with the generated DNS server config
cat >/etc/dnsmasq.conf <<EOL
interface=$ETH_IFACE
domain-needed
bogus-priv
no-resolv
$dns_servers_config
$dhcp_opt_6_config
$dns_domain_config
listen-address=::1,127.0.0.1,$PRIVATE_LAN_IP
expand-hosts
dhcp-range=$PRIVATE_LAN_IP,$BRIDGED_CLIENT_IP,12h
dhcp-option=option:router,$PRIVATE_LAN_IP
dhcp-authoritative
dhcp-leasefile=/var/lib/dnsmasq/dnsmasq.leases
EOL
echo "Wrote to /etc/dnsmasq.conf"
# Configure NAT to forward traffic between the private LAN and the WLAN
iptables -X
iptables -F
iptables -t nat -X
iptables -t nat -F
echo "Reset iptables"
# Route/forward traffic between nets
iptables -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -I POSTROUTING -o $WLAN_IFACE -j MASQUERADE
echo "Created iptables to route traffic between nets"
# Port forward everything to the single client
iptables -t nat -A PREROUTING -i $WLAN_IFACE -j DNAT --to-destination $BRIDGED_CLIENT_IP
iptables -t nat -A POSTROUTING -o $ETH_IFACE -j MASQUERADE
echo "Port forwarded everything to the single bridged client"
echo -e "\nRestarting dnsmasq...\n"
service systemd-resolved stop
# systemctl enable --now dnsmasq
systemctl restart dnsmasq
echo -e "\n"
sleep 5
systemctl status --no-pager dnsmasq

95
bridge/bridge-reset.sh Executable file
View File

@ -0,0 +1,95 @@
#!/bin/bash
# ==============================================================================
# Config
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE
done
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
if [[ -f "$DIR/../config/config.sh" ]]; then
source "$DIR/../config/config.sh"
else
echo "$DIR/../config/config.sh missing!"
exit 1
fi
# Must be run as root
if [ "$(id -u)" -ne 0 ]; then
echo 'This script must be run as root.' >&2
exit 1
fi
# ==============================================================================
iptables -X
iptables -F
iptables -t nat -X
iptables -t nat -F
echo "Cleared iptables"
# Restore MAC address to WLAN interface
ifconfig $WLAN_IFACE down
ifconfig $WLAN_IFACE hw ether "$(ethtool -P $WLAN_IFACE | awk '{print $3}')"
ifconfig $WLAN_IFACE up
echo "Reset $WLAN_IFACE"
while true; do
WLAN_IFACE_IP=$(ip -4 -br addr show $WLAN_IFACE | grep -Po "\\d+\\.\\d+\\.\\d+\\.\\d+")
if [ -n "${WLAN_IFACE_IP}" ]; then
echo "Got it!"
break
fi
echo "Waiting for $WLAN_IFACE to get an IP..."
sleep 5
done
service systemd-resolved start
sudo systemctl stop dnsmasq
echo "Reset DNS services"
echo -e "\nConnecting to WiFi..."
WPA_SUPP_FILE="/etc/wpa_supplicant/wpa_supplicant-$WLAN_IFACE.conf"
cat >"$WPA_SUPP_FILE" <<EOF
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=USifconfig $WLAN_IFACE
network={
ssid="$WIFI_SSID"
scan_ssid=1
key_mgmt=WPA-EAP
eap=PEAP
identity="$WIFI_USERNAME"
password="$WIFI_PWD"
phase1="peaplabel=0"
phase2="auth=MSCHAPV2"
}
EOF
chmod 600 "$WPA_SUPP_FILE"
echo "Created wpa_supplicant: $WPA_SUPP_FILE"
systemctl disable wpa_supplicant.service
systemctl stop wpa_supplicant.service
systemctl enable --now wpa_supplicant@$WLAN_IFACE.service
systemctl status --no-pager wpa_supplicant@$WLAN_IFACE.service
echo ""
ifconfig $WLAN_IFACE
echo -e "\n\n"
ifconfig $WLAN_IFACE
NET_CONF_FILE="/etc/systemd/network/08-$WLAN_IFACE.network"
cat >"$NET_CONF_FILE" <<EOF
[Match]
Name=$WLAN_IFACE
[Network]
IPForward=yes
DHCP=yes
EOF
echo "Created network config: $WLAN_IFACE"
echo "Restarting systemd-networkd..."
systemctl restart systemd-networkd.service

52
bridge/clone-client-mac.sh Executable file
View File

@ -0,0 +1,52 @@
#!/bin/bash
# ==============================================================================
# Config and setup
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE
done
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
if [[ -f "$DIR/../config/config.sh" ]]; then
. "$DIR/../config/config.sh"
else
echo "$DIR/../config/config.sh missing!"
exit 1
fi
if [ "$(id -u)" -ne 0 ]; then
echo 'This script must be run as root.' >&2
exit 1
fi
. "$DIR/get_client_mac_address.sh"
# ==============================================================================
MAC_OTHR=$(get_client_mac_address $ETH_IFACE)
if [ -z "$MAC_OTHR" ]; then
echo "Bridged client not found! MAC address was empty."
exit 1
else
echo "Cloning MAC: $MAC_OTHR"
fi
# Clone MAC address of the wired-only device to the WiFi device
ifconfig $WLAN_IFACE down
ifconfig $WLAN_IFACE hw ether $MAC_OTHR
ifconfig $WLAN_IFACE up
echo "Set $WLAN_IFACE MAC to $MAC_OTHR"
while true; do
WLAN_IFACE_IP=$(ip -4 -br addr show $WLAN_IFACE | grep -Po "\\d+\\.\\d+\\.\\d+\\.\\d+")
if [ -n "${WLAN_IFACE_IP}" ]; then
echo "Got DHCP IP: $WLAN_IFACE_IP"
break
fi
echo "Waiting for $WLAN_IFACE to get an IP..."
sleep 5
done

9
bridge/get-dhcp-dns.sh Executable file
View File

@ -0,0 +1,9 @@
get_dns_servers() {
local interface="$1"
resolvectl status | awk -v iface="Link [0-9]+ \\($interface\\)" '$0 ~ iface {flag=1; next} flag && /DNS Servers/ {gsub(",", ""); print; exit}' | awk -F ': ' '{print $2}'
}
get_dns_domain() {
local interface="$1"
resolvectl status | awk -v iface="Link [0-9]+ \\($interface\\)" '$0 ~ iface {flag=1; next} flag && /DNS Domain/ {print $3; exit}'
}

View File

@ -0,0 +1,20 @@
function get_client_mac_address() {
# Usage: get_client_mac_address <interface_name>
local ETH_IFACE="$1"
local TARGET_IP="192.168.2.2"
# Check if the interface is plugged in
if ip link show "$ETH_IFACE" | grep -q "state UP"; then
local MAC_ADDRESS=$(arp -i "$ETH_IFACE" -n | grep "$TARGET_IP" | awk '{print $3}')
if [ -n "$MAC_ADDRESS" ]; then
echo "$MAC_ADDRESS"
else
echo "Could not find the MAC address of the connected device with IP address $TARGET_IP"
exit 1
fi
else
echo "Interface $ETH_IFACE is not plugged in."
exit 1
fi
}

View File

@ -11,5 +11,9 @@ WIFI_SSID="Example-Network"
WIFI_USERNAME="username"
WIFI_PWD="password"
# "transparent": the bridge device clones the client's MAC address and NATs traffic to a private LAN. Only supports one bridged client.
# TODO: support bridging multiple clients connected to an ethernet hub
# BRIDGE_MODE="transparent"
# Don't prompt the user for confirmation
NON_INTERACTIVE=false

14
wlan2eth.service Normal file
View File

@ -0,0 +1,14 @@
# /etc/systemd/system/wlan2eth.service
[Unit]
Description=wlan2eth
Wants=basic.target
After=basic.target network.target
[Service]
SyslogIdentifier=wlan2eth
ExecStart=/bin/bash /opt/wlan2eth/wlan2eth.sh
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target

50
wlan2eth.sh Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
# ==============================================================================
# Config
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE
done
DIR=$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)
if [[ -f "$DIR/config/config.sh" ]]; then
source "$DIR/config/config.sh"
else
echo "config/config.sh missing!"
exit 1
fi
# Must be run as root
if [ "$(id -u)" -ne 0 ]; then
echo 'This script must be run as root.' >&2
exit 1
fi
# ==============================================================================
PREV_STATUS=""
while true; do
STATUS=$(cat /sys/class/net/$ETH_IFACE/carrier 2>/dev/null)
if [ "$STATUS" != "$PREV_STATUS" ]; then
if [ "$STATUS" == "0" ]; then
echo -e "\n----> Interface $ETH_IFACE has been unplugged."
bash "$DIR/bridge/bridge-reset.sh"
echo -e "--> Reset complete\n"
elif [ "$STATUS" == "1" ]; then
echo -e "\n----> Interface $ETH_IFACE has been plugged in."
bash "$DIR/bridge/bridge-lan.sh"
bash "$DIR/bridge/clone-client-mac.sh"
echo -e "--> Bridge complete\n"
else
echo -e "\n----> Interface $ETH_IFACE not found, doing nothing...\n"
fi
PREV_STATUS="$STATUS"
fi
sleep 1
done