1923 lines
61 KiB
Bash
Executable File
1923 lines
61 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
VERSION=0.5.4
|
|
PROGNAME="$(basename $0)"
|
|
|
|
export LC_ALL=C
|
|
|
|
SCRIPT_UMASK=0122
|
|
umask $SCRIPT_UMASK
|
|
|
|
usage() {
|
|
cat << EOF
|
|
linux-router $VERSION (https://github.com/garywill/linux-router)
|
|
Released under LGPL, with no warranty. Use on your own risk.
|
|
|
|
Usage: $PROGNAME <options>
|
|
|
|
Options:
|
|
-h, --help Show this help
|
|
--version Print version number
|
|
|
|
-i <interface> Interface to make NATed sub-network,
|
|
and to provide Internet to
|
|
(To create Wifi hotspot use '--ap' instead)
|
|
-o <interface> Specify an inteface to provide Internet from.
|
|
(Note using this with default DNS option may leak
|
|
queries to other interfaces)
|
|
-n Do not provide Internet (See Notice 1)
|
|
--ban-priv Disallow clients to access my private network
|
|
|
|
-g <ip> Set this host's IPv4 address, netmask is 24
|
|
-6 Enable IPv6 (NAT)
|
|
--no4 Disable IPv4 Internet (not forwarding IPv4)
|
|
(See Notice 1). Usually used with '-6'
|
|
|
|
--p6 <prefix> Set IPv6 LAN address prefix (length 64)
|
|
(example: fd00:1:2:3::) Using this enables '-6'
|
|
|
|
--dns <ip>|<port>|<ip:port>
|
|
DNS server's upstream DNS.
|
|
Use ',' to seperate multiple servers
|
|
(default: use /etc/resolve.conf)
|
|
(Note IPv6 addresses need '[]' around)
|
|
--no-dns Do not serve DNS
|
|
--no-dnsmasq Disable dnsmasq server (DHCP, DNS, RA)
|
|
--catch-dns Transparent DNS proxy, redirect packets(TCP/UDP)
|
|
whose destination port is 53 to this host
|
|
--log-dns Show DNS query log
|
|
--dhcp-dns <IP1[,IP2]>|no
|
|
Set IPv4 DNS offered by DHCP (default: this host)
|
|
--dhcp-dns6 <IP1[,IP2]>|no
|
|
Set IPv6 DNS offered by DHCP (RA)
|
|
(default: this host)
|
|
(Note IPv6 addresses need '[]' around)
|
|
--hostname <name> DNS server associate this name with this host.
|
|
Use '-' to read name from /etc/hostname
|
|
-d DNS server will take into account /etc/hosts
|
|
-e <hosts_file> DNS server will take into account additional
|
|
hosts file
|
|
|
|
--mac <MAC> Set MAC address
|
|
--random-mac Use random MAC address
|
|
|
|
--tp <port> Transparent proxy,
|
|
redirect non-LAN TCP and UDP traffic to port.
|
|
Usually used with '--dns'
|
|
|
|
Wifi hotspot options:
|
|
--ap <wifi interface> <SSID>
|
|
Create Wifi access point
|
|
-p, --password <password>
|
|
Wifi password
|
|
--qr Show Wifi QR code in terminal
|
|
|
|
--hidden Hide access point (not broadcast SSID)
|
|
--no-virt Do not create virtual interface
|
|
Using this you can't use same wlan interface
|
|
for both Internet and AP
|
|
-c <channel> Channel number (default: 1)
|
|
--country <code> Set two-letter country code for regularity
|
|
(example: US)
|
|
--freq-band <GHz> Set frequency band: 2.4 or 5 (default: 2.4)
|
|
--driver Choose your WiFi adapter driver (default: nl80211)
|
|
-w <WPA version> '2' for WPA2, '1' for WPA, '1+2' for both
|
|
(default: 2)
|
|
--psk Use 64 hex digits pre-shared-key instead of
|
|
passphrase
|
|
--mac-filter Enable Wifi hotspot MAC address filtering
|
|
--mac-filter-accept Location of Wifi hotspot MAC address filter list
|
|
(defaults to /etc/hostapd/hostapd.accept)
|
|
--hostapd-debug <level> 1 or 2. Passes -d or -dd to hostapd
|
|
--isolate-clients Disable wifi communication between clients
|
|
|
|
--ieee80211n Enable IEEE 802.11n (HT)
|
|
--ieee80211ac Enable IEEE 802.11ac (VHT)
|
|
--ht_capab <HT> HT capabilities (default: [HT40+])
|
|
--vht_capab <VHT> VHT capabilities
|
|
|
|
--no-haveged Do not run haveged automatically when needed
|
|
|
|
Instance managing:
|
|
--daemon Run in background
|
|
--list-running Show running instances
|
|
--list-clients <id> List clients of an instance
|
|
--stop <id> Stop a running instance
|
|
For <id> you can use PID or subnet interface name.
|
|
You can get them with '--list-running'
|
|
|
|
Notice 1: This script assume your host's default policy won't forward
|
|
packets, so the script won't explictly ban forwarding in any
|
|
mode. In some unexpected case may cause unwanted packets
|
|
leakage between 2 networks, which you should be aware of if you
|
|
want isolated network
|
|
|
|
Examples:
|
|
$PROGNAME -i eth1
|
|
$PROGNAME --ap wlan0 MyAccessPoint
|
|
$PROGNAME --ap wlan0 MyAccessPoint -p MyPassPhrase
|
|
$PROGNAME -n --ap wlan0 MyAccessPoint -p MyPassPhrase
|
|
$PROGNAME -i eth1 --tp <transparent-proxy> --dns <dns-proxy>
|
|
EOF
|
|
}
|
|
|
|
check_empty_option(){
|
|
if [[ "$1" == "" ]]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
|
|
|
|
define_global_variables(){
|
|
#======== Global variables for user options =====
|
|
GATEWAY= # IPv4 address for this host
|
|
PREFIX6= # IPv6 LAN address prefix for this host
|
|
IID6=1 # IPv6 LAN ID for this host
|
|
IPV6=0 # enable ipv6
|
|
NO4=0 # no IPv4 Internet
|
|
BANLAN=0 # ban clients from accessing private addresses
|
|
DHCP_DNS=gateway # which ipv4 DNS the DHCP gives clients
|
|
DHCP_DNS6=gateway # which ipv6 DNS the DHCP gives clients
|
|
dnsmasq_NO_DNS=0 # disable dns server
|
|
NO_DNSMASQ=0 # disable dnsmasq (dns and dhcp)
|
|
CATCH_DNS=0 # catch clients 53 port packets
|
|
SHOW_DNS_QUERY=0 # log dns
|
|
ETC_HOSTS=0
|
|
ADDN_HOSTS=
|
|
|
|
SUBNET_IFACE= # which interface to create network
|
|
CONN_IFACE= # which interface user choose to use to create network
|
|
INTERNET_IFACE= # which interface to get Internet from
|
|
THISHOSTNAME= # this host's name the DNS tells clients
|
|
|
|
SHARE_METHOD=nat
|
|
TP_PORT= # transparent proxy port
|
|
DNS= # upstream DNS
|
|
|
|
USE_RANDOM_MAC=0
|
|
NEW_MACADDR=
|
|
OLD_MACADDR=
|
|
DAEMONIZE=0
|
|
|
|
# wifi hotspot
|
|
HIDDEN=0 # hidden wifi hotspot
|
|
WIFI_IFACE=
|
|
VWIFI_IFACE=
|
|
AP_IFACE=
|
|
CHANNEL=default
|
|
WPA_VERSION=2
|
|
MAC_FILTER=0
|
|
MAC_FILTER_ACCEPT=/etc/hostapd/hostapd.accept
|
|
IEEE80211N=0
|
|
IEEE80211AC=0
|
|
HT_CAPAB='[HT40+]'
|
|
VHT_CAPAB=
|
|
DRIVER=nl80211
|
|
NO_VIRT=0 # not use virtual interface
|
|
COUNTRY=
|
|
FREQ_BAND=2.4
|
|
NO_HAVEGED=0
|
|
HOSTAPD_DEBUG_ARGS=
|
|
USE_PSK=0
|
|
ISOLATE_CLIENTS=0
|
|
USE_IWCONFIG=0 # automatically decided
|
|
QR=0 # show wifi qr
|
|
|
|
#-- to deal with a running instance
|
|
LIST_RUNNING=0
|
|
STOP_ID=
|
|
LIST_CLIENTS_ID=
|
|
|
|
# -- variables for running
|
|
CONFDIR=
|
|
NM_RUNNING=0
|
|
NM_UNM_LIST= # it's called "list" but for now one interface
|
|
}
|
|
|
|
|
|
|
|
parse_user_options(){
|
|
while [[ -n "$1" ]]; do
|
|
case "$1" in
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
--version)
|
|
echo "$VERSION"
|
|
exit 0
|
|
;;
|
|
-i)
|
|
shift
|
|
CONN_IFACE="$1"
|
|
shift
|
|
;;
|
|
-o)
|
|
shift
|
|
INTERNET_IFACE="$1"
|
|
shift
|
|
echo ""
|
|
echo "WARN: Since you're using in this mode, make sure you've read Notice 1" >&2
|
|
echo ""
|
|
;;
|
|
-n)
|
|
shift
|
|
SHARE_METHOD=none
|
|
echo ""
|
|
echo "WARN: Since you're using in this mode, make sure you've read Notice 1" >&2
|
|
echo ""
|
|
;;
|
|
--ban-priv)
|
|
shift
|
|
BANLAN=1
|
|
;;
|
|
--tp)
|
|
shift
|
|
TP_PORT="$1"
|
|
shift
|
|
;;
|
|
|
|
|
|
-g)
|
|
shift
|
|
GATEWAY="$1"
|
|
shift
|
|
;;
|
|
-6)
|
|
shift
|
|
IPV6=1
|
|
;;
|
|
--no4)
|
|
shift
|
|
NO4=1
|
|
echo ""
|
|
echo "WARN: Since you're using in this mode, make sure you've read Notice 1" >&2
|
|
echo ""
|
|
;;
|
|
--p6)
|
|
shift
|
|
PREFIX6="$1"
|
|
IPV6=1
|
|
shift
|
|
;;
|
|
--mac)
|
|
shift
|
|
NEW_MACADDR="$1"
|
|
shift
|
|
;;
|
|
--random-mac)
|
|
shift
|
|
USE_RANDOM_MAC=1
|
|
;;
|
|
|
|
--dns)
|
|
shift
|
|
DNS="$1"
|
|
shift
|
|
;;
|
|
--no-dns)
|
|
shift
|
|
dnsmasq_NO_DNS=1
|
|
;;
|
|
--no-dnsmasq)
|
|
shift
|
|
NO_DNSMASQ=1
|
|
;;
|
|
--dhcp-dns)
|
|
shift
|
|
DHCP_DNS="$1"
|
|
shift
|
|
;;
|
|
--dhcp-dns6)
|
|
shift
|
|
DHCP_DNS6="$1"
|
|
shift
|
|
;;
|
|
--catch-dns)
|
|
shift
|
|
CATCH_DNS=1
|
|
;;
|
|
--log-dns)
|
|
shift
|
|
SHOW_DNS_QUERY=1
|
|
;;
|
|
--hostname)
|
|
shift
|
|
THISHOSTNAME="$1"
|
|
shift
|
|
;;
|
|
-d)
|
|
shift
|
|
ETC_HOSTS=1
|
|
;;
|
|
-e)
|
|
shift
|
|
ADDN_HOSTS="$1"
|
|
shift
|
|
;;
|
|
|
|
--isolate-clients)
|
|
shift
|
|
ISOLATE_CLIENTS=1
|
|
;;
|
|
|
|
--ap)
|
|
shift
|
|
WIFI_IFACE="$1"
|
|
shift
|
|
SSID="$1"
|
|
shift
|
|
;;
|
|
-p|--password)
|
|
shift
|
|
PASSPHRASE="$1"
|
|
shift
|
|
;;
|
|
--qr)
|
|
shift
|
|
QR=1
|
|
;;
|
|
|
|
|
|
--hidden)
|
|
shift
|
|
HIDDEN=1
|
|
;;
|
|
--mac-filter)
|
|
shift
|
|
MAC_FILTER=1
|
|
;;
|
|
--mac-filter-accept)
|
|
shift
|
|
MAC_FILTER_ACCEPT="$1"
|
|
shift
|
|
;;
|
|
|
|
-c)
|
|
shift
|
|
CHANNEL="$1"
|
|
shift
|
|
;;
|
|
-w)
|
|
shift
|
|
WPA_VERSION="$1"
|
|
[[ "$WPA_VERSION" == "2+1" ]] && WPA_VERSION=1+2
|
|
shift
|
|
;;
|
|
|
|
|
|
|
|
--ieee80211n)
|
|
shift
|
|
IEEE80211N=1
|
|
;;
|
|
--ieee80211ac)
|
|
shift
|
|
IEEE80211AC=1
|
|
;;
|
|
--ht_capab)
|
|
shift
|
|
HT_CAPAB="$1"
|
|
shift
|
|
;;
|
|
--vht_capab)
|
|
shift
|
|
VHT_CAPAB="$1"
|
|
shift
|
|
;;
|
|
--driver)
|
|
shift
|
|
DRIVER="$1"
|
|
shift
|
|
;;
|
|
--no-virt)
|
|
shift
|
|
NO_VIRT=1
|
|
;;
|
|
|
|
--country)
|
|
shift
|
|
COUNTRY="$1"
|
|
shift
|
|
;;
|
|
--freq-band)
|
|
shift
|
|
FREQ_BAND="$1"
|
|
shift
|
|
;;
|
|
--no-haveged)
|
|
shift
|
|
NO_HAVEGED=1
|
|
;;
|
|
--hostapd-debug)
|
|
shift
|
|
if [ "x$1" = "x1" ]; then
|
|
HOSTAPD_DEBUG_ARGS="-d"
|
|
elif [ "x$1" = "x2" ]; then
|
|
HOSTAPD_DEBUG_ARGS="-dd"
|
|
else
|
|
printf "Error: argument for --hostapd-debug expected 1 or 2, got %s\n" "$1"
|
|
exit 1
|
|
fi
|
|
shift
|
|
;;
|
|
--psk)
|
|
shift
|
|
USE_PSK=1
|
|
;;
|
|
|
|
--daemon)
|
|
shift
|
|
DAEMONIZE=1
|
|
;;
|
|
--stop)
|
|
shift
|
|
STOP_ID="$1"
|
|
shift
|
|
;;
|
|
--list-running)
|
|
shift
|
|
LIST_RUNNING=1
|
|
;;
|
|
--list-clients)
|
|
shift
|
|
LIST_CLIENTS_ID="$1"
|
|
shift
|
|
;;
|
|
|
|
|
|
|
|
*)
|
|
echo "Invalid parameter: $1" 1>&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
|
|
# seperate ip and port
|
|
sep_ip_port() {
|
|
local IP
|
|
local PORT
|
|
local INPUT
|
|
INPUT="$1"
|
|
if (echo "$INPUT" | grep '\.' >/dev/null 2>&1) ;then
|
|
if (echo "$INPUT" | grep ':' >/dev/null 2>&1) ;then
|
|
# ipv4 + port
|
|
IP="$(echo $INPUT | cut -d: -f1)"
|
|
PORT="$(echo $INPUT | cut -d: -f2)"
|
|
else
|
|
# ipv4
|
|
IP="$INPUT"
|
|
fi
|
|
elif (echo "$INPUT" | grep '\]' >/dev/null 2>&1) ;then
|
|
if (echo "$INPUT" | grep '\]\:' >/dev/null 2>&1) ;then
|
|
# ipv6 + port
|
|
IP="$(echo $INPUT | cut -d']' -f1 | cut -d'[' -f2)"
|
|
PORT="$(echo $INPUT | cut -d']' -f2 |cut -d: -f2)"
|
|
else
|
|
# ipv6
|
|
IP="$(echo $INPUT | cut -d']' -f1 | cut -d'[' -f2)"
|
|
fi
|
|
else
|
|
# port
|
|
IP='127.0.0.1'
|
|
PORT="$INPUT"
|
|
fi
|
|
printf -v "$2" %s "$IP"
|
|
printf -v "$3" %s "$PORT"
|
|
}
|
|
|
|
is_interface() {
|
|
[[ -z "$1" ]] && return 1
|
|
[[ -d "/sys/class/net/${1}" ]]
|
|
}
|
|
|
|
get_phy_device() { # only for wifi interface
|
|
local x
|
|
for x in /sys/class/ieee80211/*; do
|
|
[[ ! -e "$x" ]] && continue
|
|
if [[ "${x##*/}" = "$1" ]]; then
|
|
echo "$1"
|
|
return 0
|
|
elif [[ -e "$x/device/net/$1" ]]; then
|
|
echo ${x##*/}
|
|
return 0
|
|
elif [[ -e "$x/device/net:$1" ]]; then
|
|
echo ${x##*/}
|
|
return 0
|
|
fi
|
|
done
|
|
echo "Failed to get phy interface" >&2
|
|
return 1
|
|
}
|
|
|
|
get_adapter_info() { # only for wifi interface
|
|
#local PHY
|
|
PHY=$(get_phy_device "$1")
|
|
[[ $? -ne 0 ]] && return 1
|
|
iw phy $PHY info
|
|
}
|
|
|
|
get_adapter_kernel_module() {
|
|
local MODULE
|
|
MODULE=$(readlink -f "/sys/class/net/$1/device/driver/module")
|
|
echo ${MODULE##*/}
|
|
}
|
|
|
|
can_be_sta_and_ap() {
|
|
# iwconfig does not provide this information, assume false
|
|
[[ $USE_IWCONFIG -eq 1 ]] && return 1
|
|
if [[ "$(get_adapter_kernel_module "$1")" == "brcmfmac" ]]; then
|
|
echo "WARN: brmfmac driver doesn't work properly with virtual interfaces and" >&2
|
|
echo " it can cause kernel panic. For this reason we disallow virtual" >&2
|
|
echo " interfaces for your adapter." >&2
|
|
echo " For more info: https://github.com/oblique/create_ap/issues/203" >&2
|
|
return 1
|
|
fi
|
|
get_adapter_info "$1" | grep -E '{.* managed.* AP.*}' > /dev/null 2>&1 && return 0
|
|
get_adapter_info "$1" | grep -E '{.* AP.* managed.*}' > /dev/null 2>&1 && return 0
|
|
return 1
|
|
}
|
|
|
|
can_be_ap() {
|
|
# iwconfig does not provide this information, assume true
|
|
[[ $USE_IWCONFIG -eq 1 ]] && return 0
|
|
get_adapter_info "$1" | grep -E '\* AP$' > /dev/null 2>&1 && return 0
|
|
return 1
|
|
}
|
|
|
|
can_transmit_to_channel() {
|
|
local IFACE CHANNEL_NUM CHANNEL_INFO
|
|
IFACE=$1
|
|
CHANNEL_NUM=$2
|
|
|
|
if [[ $USE_IWCONFIG -eq 0 ]]; then
|
|
if [[ $FREQ_BAND == 2.4 ]]; then
|
|
CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " 24[0-9][0-9] MHz \[${CHANNEL_NUM}\]")
|
|
else
|
|
CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " \(49[0-9][0-9]\|5[0-9]\{3\}\) MHz \[${CHANNEL_NUM}\]")
|
|
fi
|
|
[[ -z "${CHANNEL_INFO}" ]] && return 1
|
|
[[ "${CHANNEL_INFO}" == *no\ IR* ]] && return 1
|
|
[[ "${CHANNEL_INFO}" == *disabled* ]] && return 1
|
|
return 0
|
|
else
|
|
CHANNEL_NUM=$(printf '%02d' ${CHANNEL_NUM})
|
|
CHANNEL_INFO=$(iwlist ${IFACE} channel | grep -E "Channel[[:blank:]]${CHANNEL_NUM}[[:blank:]]?:")
|
|
[[ -z "${CHANNEL_INFO}" ]] && return 1
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# taken from iw/util.c
|
|
ieee80211_frequency_to_channel() {
|
|
local FREQ=$1
|
|
if [[ $FREQ -eq 2484 ]]; then
|
|
echo 14
|
|
elif [[ $FREQ -lt 2484 ]]; then
|
|
echo $(( ($FREQ - 2407) / 5 ))
|
|
elif [[ $FREQ -ge 4910 && $FREQ -le 4980 ]]; then
|
|
echo $(( ($FREQ - 4000) / 5 ))
|
|
elif [[ $FREQ -le 45000 ]]; then
|
|
echo $(( ($FREQ - 5000) / 5 ))
|
|
elif [[ $FREQ -ge 58320 && $FREQ -le 64800 ]]; then
|
|
echo $(( ($FREQ - 56160) / 2160 ))
|
|
else
|
|
echo 0
|
|
fi
|
|
}
|
|
|
|
is_5ghz_frequency() {
|
|
[[ $1 =~ ^(49[0-9]{2})|(5[0-9]{3})$ ]]
|
|
}
|
|
|
|
is_wifi_connected() {
|
|
if [[ $USE_IWCONFIG -eq 0 ]]; then
|
|
iw dev "$1" link 2>&1 | grep -E '^Connected to' > /dev/null 2>&1 && return 0
|
|
else
|
|
iwconfig "$1" 2>&1 | grep -E 'Access Point: [0-9a-fA-F]{2}:' > /dev/null 2>&1 && return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
|
|
is_unicast_macaddr() {
|
|
local x
|
|
x=$(echo "$1" | cut -d: -f1)
|
|
x=$(printf '%d' "0x${x}")
|
|
[[ $(expr $x % 2) -eq 0 ]]
|
|
}
|
|
|
|
get_macaddr() {
|
|
is_interface "$1" || return
|
|
cat "/sys/class/net/${1}/address"
|
|
}
|
|
|
|
|
|
alloc_new_iface() { # only for wifi
|
|
local i=0
|
|
local v_iface_name=
|
|
while :; do
|
|
v_iface_name="x$i${WIFI_IFACE}"
|
|
if ! is_interface ${v_iface_name} && [[ ! -f $COMMON_CONFDIR/ifaces/${v_iface_name} ]]; then
|
|
mkdir -p $COMMON_CONFDIR/ifaces
|
|
touch $COMMON_CONFDIR/ifaces/${v_iface_name}
|
|
echo "${v_iface_name}"
|
|
return
|
|
fi
|
|
i=$((i + 1))
|
|
done
|
|
}
|
|
|
|
dealloc_iface() {
|
|
rm -f $COMMON_CONFDIR/ifaces/$1
|
|
}
|
|
|
|
#======
|
|
|
|
get_all_macaddrs() {
|
|
cat /sys/class/net/*/address
|
|
}
|
|
|
|
get_new_macaddr() {
|
|
local OLDMAC NEWMAC LAST_BYTE i
|
|
OLDMAC=$(get_macaddr "$1")
|
|
LAST_BYTE=$(printf %d 0x${OLDMAC##*:})
|
|
for i in {10..240}; do
|
|
NEWMAC="${OLDMAC%:*}:$(printf %02x $(( ($LAST_BYTE + $i) % 256 )))"
|
|
(get_all_macaddrs | grep "$NEWMAC" > /dev/null 2>&1) || break
|
|
done
|
|
echo "$NEWMAC"
|
|
}
|
|
|
|
generate_random_mac() {
|
|
local r1 r2 r3 r4 r5 r6
|
|
while :; do
|
|
r1=$( printf "%02x" $(($RANDOM%256/4*4)) )
|
|
r2=$( printf "%02x" $(($RANDOM%256)) )
|
|
r3=$( printf "%02x" $(($RANDOM%256)) )
|
|
r4=$( printf "%02x" $(($RANDOM%256)) )
|
|
r5=$( printf "%02x" $(($RANDOM%256)) )
|
|
r6=$( printf "%02x" $(($RANDOM%256)) )
|
|
RAND_MAC="$r1:$r2:$r3:$r4:$r5:$r6"
|
|
( ! ip link | grep "link" | grep $RAND_MAC > /dev/null 2>&1 ) && \
|
|
( ! ip maddress | grep "link" | grep $RAND_MAC > /dev/null 2>&1 ) && \
|
|
( ! ip neigh | grep "lladdr $RAND_MAC" > /dev/null 2>&1 ) && \
|
|
( ! get_all_macaddrs | grep $RAND_MAC ) && \
|
|
break
|
|
done
|
|
NEW_MACADDR=$RAND_MAC
|
|
}
|
|
|
|
|
|
is_ip4_range_available() {
|
|
( ip -4 address | grep "inet 192\.168\.$1\." > /dev/null 2>&1 ) && return 1
|
|
( ip -4 route | grep "^192\.168\.$1\." > /dev/null 2>&1 ) && return 1
|
|
( ip -4 route get 192.168.$1.0 2>&1 | grep -E "\bvia\b|\bunreachable\b" > /dev/null 2>&1 ) && \
|
|
( ip -4 route get 192.168.$1.255 2>&1 | grep -E "\bvia\b|\bunreachable\b" > /dev/null 2>&1 ) && return 0
|
|
return 1
|
|
}
|
|
is_ip6_range_available() {
|
|
( ip -6 address | grep -i "inet6 fd$1:$2$3:$4$5:$6$7:" > /dev/null 2>&1 ) && return 1
|
|
( ip -6 route | grep -i "^fd$1:$2$3:$4$5:$6$7:" > /dev/null 2>&1 ) && return 1
|
|
( ip -6 route get fd$1:$2$3:$4$5:$6$7:: 2>&1 | grep -E "\bvia\b|\bunreachable\b" > /dev/null 2>&1 ) && \
|
|
( ip -6 route get fd$1:$2$3:$4$5:$6$7:ffff:ffff:ffff:ffff 2>&1 | grep -E "\bvia\b|\bunreachable\b" > /dev/null 2>&1 ) && return 0
|
|
return 1
|
|
}
|
|
|
|
generate_random_ip4() {
|
|
local random_ip4
|
|
while :; do
|
|
random_ip4=$(($RANDOM%256))
|
|
is_ip4_range_available $random_ip4 && break
|
|
done
|
|
GATEWAY="192.168.$random_ip4.1"
|
|
}
|
|
generate_random_ip6() {
|
|
local r1 r2 r3 r4 r5 r6 r7
|
|
while :; do
|
|
r1=$( printf "%x" $(($RANDOM%240+16)) )
|
|
r2=$( printf "%x" $(($RANDOM%240+16)) )
|
|
r3=$( printf "%x" $(($RANDOM%240+16)) )
|
|
r4=$( printf "%x" $(($RANDOM%240+16)) )
|
|
r5=$( printf "%x" $(($RANDOM%240+16)) )
|
|
r6=$( printf "%x" $(($RANDOM%240+16)) )
|
|
r7=$( printf "%x" $(($RANDOM%240+16)) )
|
|
is_ip6_range_available $r1 $r2 $r3 $r4 $r5 $r6 $r7 && break
|
|
done
|
|
PREFIX6="fd$r1:$r2$r3:$r4$r5:$r6$r7::"
|
|
}
|
|
|
|
|
|
|
|
# start haveged when needed
|
|
haveged_watchdog() {
|
|
local show_warn=1
|
|
while :; do
|
|
if [[ $(cat /proc/sys/kernel/random/entropy_avail) -lt 1000 ]]; then
|
|
if ! which haveged > /dev/null 2>&1; then
|
|
if [[ $show_warn -eq 1 ]]; then
|
|
echo "WARN: Low entropy detected. We recommend you to install \`haveged'" 1>&2
|
|
show_warn=0
|
|
fi
|
|
elif ! pidof haveged > /dev/null 2>&1; then
|
|
echo "Low entropy detected, starting haveged" 1>&2
|
|
# boost low-entropy
|
|
haveged -w 1024 -p $COMMON_CONFDIR/haveged.pid
|
|
fi
|
|
fi
|
|
sleep 2
|
|
done
|
|
}
|
|
|
|
#========
|
|
|
|
|
|
# only support NetworkManager >= 0.9.9
|
|
nm_initcheck() {
|
|
if (which nmcli >/dev/null 2>&1 ) && (nmcli -t -f RUNNING g 2>&1 | grep -E '^running$' >/dev/null 2>&1 ) ; then
|
|
NM_RUNNING=1
|
|
fi
|
|
}
|
|
|
|
|
|
nm_knows() {
|
|
(nmcli dev show $1 | grep -E "^GENERAL.STATE:" >/dev/null 2>&1 ) && return 0 # nm sees
|
|
return 1 # nm doesn't see this interface
|
|
}
|
|
nm_get_manage() { # get an interface's managed state
|
|
local s
|
|
s=$(nmcli dev show $1 | grep -E "^GENERAL.STATE:") || return 2 # no such interface
|
|
(echo $s | grep "unmanaged" >/dev/null 2>&1) && return 1 # unmanaged
|
|
return 0 # managed
|
|
}
|
|
nm_set_unmanaged() {
|
|
while ! nm_knows $1 ; do # wait for virtual wifi interface seen by NM
|
|
sleep 0.5
|
|
done
|
|
if nm_get_manage $1 ;then
|
|
echo "Set $1 unmanaged by NetworkManager"
|
|
nmcli dev set $1 managed no || die "Failed to set $1 unmanaged by NetworkManager"
|
|
NM_UNM_LIST=$1
|
|
sleep 1
|
|
fi
|
|
}
|
|
|
|
nm_set_managed() {
|
|
nmcli dev set $1 managed yes
|
|
NM_UNM_LIST=
|
|
}
|
|
nm_restore_manage() {
|
|
if [[ $NM_UNM_LIST ]]; then
|
|
echo "Restore $NM_UNM_LIST managed by NetworkManager"
|
|
nm_set_managed $NM_UNM_LIST
|
|
sleep 0.5
|
|
fi
|
|
}
|
|
|
|
|
|
#=========
|
|
|
|
iptables_()
|
|
{
|
|
iptables -w $@ -m comment --comment "lnxrouter-$$-$SUBNET_IFACE"
|
|
return $?
|
|
}
|
|
ip6tables_()
|
|
{
|
|
ip6tables -w $@ -m comment --comment "lnxrouter-$$-$SUBNET_IFACE"
|
|
return $?
|
|
}
|
|
|
|
start_nat() {
|
|
if [[ $INTERNET_IFACE ]]; then
|
|
IPTABLES_NAT_OUT="-o ${INTERNET_IFACE}"
|
|
IPTABLES_NAT_IN="-i ${INTERNET_IFACE}"
|
|
MASQUERADE_NOTOUT=""
|
|
else
|
|
MASQUERADE_NOTOUT="! -o ${SUBNET_IFACE}"
|
|
fi
|
|
echo
|
|
echo "iptables: NAT "
|
|
if [[ $NO4 -eq 0 ]]; then
|
|
iptables_ -v -t nat -I POSTROUTING -s ${GATEWAY%.*}.0/24 $IPTABLES_NAT_OUT $MASQUERADE_NOTOUT ! -d ${GATEWAY%.*}.0/24 -j MASQUERADE || die
|
|
iptables_ -v -I FORWARD -i ${SUBNET_IFACE} $IPTABLES_NAT_OUT -s ${GATEWAY%.*}.0/24 -j ACCEPT || die
|
|
iptables_ -v -I FORWARD -o ${SUBNET_IFACE} $IPTABLES_NAT_IN -d ${GATEWAY%.*}.0/24 -j ACCEPT || die
|
|
fi
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -v -t nat -I POSTROUTING -s ${PREFIX6}/64 $IPTABLES_NAT_OUT $MASQUERADE_NOTOUT ! -d ${PREFIX6}/64 -j MASQUERADE || die
|
|
ip6tables_ -v -I FORWARD -i ${SUBNET_IFACE} $IPTABLES_NAT_OUT -s ${PREFIX6}/64 -j ACCEPT || die
|
|
ip6tables_ -v -I FORWARD -o ${SUBNET_IFACE} $IPTABLES_NAT_IN -d ${PREFIX6}/64 -j ACCEPT || die
|
|
fi
|
|
}
|
|
stop_nat() {
|
|
echo "iptables: stop NAT"
|
|
if [[ $NO4 -eq 0 ]]; then
|
|
iptables_ -t nat -D POSTROUTING -s ${GATEWAY%.*}.0/24 $IPTABLES_NAT_OUT $MASQUERADE_NOTOUT ! -d ${GATEWAY%.*}.0/24 -j MASQUERADE
|
|
iptables_ -D FORWARD -i ${SUBNET_IFACE} $IPTABLES_NAT_OUT -s ${GATEWAY%.*}.0/24 -j ACCEPT
|
|
iptables_ -D FORWARD -o ${SUBNET_IFACE} $IPTABLES_NAT_IN -d ${GATEWAY%.*}.0/24 -j ACCEPT
|
|
fi
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -t nat -D POSTROUTING -s ${PREFIX6}/64 $IPTABLES_NAT_OUT $MASQUERADE_NOTOUT ! -d ${PREFIX6}/64 -j MASQUERADE
|
|
ip6tables_ -D FORWARD -i ${SUBNET_IFACE} $IPTABLES_NAT_OUT -s ${PREFIX6}/64 -j ACCEPT
|
|
ip6tables_ -D FORWARD -o ${SUBNET_IFACE} $IPTABLES_NAT_IN -d ${PREFIX6}/64 -j ACCEPT
|
|
fi
|
|
}
|
|
|
|
start_ban_lan() {
|
|
echo
|
|
echo "iptables: Disallow clients to access LAN"
|
|
iptables_ -N BANLAN-f-${SUBNET_IFACE} || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 0.0.0.0/8 -j REJECT || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 10.0.0.0/8 -j REJECT || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 100.64.0.0/10 -j REJECT || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 127.0.0.0/8 -j REJECT || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 169.254.0.0/16 -j REJECT || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 172.16.0.0/12 -j REJECT || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 192.168.0.0/16 -j REJECT || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 224.0.0.0/4 -j REJECT || die
|
|
iptables_ -v -I BANLAN-f-${SUBNET_IFACE} -d 255.255.255.255 -j REJECT || die
|
|
|
|
iptables_ -I FORWARD -i ${SUBNET_IFACE} -j BANLAN-f-${SUBNET_IFACE} || die
|
|
|
|
iptables_ -N BANLAN-i-${SUBNET_IFACE}
|
|
#iptables_ -v -I BANLAN-i-${SUBNET_IFACE} -i ${SUBNET_IFACE} -j REJECT || die
|
|
iptables_ -v -I BANLAN-i-${SUBNET_IFACE} -i ${SUBNET_IFACE} ! -p icmp -j REJECT || die
|
|
|
|
iptables_ -I INPUT -i ${SUBNET_IFACE} -j BANLAN-i-${SUBNET_IFACE} || die
|
|
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -N BANLAN-f-${SUBNET_IFACE} || die
|
|
ip6tables_ -v -I BANLAN-f-${SUBNET_IFACE} -d fc00::/7 -j REJECT || die
|
|
ip6tables_ -v -I BANLAN-f-${SUBNET_IFACE} -d fe80::/10 -j REJECT || die
|
|
ip6tables_ -v -I BANLAN-f-${SUBNET_IFACE} -d ff00::/8 -j REJECT || die
|
|
ip6tables_ -v -I BANLAN-f-${SUBNET_IFACE} -d ::1 -j REJECT || die
|
|
ip6tables_ -v -I BANLAN-f-${SUBNET_IFACE} -d ::/128 -j REJECT || die
|
|
ip6tables_ -v -I BANLAN-f-${SUBNET_IFACE} -d ::ffff:0:0/96 -j REJECT || die
|
|
ip6tables_ -v -I BANLAN-f-${SUBNET_IFACE} -d ::ffff:0:0:0/96 -j REJECT || die
|
|
|
|
ip6tables_ -I FORWARD -i ${SUBNET_IFACE} -j BANLAN-f-${SUBNET_IFACE} || die
|
|
|
|
ip6tables_ -N BANLAN-i-${SUBNET_IFACE} || die
|
|
#ip6tables_ -v -I BANLAN-i-${SUBNET_IFACE} -i ${SUBNET_IFACE} -j REJECT || die
|
|
ip6tables_ -v -I BANLAN-i-${SUBNET_IFACE} -i ${SUBNET_IFACE} ! -p icmpv6 -j REJECT || die
|
|
|
|
ip6tables_ -I INPUT -i ${SUBNET_IFACE} -j BANLAN-i-${SUBNET_IFACE} || die
|
|
fi
|
|
}
|
|
stop_ban_lan() {
|
|
echo "iptables: Unban clients' LAN access"
|
|
|
|
iptables_ -D FORWARD -i ${SUBNET_IFACE} -j BANLAN-f-${SUBNET_IFACE}
|
|
|
|
iptables_ -F BANLAN-f-${SUBNET_IFACE}
|
|
iptables_ -X BANLAN-f-${SUBNET_IFACE}
|
|
|
|
iptables_ -D INPUT -i ${SUBNET_IFACE} -j BANLAN-i-${SUBNET_IFACE}
|
|
|
|
iptables_ -F BANLAN-i-${SUBNET_IFACE}
|
|
iptables_ -X BANLAN-i-${SUBNET_IFACE}
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -D FORWARD -i ${SUBNET_IFACE} -j BANLAN-f-${SUBNET_IFACE}
|
|
|
|
ip6tables_ -F BANLAN-f-${SUBNET_IFACE}
|
|
ip6tables_ -X BANLAN-f-${SUBNET_IFACE}
|
|
|
|
ip6tables_ -D INPUT -i ${SUBNET_IFACE} -j BANLAN-i-${SUBNET_IFACE}
|
|
|
|
ip6tables_ -F BANLAN-i-${SUBNET_IFACE}
|
|
ip6tables_ -X BANLAN-i-${SUBNET_IFACE}
|
|
fi
|
|
}
|
|
|
|
allow_dns_port() {
|
|
echo
|
|
echo "iptables: allow DNS port access"
|
|
iptables_ -v -I INPUT -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} -p tcp -m tcp --dport 53 -j ACCEPT || die
|
|
iptables_ -v -I INPUT -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} -p udp -m udp --dport 53 -j ACCEPT || die
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -v -I INPUT -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -d ${GATEWAY6} -p tcp -m tcp --dport 53 -j ACCEPT || die
|
|
ip6tables_ -v -I INPUT -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -d ${GATEWAY6} -p udp -m udp --dport 53 -j ACCEPT || die
|
|
fi
|
|
}
|
|
unallow_dns_port() {
|
|
echo "iptables: stop allowing DNS"
|
|
iptables_ -D INPUT -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} -p tcp -m tcp --dport 53 -j ACCEPT
|
|
iptables_ -D INPUT -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} -p udp -m udp --dport 53 -j ACCEPT
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -D INPUT -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -d ${GATEWAY6} -p tcp -m tcp --dport 53 -j ACCEPT
|
|
ip6tables_ -D INPUT -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -d ${GATEWAY6} -p udp -m udp --dport 53 -j ACCEPT
|
|
fi
|
|
}
|
|
|
|
start_catch_dns() {
|
|
echo
|
|
echo "iptables: redirect all TCP/UDP packet that destination port is 53"
|
|
iptables_ -v -t nat -I PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY} -p udp -m udp --dport 53 -j REDIRECT --to-ports 53 || die
|
|
iptables_ -v -t nat -I PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY} -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 53 || die
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -v -t nat -I PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY6} -p udp -m udp --dport 53 -j REDIRECT --to-ports 53 || die
|
|
ip6tables_ -v -t nat -I PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY6} -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 53 || die
|
|
fi
|
|
}
|
|
stop_catch_dns() {
|
|
echo "iptables: stop redirecting DNS queries"
|
|
iptables_ -t nat -D PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY} -p udp -m udp --dport 53 -j REDIRECT --to-ports 53
|
|
iptables_ -t nat -D PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY} -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 53
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -t nat -D PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY6} -p udp -m udp --dport 53 -j REDIRECT --to-ports 53
|
|
ip6tables_ -t nat -D PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY6} -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 53
|
|
fi
|
|
}
|
|
|
|
start_dhcp() {
|
|
echo
|
|
echo "iptables: allow DHCP port access"
|
|
iptables_ -v -I INPUT -i ${SUBNET_IFACE} -p udp -m udp --dport 67 -j ACCEPT || die
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -v -I INPUT -i ${SUBNET_IFACE} -p udp -m udp --dport 547 -j ACCEPT || die
|
|
fi
|
|
}
|
|
stop_dhcp() {
|
|
echo "iptables: stop dhcp"
|
|
iptables_ -D INPUT -i ${SUBNET_IFACE} -p udp -m udp --dport 67 -j ACCEPT
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -D INPUT -i ${SUBNET_IFACE} -p udp -m udp --dport 547 -j ACCEPT
|
|
fi
|
|
}
|
|
|
|
start_redsocks() {
|
|
echo
|
|
echo "iptables: transparent proxy non-LAN TCP/UDP traffic to port ${TP_PORT}"
|
|
if [[ $NO4 -eq 0 ]]; then
|
|
iptables_ -t nat -N REDSOCKS-${SUBNET_IFACE} || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 0.0.0.0/8 -j RETURN || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 10.0.0.0/8 -j RETURN || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 100.64.0.0/10 -j RETURN || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 127.0.0.0/8 -j RETURN || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 169.254.0.0/16 -j RETURN || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 172.16.0.0/12 -j RETURN || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 192.168.0.0/16 -j RETURN || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 224.0.0.0/4 -j RETURN || die
|
|
iptables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d 255.255.255.255 -j RETURN || die
|
|
|
|
iptables_ -v -t nat -A REDSOCKS-${SUBNET_IFACE} -p tcp -j REDIRECT --to-ports ${TP_PORT} || die
|
|
iptables_ -v -t nat -A REDSOCKS-${SUBNET_IFACE} -p udp -j REDIRECT --to-ports ${TP_PORT} || die
|
|
|
|
iptables_ -v -t nat -I PREROUTING -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -j REDSOCKS-${SUBNET_IFACE} || die
|
|
|
|
iptables_ -v -I INPUT -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -p tcp -m tcp --dport ${TP_PORT} -j ACCEPT || die
|
|
iptables_ -v -I INPUT -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -p udp -m udp --dport ${TP_PORT} -j ACCEPT || die
|
|
fi
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -t nat -N REDSOCKS-${SUBNET_IFACE} || die
|
|
ip6tables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d fc00::/7 -j RETURN || die
|
|
ip6tables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d fe80::/10 -j RETURN || die
|
|
ip6tables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d ff00::/8 -j RETURN || die
|
|
ip6tables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d ::1 -j RETURN || die
|
|
ip6tables_ -t nat -A REDSOCKS-${SUBNET_IFACE} -d :: -j RETURN || die
|
|
|
|
ip6tables_ -v -t nat -A REDSOCKS-${SUBNET_IFACE} -p tcp -j REDIRECT --to-ports ${TP_PORT} || die
|
|
ip6tables_ -v -t nat -A REDSOCKS-${SUBNET_IFACE} -p udp -j REDIRECT --to-ports ${TP_PORT} || die
|
|
|
|
ip6tables_ -v -t nat -I PREROUTING -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -j REDSOCKS-${SUBNET_IFACE} || die
|
|
|
|
ip6tables_ -v -I INPUT -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -p tcp -m tcp --dport ${TP_PORT} -j ACCEPT || die
|
|
ip6tables_ -v -I INPUT -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -p udp -m udp --dport ${TP_PORT} -j ACCEPT || die
|
|
fi
|
|
}
|
|
stop_redsocks() {
|
|
echo "iptables: stop transparent proxy"
|
|
if [[ $NO4 -eq 0 ]]; then
|
|
iptables_ -t nat -D PREROUTING -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -j REDSOCKS-${SUBNET_IFACE}
|
|
iptables_ -t nat -F REDSOCKS-${SUBNET_IFACE}
|
|
iptables_ -t nat -X REDSOCKS-${SUBNET_IFACE}
|
|
|
|
iptables_ -D INPUT -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -p tcp -m tcp --dport ${TP_PORT} -j ACCEPT
|
|
iptables_ -D INPUT -i ${SUBNET_IFACE} -s ${GATEWAY%.*}.0/24 -p udp -m udp --dport ${TP_PORT} -j ACCEPT
|
|
fi
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
ip6tables_ -t nat -D PREROUTING -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -j REDSOCKS-${SUBNET_IFACE}
|
|
ip6tables_ -t nat -F REDSOCKS-${SUBNET_IFACE}
|
|
ip6tables_ -t nat -X REDSOCKS-${SUBNET_IFACE}
|
|
|
|
ip6tables_ -D INPUT -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -p tcp -m tcp --dport ${TP_PORT} -j ACCEPT
|
|
ip6tables_ -D INPUT -i ${SUBNET_IFACE} -s ${PREFIX6}/64 -p udp -m udp --dport ${TP_PORT} -j ACCEPT
|
|
fi
|
|
}
|
|
|
|
kill_processes() {
|
|
#echo "Killing processes"
|
|
local x pid
|
|
for x in $CONFDIR/*.pid; do
|
|
# even if the $CONFDIR is empty, the for loop will assign
|
|
# a value in $x. so we need to check if the value is a file
|
|
if [[ -f $x ]] && sleep 0.3 && [[ -f $x ]]; then
|
|
pid=$(cat $x)
|
|
pn=$( ps -p $pid -o comm= )
|
|
#echo "Killing $pid $pn ... "
|
|
pkill -P $pid
|
|
kill $pid 2>/dev/null && ( echo "Killed $pid $pn" && rm $x ) || echo "Failed to kill $pid $pn, it may have exited"
|
|
fi
|
|
done
|
|
|
|
}
|
|
_cleanup() {
|
|
local x
|
|
|
|
ip addr flush ${SUBNET_IFACE}
|
|
|
|
if [[ -d "$CONFDIR/sys_6_conf_iface" ]]; then
|
|
cp -f "$CONFDIR/sys_6_conf_iface/*" "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/"
|
|
fi
|
|
rm -rf $CONFDIR
|
|
|
|
if [[ "$WIFI_IFACE" && "$NO_VIRT" -eq 0 ]]; then
|
|
ip link set down dev ${AP_IFACE}
|
|
iw dev ${VWIFI_IFACE} del
|
|
dealloc_iface $VWIFI_IFACE
|
|
else
|
|
if [[ -n "$NEW_MACADDR" ]]; then
|
|
ip link set dev ${TARGET_IFACE} address ${OLD_MACADDR} && echo "Restore ${TARGET_IFACE} to old MAC address ${OLD_MACADDR}"
|
|
fi
|
|
fi
|
|
|
|
|
|
if ! has_running_instance; then
|
|
echo "Exiting: This is the only running instance"
|
|
# kill common processes
|
|
for x in $COMMON_CONFDIR/*.pid; do
|
|
[[ -f $x ]] && kill -9 $(cat $x) && rm $x
|
|
done
|
|
|
|
rm -d $COMMON_CONFDIR/ifaces
|
|
rm -d $COMMON_CONFDIR
|
|
rm -d $TMPDIR
|
|
else
|
|
echo "Exiting: This is NOT the only running instance"
|
|
fi
|
|
|
|
nm_restore_manage
|
|
}
|
|
|
|
clean_iptables() {
|
|
|
|
if [[ "$SHARE_METHOD" == "nat" ]]; then
|
|
stop_nat
|
|
elif [[ "$SHARE_METHOD" == "redsocks" ]]; then
|
|
stop_redsocks
|
|
fi
|
|
|
|
if [[ "$DHCP_DNS" == "gateway" || "$DHCP_DNS6" == "gateway" ]]; then
|
|
unallow_dns_port
|
|
fi
|
|
|
|
[[ "$CATCH_DNS" -eq 1 ]] && stop_catch_dns
|
|
|
|
|
|
if [[ $NO_DNSMASQ -eq 0 ]]; then
|
|
stop_dhcp
|
|
fi
|
|
|
|
[[ "$BANLAN" -eq 1 ]] && stop_ban_lan
|
|
}
|
|
|
|
cleanup() {
|
|
trap "" SIGINT SIGUSR1 SIGUSR2 EXIT SIGTERM
|
|
echo
|
|
echo
|
|
echo "Doing cleanup.. "
|
|
kill_processes
|
|
clean_iptables 2> /dev/null
|
|
_cleanup 2> /dev/null
|
|
|
|
pgid=$(ps opgid= $$ |awk '{print $1}' )
|
|
kill -15 -$pgid
|
|
sleep 1
|
|
echo "Cleaning up done"
|
|
#kill -9 -$pgid
|
|
}
|
|
|
|
die() { # SIGUSR2
|
|
echo "Error occured"
|
|
[[ -n "$1" ]] && echo -e "\nERROR: $1\n" >&2
|
|
# send die signal to the main process
|
|
[[ $BASHPID -ne $$ ]] && kill -USR2 $$ || cleanup
|
|
exit 1
|
|
}
|
|
|
|
clean_exit() { # SIGUSR1
|
|
# send clean_exit signal to the main process
|
|
[[ $BASHPID -ne $$ ]] && kill -USR1 $$ || cleanup
|
|
exit 0
|
|
}
|
|
|
|
#== functions to deal with running instances
|
|
|
|
list_running_conf() {
|
|
local x
|
|
for x in $TMPDIR/lnxrouter.*; do
|
|
if [[ -f $x/pid && -f $x/subn_iface && -d /proc/$(cat $x/pid) ]]; then
|
|
echo "$x"
|
|
fi
|
|
done
|
|
}
|
|
|
|
list_running() {
|
|
local IFACE subn_iface x
|
|
for x in $(list_running_conf); do
|
|
IFACE=${x#*.}
|
|
IFACE=${IFACE%%.*}
|
|
subn_iface=$(cat $x/subn_iface)
|
|
|
|
if [[ $IFACE == $subn_iface ]]; then
|
|
echo $(cat $x/pid) $IFACE
|
|
else
|
|
echo $(cat $x/pid) $IFACE '('$(cat $x/subn_iface)')'
|
|
fi
|
|
done
|
|
}
|
|
|
|
get_subn_iface_from_pid() {
|
|
list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E "^${1} " | cut -d' ' -f2
|
|
}
|
|
|
|
get_pid_from_subn_iface() {
|
|
list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E " ${1}$" | cut -d' ' -f1
|
|
}
|
|
|
|
get_confdir_from_pid() {
|
|
local IFACE x
|
|
for x in $(list_running_conf); do
|
|
if [[ $(cat $x/pid) == "$1" ]]; then
|
|
echo "$x"
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
print_client_by_mac() {
|
|
local line ipaddr hostname
|
|
local mac="$1"
|
|
|
|
if [[ -f "$CONFDIR/dnsmasq.leases" ]]; then
|
|
line=$(grep " $mac " "$CONFDIR/dnsmasq.leases" | tail -n 1)
|
|
ipaddr=$(echo "$line" | cut -d' ' -f3)
|
|
hostname=$(echo "$line" | cut -d' ' -f4)
|
|
fi
|
|
|
|
[[ -z "$ipaddr" ]] && ipaddr="*"
|
|
[[ -z "$hostname" ]] && hostname="*"
|
|
|
|
printf "%-20s %-18s %s\n" "$mac" "$ipaddr" "$hostname"
|
|
}
|
|
|
|
print_clients_in_leases() {
|
|
local line ipaddr hostname
|
|
local mac
|
|
|
|
if [[ -f "$CONFDIR/dnsmasq.leases" ]]; then
|
|
while read line
|
|
do
|
|
mac=$(echo "$line" | cut -d' ' -f2)
|
|
ipaddr=$(echo "$line" | cut -d' ' -f3)
|
|
hostname=$(echo "$line" | cut -d' ' -f4)
|
|
|
|
printf "%-20s %-18s %s\n" "MAC" "IP" "Hostname"
|
|
printf "%-20s %-18s %s\n" "$mac" "$ipaddr" "$hostname"
|
|
done < "$CONFDIR/dnsmasq.leases"
|
|
fi
|
|
}
|
|
|
|
list_clients() {
|
|
local subn_iface pid
|
|
|
|
# If PID is given, get the associated wifi iface
|
|
if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then
|
|
pid="$1"
|
|
subn_iface=$(get_subn_iface_from_pid "$pid")
|
|
[[ -z "$subn_iface" ]] && die "'$pid' is not the pid of a running $PROGNAME instance."
|
|
fi
|
|
|
|
[[ -z "$subn_iface" ]] && subn_iface="$1"
|
|
|
|
[[ -z "$pid" ]] && pid=$(get_pid_from_subn_iface "$subn_iface")
|
|
[[ -z "$pid" ]] && die "'$subn_iface' is not used from $PROGNAME instance.\n\
|
|
Maybe you need to pass the virtual interface instead.\n\
|
|
Use --list-running to find it out."
|
|
[[ -z "$CONFDIR" ]] && CONFDIR=$(get_confdir_from_pid "$pid")
|
|
|
|
if [[ -f "$CONFDIR/hostapd.conf" && $USE_IWCONFIG -eq 0 ]]; then
|
|
local awk_cmd='($1 ~ /Station$/) {print $2}'
|
|
local client_list=$(iw dev "$subn_iface" station dump | awk "$awk_cmd")
|
|
|
|
if [[ -z "$client_list" ]]; then
|
|
echo "No clients connected"
|
|
return
|
|
fi
|
|
|
|
printf "%-20s %-18s %s\n" "MAC" "IP" "Hostname"
|
|
|
|
local mac
|
|
for mac in $client_list; do
|
|
print_client_by_mac $mac
|
|
done
|
|
else
|
|
echo "Listing clients via DNS lease file. non-DHCPed clients won't be showed"
|
|
print_clients_in_leases
|
|
fi
|
|
}
|
|
|
|
has_running_instance() {
|
|
local PID x
|
|
|
|
for x in $TMPDIR/lnxrouter.*; do
|
|
if [[ -f $x/pid ]]; then
|
|
PID=$(cat $x/pid)
|
|
if [[ -d /proc/$PID ]]; then
|
|
return 0
|
|
fi
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
is_running_pid() {
|
|
list_running | grep -E "^${1} " > /dev/null 2>&1
|
|
}
|
|
|
|
send_stop() {
|
|
local x
|
|
|
|
# send stop signal to specific pid
|
|
if is_running_pid $1; then
|
|
kill -USR1 $1
|
|
return
|
|
fi
|
|
|
|
# send stop signal to specific interface
|
|
for x in $(list_running | grep -E " \(?${1}( |\)?\$)" | cut -f1 -d' '); do
|
|
kill -USR1 $x
|
|
done
|
|
}
|
|
|
|
|
|
## ========================================================
|
|
## ========================================================
|
|
# decide linux-router's global temporary path for all instances
|
|
# this is different and should be before config-saving dir. The latter is for one instance
|
|
init_tmpdir(){
|
|
if [[ -d /dev/shm ]]; then
|
|
TMPD=/dev/shm
|
|
elif [[ -d /run/shm ]]; then
|
|
TMPD=/run/shm
|
|
else
|
|
TMPD=/tmp
|
|
fi
|
|
TMPDIR=$TMPD/lnxrouter_tmp
|
|
}
|
|
|
|
#======
|
|
|
|
check_other_functions(){
|
|
if [[ $LIST_RUNNING -eq 1 ]]; then
|
|
echo -e "List of running $PROGNAME instances:\n"
|
|
list_running
|
|
exit 0
|
|
fi
|
|
|
|
if [[ -n "$LIST_CLIENTS_ID" ]]; then
|
|
list_clients "$LIST_CLIENTS_ID"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ $(id -u) -ne 0 ]]; then
|
|
echo "You must run it as root." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "$STOP_ID" ]]; then
|
|
echo "Trying to kill $PROGNAME instance associated with $STOP_ID..."
|
|
send_stop "$STOP_ID"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
|
|
daemonizing_check(){
|
|
if [[ $DAEMONIZE -eq 1 && $RUNNING_AS_DAEMON -eq 0 ]]; then
|
|
echo "Running as Daemon..."
|
|
# run a detached lnxrouter
|
|
RUNNING_AS_DAEMON=1 setsid "$0" "${ARGS[@]}" &
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
#============================
|
|
prepare_wifi() {
|
|
if [[ $FREQ_BAND != 2.4 && $FREQ_BAND != 5 ]]; then
|
|
echo "ERROR: Invalid frequency band" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $CHANNEL == default ]]; then
|
|
if [[ $FREQ_BAND == 2.4 ]]; then
|
|
CHANNEL=1
|
|
else
|
|
CHANNEL=36
|
|
fi
|
|
fi
|
|
|
|
if [[ $FREQ_BAND != 5 && $CHANNEL -gt 14 ]]; then
|
|
echo "Channel number is greater than 14, assuming 5GHz frequency band"
|
|
FREQ_BAND=5
|
|
fi
|
|
|
|
if ! can_be_ap ${WIFI_IFACE}; then
|
|
echo "ERROR: Your adapter does not support AP (master) mode" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! can_be_sta_and_ap ${WIFI_IFACE}; then
|
|
if is_wifi_connected ${WIFI_IFACE}; then
|
|
echo "ERROR: Your adapter can not be a station (i.e. be connected) and an AP at the same time" >&2
|
|
exit 1
|
|
elif [[ $NO_VIRT -eq 0 ]]; then
|
|
echo "WARN: Your adapter does not fully support AP virtual interface, enabling --no-virt" >&2
|
|
NO_VIRT=1
|
|
fi
|
|
fi
|
|
|
|
HOSTAPD=$(which hostapd)
|
|
|
|
if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^(8192[cd][ue]|8723a[sue])$ ]]; then
|
|
if ! strings "$HOSTAPD" | grep -m1 rtl871xdrv > /dev/null 2>&1; then
|
|
echo "ERROR: You need to patch your hostapd with rtl871xdrv patches." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $DRIVER != "rtl871xdrv" ]]; then
|
|
echo "WARN: Your adapter needs rtl871xdrv, enabling --driver=rtl871xdrv" >&2
|
|
DRIVER=rtl871xdrv
|
|
fi
|
|
fi
|
|
|
|
if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then
|
|
echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $USE_PSK -eq 0 ]]; then
|
|
if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then
|
|
echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2
|
|
exit 1
|
|
fi
|
|
elif [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then
|
|
echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^rtl[0-9].*$ ]]; then
|
|
if [[ $WPA_VERSION == '1' || $WPA_VERSION == '1+2' ]]; then
|
|
echo "WARN: Realtek drivers usually have problems with WPA1, WPA2 is recommended" >&2
|
|
fi
|
|
echo "WARN: If AP doesn't work, read https://github.com/oblique/create_ap/blob/master/howto/realtek.md" >&2
|
|
fi
|
|
}
|
|
|
|
check_if_new_mac_valid() {
|
|
if ! is_unicast_macaddr "$NEW_MACADDR"; then
|
|
echo "ERROR: The first byte of MAC address (${NEW_MACADDR}) must be even" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $(get_all_macaddrs | grep -c ${NEW_MACADDR}) -ne 0 ]]; then
|
|
echo "WARN: MAC address '${NEW_MACADDR}' already exists" >&2
|
|
fi
|
|
}
|
|
|
|
decide_target_interface() {
|
|
TARGET_IFACE= # This is the existing physical interface to use
|
|
if [[ $CONN_IFACE ]]; then
|
|
TARGET_IFACE=$CONN_IFACE
|
|
elif [[ $WIFI_IFACE ]]; then
|
|
TARGET_IFACE=$WIFI_IFACE
|
|
else
|
|
echo "No target interface specified" 1>&2
|
|
exit 1
|
|
fi
|
|
echo "Target interface is ${TARGET_IFACE}"
|
|
}
|
|
|
|
decide_ip_addresses() {
|
|
if [[ ! -n $GATEWAY ]]; then
|
|
generate_random_ip4
|
|
echo "Use random IPv4 address $GATEWAY"
|
|
fi
|
|
|
|
if [[ $IPV6 -eq 1 && ! -n $PREFIX6 ]]; then
|
|
generate_random_ip6
|
|
echo "Use random IPv6 address ${PREFIX6}${IID6}"
|
|
fi
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
GATEWAY6=${PREFIX6}${IID6}
|
|
fi
|
|
}
|
|
|
|
prepare_wifi_interface() {
|
|
if [[ $USE_IWCONFIG -eq 0 ]]; then
|
|
iw dev ${WIFI_IFACE} set power_save off
|
|
fi
|
|
|
|
if [[ $NO_VIRT -eq 0 ]]; then
|
|
## Generate virtual wifi interface
|
|
|
|
VWIFI_IFACE=$(alloc_new_iface)
|
|
|
|
if is_wifi_connected ${WIFI_IFACE}; then
|
|
WIFI_IFACE_FREQ=$(iw dev ${WIFI_IFACE} link | grep -i freq | awk '{print $2}')
|
|
WIFI_IFACE_CHANNEL=$(ieee80211_frequency_to_channel ${WIFI_IFACE_FREQ})
|
|
echo "${WIFI_IFACE} already in channel ${WIFI_IFACE_CHANNEL} (${WIFI_IFACE_FREQ} MHz)"
|
|
if is_5ghz_frequency $WIFI_IFACE_FREQ; then
|
|
FREQ_BAND=5
|
|
else
|
|
FREQ_BAND=2.4
|
|
fi
|
|
if [[ $WIFI_IFACE_CHANNEL -ne $CHANNEL ]]; then
|
|
echo "Channel fallback to ${WIFI_IFACE_CHANNEL}"
|
|
CHANNEL=$WIFI_IFACE_CHANNEL
|
|
else
|
|
echo
|
|
fi
|
|
fi
|
|
|
|
VIRTDIEMSG="Maybe your WiFi adapter does not fully support virtual interfaces.
|
|
Try again with --no-virt."
|
|
echo "Creating a virtual WiFi interface... "
|
|
|
|
if iw dev ${WIFI_IFACE} interface add ${VWIFI_IFACE} type __ap; then
|
|
echo "${VWIFI_IFACE} created."
|
|
sleep 2
|
|
else
|
|
VWIFI_IFACE=
|
|
die "$VIRTDIEMSG"
|
|
fi
|
|
OLD_MACADDR=$(get_macaddr ${VWIFI_IFACE})
|
|
if [[ -z "$NEW_MACADDR" && $(get_all_macaddrs | grep -c ${OLD_MACADDR}) -ne 1 ]]; then
|
|
NEW_MACADDR=$(get_new_macaddr ${VWIFI_IFACE})
|
|
fi
|
|
AP_IFACE=${VWIFI_IFACE}
|
|
else
|
|
OLD_MACADDR=$(get_macaddr ${WIFI_IFACE})
|
|
AP_IFACE=${WIFI_IFACE}
|
|
fi
|
|
}
|
|
|
|
decide_subnet_interface() {
|
|
if [[ $WIFI_IFACE ]]; then
|
|
SUBNET_IFACE=${AP_IFACE}
|
|
else
|
|
SUBNET_IFACE=${TARGET_IFACE}
|
|
fi
|
|
}
|
|
|
|
write_hostapd_conf() {
|
|
if [[ -n "$COUNTRY" && $USE_IWCONFIG -eq 0 ]]; then
|
|
iw reg set "$COUNTRY"
|
|
fi
|
|
|
|
can_transmit_to_channel ${AP_IFACE} ${CHANNEL} || die "Your adapter can not transmit to channel ${CHANNEL}, frequency band ${FREQ_BAND}GHz."
|
|
|
|
[[ $HIDDEN -eq 1 ]] && echo "Access Point's SSID is hidden!"
|
|
|
|
[[ $MAC_FILTER -eq 1 ]] && echo "MAC address filtering is enabled!"
|
|
|
|
[[ $ISOLATE_CLIENTS -eq 1 ]] && echo "Access Point's clients will be isolated!"
|
|
# TODO: move above code
|
|
|
|
# hostapd config
|
|
cat <<- EOF > "$CONFDIR/hostapd.conf"
|
|
beacon_int=100
|
|
ssid=${SSID}
|
|
interface=${AP_IFACE}
|
|
driver=${DRIVER}
|
|
channel=${CHANNEL}
|
|
ctrl_interface=$CONFDIR/hostapd_ctrl
|
|
ctrl_interface_group=0
|
|
ignore_broadcast_ssid=$HIDDEN
|
|
ap_isolate=$ISOLATE_CLIENTS
|
|
EOF
|
|
|
|
if [[ -n "$COUNTRY" ]]; then
|
|
cat <<- EOF >> "$CONFDIR/hostapd.conf"
|
|
country_code=${COUNTRY}
|
|
ieee80211d=1
|
|
EOF
|
|
fi
|
|
|
|
if [[ $FREQ_BAND == 2.4 ]]; then
|
|
echo "hw_mode=g" >> "$CONFDIR/hostapd.conf"
|
|
else
|
|
echo "hw_mode=a" >> "$CONFDIR/hostapd.conf"
|
|
fi
|
|
|
|
if [[ $MAC_FILTER -eq 1 ]]; then
|
|
cat <<- EOF >> "$CONFDIR/hostapd.conf"
|
|
macaddr_acl=${MAC_FILTER}
|
|
accept_mac_file=${MAC_FILTER_ACCEPT}
|
|
EOF
|
|
fi
|
|
|
|
if [[ $IEEE80211N -eq 1 ]]; then
|
|
cat <<- EOF >> "$CONFDIR/hostapd.conf"
|
|
ieee80211n=1
|
|
ht_capab=${HT_CAPAB}
|
|
EOF
|
|
fi
|
|
|
|
if [[ $IEEE80211AC -eq 1 ]]; then
|
|
echo "ieee80211ac=1" >> "$CONFDIR/hostapd.conf"
|
|
fi
|
|
|
|
if [[ -n "$VHT_CAPAB" ]]; then
|
|
echo "vht_capab=${VHT_CAPAB}" >> "$CONFDIR/hostapd.conf"
|
|
fi
|
|
|
|
if [[ $IEEE80211N -eq 1 ]] || [[ $IEEE80211AC -eq 1 ]]; then
|
|
echo "wmm_enabled=1" >> "$CONFDIR/hostapd.conf"
|
|
fi
|
|
|
|
if [[ -n "$PASSPHRASE" ]]; then
|
|
[[ "$WPA_VERSION" == "1+2" ]] && WPA_VERSION=3
|
|
if [[ $USE_PSK -eq 0 ]]; then
|
|
WPA_KEY_TYPE=passphrase
|
|
else
|
|
WPA_KEY_TYPE=psk
|
|
fi
|
|
cat <<- EOF >> "$CONFDIR/hostapd.conf"
|
|
wpa=${WPA_VERSION}
|
|
wpa_${WPA_KEY_TYPE}=${PASSPHRASE}
|
|
wpa_key_mgmt=WPA-PSK
|
|
wpa_pairwise=CCMP
|
|
rsn_pairwise=CCMP
|
|
EOF
|
|
else
|
|
echo "WARN: Wifi is not protected by password" >&2
|
|
fi
|
|
chmod 600 "$CONFDIR/hostapd.conf"
|
|
}
|
|
|
|
write_dnsmasq_conf() {
|
|
if grep "^nobody:" /etc/group >/dev/null 2>&1 ; then
|
|
NOBODY_GROUP="nobody"
|
|
else
|
|
NOBODY_GROUP="nogroup"
|
|
fi
|
|
cat <<- EOF > "$CONFDIR/dnsmasq.conf"
|
|
user=nobody
|
|
group=$NOBODY_GROUP
|
|
bind-dynamic
|
|
listen-address=${GATEWAY}
|
|
interface=$SUBNET_IFACE
|
|
except-interface=lo
|
|
no-dhcp-interface=lo
|
|
dhcp-range=${GATEWAY%.*}.10,${GATEWAY%.*}.250,255.255.255.0
|
|
dhcp-option-force=option:router,${GATEWAY}
|
|
#log-dhcp
|
|
log-facility=/dev/stdout
|
|
bogus-priv
|
|
domain-needed
|
|
EOF
|
|
# 'log-dhcp'(Extra logging for DHCP) shows too much logs.
|
|
# if use '-d', 'log-facility' should = /dev/null
|
|
if [[ $SHARE_METHOD == "none" ]]; then
|
|
echo "no-resolv" >> "$CONFDIR/dnsmasq.conf"
|
|
echo "no-poll" >> "$CONFDIR/dnsmasq.conf"
|
|
fi
|
|
if [[ "$DHCP_DNS" != "no" ]]; then
|
|
if [[ "$DHCP_DNS" == "gateway" ]]; then
|
|
dns_offer="$GATEWAY"
|
|
else
|
|
dns_offer="$DHCP_DNS"
|
|
fi
|
|
echo "dhcp-option-force=option:dns-server,${dns_offer}" >> "$CONFDIR/dnsmasq.conf"
|
|
fi
|
|
|
|
if [[ ! "$dnsmasq_NO_DNS" -eq 0 ]]; then
|
|
echo "port=0" >> "$CONFDIR/dnsmasq.conf"
|
|
fi
|
|
|
|
[[ -n "$MTU" ]] && echo "dhcp-option-force=option:mtu,${MTU}" >> "$CONFDIR/dnsmasq.conf"
|
|
[[ $ETC_HOSTS -eq 0 ]] && echo no-hosts >> "$CONFDIR/dnsmasq.conf"
|
|
[[ -n "$ADDN_HOSTS" ]] && echo "addn-hosts=${ADDN_HOSTS}" >> "$CONFDIR/dnsmasq.conf"
|
|
if [[ "$THISHOSTNAME" ]]; then
|
|
[[ "$THISHOSTNAME" == "-" ]] && THISHOSTNAME="$(cat /etc/hostname)"
|
|
echo "interface-name=$THISHOSTNAME,$SUBNET_IFACE" >> "$CONFDIR/dnsmasq.conf"
|
|
fi
|
|
if [[ ! "$SHOW_DNS_QUERY" -eq 0 ]]; then
|
|
echo log-queries=extra >> "$CONFDIR/dnsmasq.conf"
|
|
fi
|
|
|
|
if [[ $DNS ]]; then
|
|
DNS_count=$(echo "$DNS" | awk -F, '{print NF}')
|
|
for (( i=1;i<=DNS_count;i++ )); do
|
|
sep_ip_port "$(echo $DNS | cut -d, -f$i)" DNS_IP DNS_PORT
|
|
[[ "$DNS_PORT" ]] && DNS_PORT_D="#$DNS_PORT"
|
|
echo "server=${DNS_IP}${DNS_PORT_D}" >> "$CONFDIR/dnsmasq.conf"
|
|
done
|
|
|
|
cat <<- EOF >> "$CONFDIR/dnsmasq.conf"
|
|
no-resolv
|
|
no-poll
|
|
EOF
|
|
fi
|
|
if [[ $IPV6 -eq 1 ]];then
|
|
cat <<- EOF >> "$CONFDIR/dnsmasq.conf"
|
|
listen-address=${GATEWAY6}
|
|
enable-ra
|
|
#quiet-ra
|
|
dhcp-range=interface:${SUBNET_IFACE},::,::ffff:ffff:ffff:ffff,constructor:${SUBNET_IFACE},ra-stateless,64
|
|
EOF
|
|
if [[ "$DHCP_DNS6" != "no" ]]; then
|
|
if [[ "$DHCP_DNS6" == "gateway" ]]; then
|
|
dns_offer6="[$GATEWAY6]"
|
|
else
|
|
dns_offer6="$DHCP_DNS6"
|
|
fi
|
|
echo "dhcp-option=option6:dns-server,${dns_offer6}" >> "$CONFDIR/dnsmasq.conf"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
run_wifi_ap_processes() {
|
|
if [[ $NO_HAVEGED -eq 0 ]]; then
|
|
haveged_watchdog &
|
|
HAVEGED_WATCHDOG_PID=$!
|
|
echo "$HAVEGED_WATCHDOG_PID" > "$CONFDIR/haveged_watchdog.pid"
|
|
echo "haveged_watchdog PID: $HAVEGED_WATCHDOG_PID"
|
|
fi
|
|
|
|
# start access point
|
|
#echo "hostapd command-line interface: hostapd_cli -p $CONFDIR/hostapd_ctrl"
|
|
# start hostapd (use stdbuf when available for no delayed output in programs that redirect stdout)
|
|
STDBUF_PATH=`which stdbuf`
|
|
if [ $? -eq 0 ]; then
|
|
STDBUF_PATH=$STDBUF_PATH" -oL"
|
|
fi
|
|
echo
|
|
echo "Starting hostapd"
|
|
# hostapd '-P' works only when use '-B' (run in background)
|
|
$STDBUF_PATH hostapd $HOSTAPD_DEBUG_ARGS -P "$CONFDIR/hostapd.pid" "$CONFDIR/hostapd.conf" &
|
|
HOSTAPD_PID=$!
|
|
echo "$HOSTAPD_PID" > "$CONFDIR/hostapd.pid"
|
|
echo "hostapd PID: $HOSTAPD_PID"
|
|
#while [[ ! -f $CONFDIR/hostapd.pid ]]; do
|
|
# sleep 1
|
|
#done
|
|
#echo -n "hostapd PID: " ; cat $CONFDIR/hostapd.pid
|
|
( while [ -e /proc/$HOSTAPD_PID ]; do sleep 10; done ; die "hostapd exited" ) &
|
|
|
|
sleep 3
|
|
}
|
|
|
|
backup_interface_ipv6_status() {
|
|
mkdir "$CONFDIR/sys_6_conf_iface"
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
cp "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/accept_ra" \
|
|
"/proc/sys/net/ipv6/conf/$SUBNET_IFACE/use_tempaddr" \
|
|
"/proc/sys/net/ipv6/conf/$SUBNET_IFACE/addr_gen_mode" \
|
|
"$CONFDIR/sys_6_conf_iface/"
|
|
|
|
echo 0 > "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/accept_ra"
|
|
echo 0 > "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/use_tempaddr"
|
|
echo 0 > "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/addr_gen_mode"
|
|
|
|
# TODO: move this code
|
|
ip -6 addr add ${GATEWAY6}/64 dev ${SUBNET_IFACE} || die "Failed setting ${SUBNET_IFACE} IPv6 address"
|
|
else
|
|
# TODO: also need to deal with "disable_ipv6" if ipv6 is enabled using this script
|
|
cp "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/disable_ipv6" "$CONFDIR/sys_6_conf_iface/"
|
|
echo 1 > "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/disable_ipv6"
|
|
fi
|
|
}
|
|
|
|
|
|
start_dnsmasq() {
|
|
start_dhcp
|
|
|
|
if which complain > /dev/null 2>&1; then
|
|
# openSUSE's apparmor does not allow dnsmasq to read files.
|
|
# remove restriction.
|
|
complain dnsmasq
|
|
fi
|
|
|
|
echo
|
|
echo "Starting dnsmasq"
|
|
# Using '-d'(no daemon) dnsmasq will not turn into 'nobody'
|
|
# '-x' works only when no '-d'
|
|
dnsmasq -k -C "$CONFDIR/dnsmasq.conf" -x "$CONFDIR/dnsmasq.pid" -l "$CONFDIR/dnsmasq.leases" &
|
|
#####DNSMASQ_PID=$! # only when with '-d'
|
|
######echo "dnsmasq PID: $DNSMASQ_PID" # only when with '-d'
|
|
i=0; while [[ ! -f "$CONFDIR/dnsmasq.pid" ]]; do
|
|
sleep 1
|
|
i=$((i + 1))
|
|
if [[ $i -gt 10 ]]; then die "Couldn't get dnsmasq PID" ; fi
|
|
done
|
|
echo -n "dnsmasq PID: " ; cat "$CONFDIR/dnsmasq.pid"
|
|
######(wait $DNSMASQ_PID ; die "dnsmasq failed") & # wait can't deal with non-child
|
|
( while [ -e "/proc/$DNSMASQ_PID" ]; do sleep 10; done ; die "dnsmasq exited" ) &
|
|
sleep 2
|
|
}
|
|
|
|
check_if_need_rfkill_unblock_wifi() {
|
|
if which rfkill > /dev/null 2>&1 ; then
|
|
PHY=$(get_phy_device ${SUBNET_IFACE})
|
|
[[ -n $PHY ]] && rfkill unblock $(rfkill | grep $PHY | awk '{print $1}') >/dev/null 2>&1
|
|
fi
|
|
}
|
|
|
|
#=========== Above are functions ======================
|
|
#=========== Executing begin ==============================
|
|
|
|
# if empty option, show usage and exit
|
|
check_empty_option "$@"
|
|
|
|
# TODO: are some global variables are still defined in those following code?
|
|
define_global_variables
|
|
|
|
ARGS=( "$@" )
|
|
|
|
parse_user_options "$@"
|
|
|
|
# check if networkManager running
|
|
nm_initcheck
|
|
|
|
init_tmpdir
|
|
|
|
# if user choose to deal with running instances, will exit after this
|
|
check_other_functions
|
|
|
|
# if user choose to daemonize, will start new background process and exit this
|
|
daemonizing_check
|
|
|
|
# check if wifi will work on this system and user settings
|
|
[[ $WIFI_IFACE ]] && prepare_wifi
|
|
|
|
[[ "$USE_RANDOM_MAC" -eq 1 ]] && generate_random_mac
|
|
|
|
[[ -n "$NEW_MACADDR" ]] && check_if_new_mac_valid
|
|
|
|
# checks finished
|
|
|
|
## ===== Above don't echo anything if no warning or error====================
|
|
## ========================================================
|
|
echo "PID: $$"
|
|
|
|
decide_target_interface
|
|
|
|
[[ "$USE_RANDOM_MAC" -eq 1 ]] && echo "Use random MAC address $NEW_MACADDR"
|
|
|
|
decide_ip_addresses
|
|
|
|
# TODO: should this be some other place?
|
|
[[ $TP_PORT ]] && SHARE_METHOD=redsocks
|
|
|
|
# if user choose to make DHCP to tell clients to use other DNS, we don't have to serve DNS
|
|
[[ $DHCP_DNS != 'gateway' && $DHCP_DNS6 != 'gateway' ]] && dnsmasq_NO_DNS=1
|
|
|
|
#===========================================================
|
|
#==== begin to do some change on config files and system===
|
|
|
|
trap "cleanup" EXIT
|
|
trap "clean_exit" SIGINT SIGUSR1 SIGTERM
|
|
trap "die" SIGUSR2
|
|
|
|
mkdir -p "$TMPDIR"
|
|
chmod 755 "$TMPDIR" 2>/dev/null
|
|
cd "$TMPDIR" || die "Couldn't change directory to linux-router's temporary path"
|
|
|
|
CONFDIR="$(mktemp -d $TMPDIR/lnxrouter.${TARGET_IFACE}.conf.XXX)"
|
|
chmod 755 "$CONFDIR"
|
|
#echo "Config dir: $CONFDIR"
|
|
echo $$ > "$CONFDIR/pid"
|
|
|
|
|
|
COMMON_CONFDIR="$TMPDIR/lnxrouter_common.conf"
|
|
mkdir -p "$COMMON_CONFDIR"
|
|
|
|
[[ $WIFI_IFACE ]] && prepare_wifi_interface
|
|
|
|
decide_subnet_interface
|
|
|
|
echo "$SUBNET_IFACE" > "$CONFDIR/subn_iface"
|
|
|
|
[[ $WIFI_IFACE ]] && write_hostapd_conf
|
|
#===================================================
|
|
#===================================================
|
|
|
|
# set interface unmanaged by networkManager
|
|
if [[ $NM_RUNNING -eq 1 ]] && nm_knows $TARGET_IFACE ; then
|
|
nm_set_unmanaged ${SUBNET_IFACE}
|
|
fi
|
|
|
|
[[ $NO_DNSMASQ -eq 0 ]] && write_dnsmasq_conf
|
|
#===========================
|
|
|
|
# initialize subnet interface
|
|
# put subnet interface down first
|
|
ip link set down dev ${SUBNET_IFACE} || die "Failed setting ${SUBNET_IFACE} down"
|
|
# flush IPs of subnet interface
|
|
ip addr flush ${SUBNET_IFACE} || die "Failed flush ${SUBNET_IFACE} IP"
|
|
|
|
# set subnet mac if needed
|
|
if [[ -n "$NEW_MACADDR" ]] ; then
|
|
ip link set dev ${SUBNET_IFACE} address ${NEW_MACADDR} || die "Failed setting new MAC address"
|
|
fi
|
|
|
|
[[ $WIFI_IFACE ]] && check_if_need_rfkill_unblock_wifi
|
|
|
|
# bring subnet interface up
|
|
ip link set up dev ${SUBNET_IFACE} || die "Failed bringing ${SUBNET_IFACE} up"
|
|
|
|
# hostapd , haveged
|
|
[[ $WIFI_IFACE ]] && run_wifi_ap_processes
|
|
|
|
# add ipv4 address to subnet interface
|
|
# TODO: move ipv6 code here
|
|
ip addr add ${GATEWAY}/24 broadcast ${GATEWAY%.*}.255 dev ${SUBNET_IFACE} || die "Failed setting ${SUBNET_IFACE} IPv4 address"
|
|
|
|
backup_interface_ipv6_status
|
|
|
|
# enable Internet sharing
|
|
if [[ "$SHARE_METHOD" == "none" ]]; then
|
|
echo "No Internet sharing"
|
|
[[ "$BANLAN" -eq 1 ]] && start_ban_lan
|
|
elif [[ "$SHARE_METHOD" == "nat" ]]; then
|
|
[[ "$INTERNET_IFACE" && "$dnsmasq_NO_DNS" -eq 0 ]] && echo -e "\nWARN: You specified Internet interface but this host is providing local DNS, queries may leak to other interfaces!!!\n" >&2
|
|
start_nat
|
|
[[ "$BANLAN" -eq 1 ]] && start_ban_lan
|
|
echo 1 > "/proc/sys/net/ipv4/ip_forward" || die "Failed enabling system ipv4 forwarding"
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
echo 1 > "/proc/sys/net/ipv6/conf/all/forwarding" || die "Failed enabling system ipv6 forwarding"
|
|
fi
|
|
# to enable clients to establish PPTP connections we must
|
|
# load nf_nat_pptp module
|
|
modprobe nf_nat_pptp > /dev/null 2>&1
|
|
elif [[ "$SHARE_METHOD" == "redsocks" ]]; then
|
|
if [[ $IPV6 -eq 1 ]]; then
|
|
echo 1 > "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/forwarding" || die "Failed enabling $SUBNET_IFACE ipv6 forwarding"
|
|
fi
|
|
[[ "$dnsmasq_NO_DNS" -eq 0 && ! $DNS ]] && echo -e "\nWARN: You are using transparent proxy but this host is providing local DNS, this may cause privacy leak !!!\n" >&2
|
|
|
|
[[ "$BANLAN" -eq 1 ]] && start_ban_lan
|
|
start_redsocks
|
|
fi
|
|
|
|
# start dhcp + dns (optional)
|
|
|
|
# allow dns port input even if we don't run dnsmasq
|
|
# user can serve their own dns server
|
|
[[ "$DHCP_DNS" == "gateway" || "$DHCP_DNS6" == "gateway" ]] && allow_dns_port
|
|
|
|
[[ "$CATCH_DNS" -eq 1 ]] && start_catch_dns
|
|
|
|
[[ $NO_DNSMASQ -eq 0 ]] && start_dnsmasq
|
|
|
|
echo
|
|
echo "== Setting up completed, now linux-router is working =="
|
|
|
|
#============================================================
|
|
#============================================================
|
|
#============================================================
|
|
|
|
show_qr() {
|
|
local T S P H
|
|
S="$SSID"
|
|
if [[ -n "$PASSPHRASE" ]]; then
|
|
T="WPA"
|
|
P="$PASSPHRASE"
|
|
else
|
|
T="nopass"
|
|
fi
|
|
[[ "$HIDDEN" -eq 1 ]] && H="true"
|
|
echo "Scan QR code on phone to connect to WiFi"
|
|
qrencode -m 2 -t ANSIUTF8 "WIFI:T:${T};S:${S};P:${P};H:${H};"
|
|
echo "Use this command to save QR code to image file:"
|
|
echo " qrencode -m 2 -o <file> \"WIFI:T:${T};S:${S};P:${P};H:${H};\""
|
|
}
|
|
|
|
[[ "$QR" -eq 1 ]] && show_qr
|
|
|
|
# need loop to keep this script running
|
|
bash -c "while :; do sleep 8000 ; done " &
|
|
KEEP_RUNNING_PID=$!
|
|
echo "$KEEP_RUNNING_PID" > "$CONFDIR/keep_running.pid"
|
|
wait $KEEP_RUNNING_PID
|
|
|
|
clean_exit
|