2018-08-31 05:41:06 -05:00
#!/bin/bash
2024-01-19 00:33:25 -06:00
VERSION=0.7.3
2024-01-13 18:49:38 -06:00
PROGNAME="$(basename "$0")"
2018-08-31 05:41:06 -05:00
export LC_ALL=C
SCRIPT_UMASK=0122
umask $SCRIPT_UMASK
2021-08-28 21:24:50 -05:00
phead() {
echo "linux-router $VERSION (https://github.com/garywill/linux-router)"
}
2021-11-06 21:28:36 -05:00
phead2() {
echo "Released under LGPL, with no warranty. Use on your own risk."
}
2018-08-31 05:41:06 -05:00
usage() {
2021-08-28 21:24:50 -05:00
phead
2021-11-06 21:28:36 -05:00
phead2
2018-08-31 05:41:06 -05:00
cat << EOF
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
2021-11-06 21:31:15 -05:00
(To create WiFi hotspot use '--ap' instead)
2018-08-31 05:41:06 -05:00
-o <interface> Specify an inteface to provide Internet from.
2021-11-06 21:28:36 -05:00
(See Notice 1)
2018-08-31 05:41:06 -05:00
(Note using this with default DNS option may leak
queries to other interfaces)
2020-12-24 06:36:09 -06:00
-n Do not provide Internet (See Notice 1)
2020-12-24 06:36:09 -06:00
--ban-priv Disallow clients to access my private network
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
-g <ip> This host's IPv4 address in subnet (mask is /24)
2021-04-16 23:29:53 -05:00
(example: '192.168.5.1' or '5' shortly)
2018-08-31 05:41:06 -05:00
-6 Enable IPv6 (NAT)
2020-12-24 06:36:09 -06:00
--no4 Disable IPv4 Internet (not forwarding IPv4)
(See Notice 1). Usually used with '-6'
2020-12-24 06:36:09 -06:00
--p6 <prefix> Set IPv6 LAN address prefix (length 64)
2021-04-16 23:29:53 -05:00
(example: 'fd00:0:0:5::' or '5' shortly)
Using this enables '-6'
2018-08-31 05:41:06 -05:00
--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)
2020-12-24 06:36:09 -06:00
whose destination port is 53 to this host
2021-11-06 21:28:36 -05:00
--log-dns Show DNS query log (dnsmasq)
2018-08-31 05:41:06 -05:00
--dhcp-dns <IP1[,IP2]>|no
2021-10-22 21:56:51 -05:00
Set IPv4 DNS offered by DHCP (default: this host).
2018-08-31 05:41:06 -05:00
--dhcp-dns6 <IP1[,IP2]>|no
Set IPv6 DNS offered by DHCP (RA)
(default: this host)
(Note IPv6 addresses need '[]' around)
2021-11-06 21:28:36 -05:00
Using both above two will enable '--no-dns'
2018-08-31 05:41:06 -05:00
--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
2022-04-03 05:03:51 -05:00
--dns-nocache DNS server no cache
2018-08-31 05:41:06 -05:00
--mac <MAC> Set MAC address
2020-12-24 06:36:09 -06:00
--random-mac Use random MAC address
2018-08-31 05:41:06 -05:00
--tp <port> Transparent proxy,
2023-01-06 19:07:18 -06:00
redirect non-LAN TCP and UDP(not tested) traffic to
port. (usually used with '--dns')
2018-08-31 05:41:06 -05:00
2021-11-06 21:31:15 -05:00
WiFi hotspot options:
2018-08-31 05:41:06 -05:00
--ap <wifi interface> <SSID>
2021-11-06 21:31:15 -05:00
Create WiFi access point
2020-12-24 06:36:09 -06:00
-p, --password <password>
2021-11-06 21:31:15 -05:00
WiFi password
2022-04-03 05:05:05 -05:00
--qr Show WiFi QR code in terminal (need qrencode)
2018-08-31 05:41:06 -05:00
--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
2023-01-04 14:04:00 -06:00
--virt-name <name> Set name of virtual interface
2023-10-04 08:48:27 -05:00
-c <channel> Specify channel (default: use current, or 1 / 36)
2018-08-31 05:41:06 -05:00
--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)
2021-01-19 23:10:02 -06:00
-w <WPA version> '2' for WPA2, '1' for WPA, '1+2' for both
(default: 2)
2018-08-31 05:41:06 -05:00
--psk Use 64 hex digits pre-shared-key instead of
passphrase
2021-11-06 21:31:15 -05:00
--mac-filter Enable WiFi hotspot MAC address filtering
--mac-filter-accept Location of WiFi hotspot MAC address filter list
2018-08-31 05:41:06 -05:00
(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
2023-09-28 01:46:59 -05:00
--no-haveged Do not run haveged automatically when needed
2023-10-04 08:48:27 -05:00
--hs20 Enable Hotspot 2.0
2023-09-28 01:12:00 -05:00
2023-10-03 22:43:24 -05:00
WiFi 4 (802.11n) configs:
2023-10-04 08:48:27 -05:00
--wifi4 Enable IEEE 802.11n (HT)
--req-ht Require station HT (High Throughput) mode
--ht-capab <HT caps> HT capabilities (default: [HT40+])
2023-09-28 01:46:59 -05:00
2023-10-03 22:43:24 -05:00
WiFi 5 (802.11ac) configs:
2023-10-04 08:48:27 -05:00
--wifi5 Enable IEEE 802.11ac (VHT)
--req-vht Require station VHT (Very High Thoughtput) mode
--vht-capab <VHT caps> VHT capabilities
2023-10-03 22:43:24 -05:00
2023-10-04 08:48:27 -05:00
--vht-ch-width <index> Index of VHT channel width:
2023-09-28 01:46:59 -05:00
0 for 20MHz or 40MHz (default)
1 for 80MHz
2 for 160MHz
3 for 80+80MHz (Non-contigous 160MHz)
2023-10-04 08:48:27 -05:00
--vht-seg0-ch <channel> Channel index of VHT center frequency for primary
segment. Use with '--vht-ch-width'
--vht-seg1-ch <channel> Channel index of VHT center frequency for secondary
(second 80MHz) segment. Use with '--vht-ch-width 3'
2018-08-31 05:41:06 -05:00
Instance managing:
--daemon Run in background
2021-02-21 19:20:41 -06:00
-l, --list-running Show running instances
--lc, --list-clients <id|interface>
2021-01-19 23:10:02 -06:00
List clients of an instance. Or list neighbors of
2021-02-21 19:20:41 -06:00
an interface, even if it isn't handled by us.
(passive mode)
2018-08-31 05:41:06 -05:00
--stop <id> Stop a running instance
For <id> you can use PID or subnet interface name.
You can get them with '--list-running'
2020-12-24 06:36:09 -06:00
Notice 1: This script assume your host's default policy won't forward
packets, so the script won't explictly ban forwarding in any
2021-11-06 21:31:15 -05:00
mode. In some unexpected case (eg. mistaken configurations) may
cause unwanted packets leakage between 2 networks, which you
should be aware of if you want isolated network
2020-12-24 06:36:09 -06:00
2018-08-31 05:41:06 -05:00
Examples:
$PROGNAME -i eth1
2020-12-24 06:36:09 -06:00
$PROGNAME --ap wlan0 MyAccessPoint -p MyPassPhrase
2018-08-31 05:41:06 -05:00
$PROGNAME -i eth1 --tp <transparent-proxy> --dns <dns-proxy>
EOF
}
2021-01-19 23:10:02 -06:00
check_empty_option(){
if [[ "$1" == "" ]]; then
usage
exit 0
fi
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
define_global_variables(){
2021-01-19 23:10:02 -06:00
# user options
2021-01-19 23:10:02 -06:00
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=
2022-04-03 05:03:51 -05:00
DNS_NOCACHE=
2021-01-19 23:10:02 -06:00
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
TP_PORT= # transparent proxy port
DNS= # upstream DNS
2021-01-19 23:10:02 -06:00
MAC_USE_RANDOM=0
2021-01-19 23:10:02 -06:00
NEW_MACADDR=
DAEMONIZE=0
2021-01-19 23:10:02 -06:00
# script variables
SUBNET_IFACE= # which interface to create network
SHARE_METHOD=nat
OLD_MACADDR=
2024-02-24 20:00:00 -06:00
SUBNET_NET4=
SUBNET_NET6=
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
##### wifi hotspot
# user options
2021-01-19 23:10:02 -06:00
HIDDEN=0 # hidden wifi hotspot
WIFI_IFACE=
CHANNEL=default
2023-09-28 01:12:00 -05:00
HOTSPOT20=0 # For enabling Hotspot 2.0
2021-01-19 23:10:02 -06:00
WPA_VERSION=2
MAC_FILTER=0
MAC_FILTER_ACCEPT=/etc/hostapd/hostapd.accept
IEEE80211N=0
2023-09-28 01:46:59 -05:00
REQUIREHT=0
2021-01-19 23:10:02 -06:00
IEEE80211AC=0
2023-09-28 01:46:59 -05:00
REQUIREVHT=0
2021-01-19 23:10:02 -06:00
HT_CAPAB='[HT40+]'
VHT_CAPAB=
2023-09-28 01:46:59 -05:00
VHTCHANNELWIDTH=0
VHTSEG0CHINDEX=0
VHTSEG1CHINDEX=0
2021-01-19 23:10:02 -06:00
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
QR=0 # show wifi qr
2021-01-19 23:10:02 -06:00
# script variables
VWIFI_IFACE= # virtual wifi interface name, if created
2023-01-04 14:04:00 -06:00
VIRT_NAME= # name to use for virtual interface if --virt-name is used
2021-01-19 23:10:02 -06:00
AP_IFACE= # can be VWIFI_IFACE or WIFI_IFACE
USE_IWCONFIG=0 # some device can't use iw
#######
#-- to deal with info of a running instance. then will exit
2021-01-19 23:10:02 -06:00
LIST_RUNNING=0
STOP_ID=
LIST_CLIENTS_ID=
# -- variables for running
CONFDIR=
2021-01-19 23:10:02 -06:00
NM_RUNNING=0
NM_UNM_LIST= # it's called "list" but for now one interface
2021-01-19 23:10:02 -06:00
}
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
;;
-n)
shift
SHARE_METHOD=none
;;
--ban-priv)
shift
BANLAN=1
;;
--tp)
shift
TP_PORT="$1"
2021-01-19 23:10:02 -06:00
SHARE_METHOD=redsocks
2021-01-19 23:10:02 -06:00
shift
;;
-g)
shift
GATEWAY="$1"
shift
;;
-6)
shift
IPV6=1
;;
--no4)
shift
NO4=1
;;
--p6)
shift
PREFIX6="$1"
IPV6=1
shift
;;
--mac)
shift
NEW_MACADDR="$1"
shift
;;
--random-mac)
shift
2021-01-19 23:10:02 -06:00
MAC_USE_RANDOM=1
2021-01-19 23:10:02 -06:00
;;
--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
;;
2022-04-03 05:03:51 -05:00
--dns-nocache)
shift
DNS_NOCACHE=1
;;
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
--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
;;
2023-09-28 01:12:00 -05:00
--hs20)
shift
HOTSPOT20=1
;;
2021-01-19 23:10:02 -06:00
-w)
shift
WPA_VERSION="$1"
[[ "$WPA_VERSION" == "2+1" ]] && WPA_VERSION=1+2
shift
;;
2023-10-04 08:48:27 -05:00
--wifi4|--ieee80211n)
2021-01-19 23:10:02 -06:00
shift
IEEE80211N=1
;;
2023-10-04 08:48:27 -05:00
--req-ht|--require-ht)
2023-09-28 01:46:59 -05:00
shift
REQUIREHT=1
;;
2023-10-04 08:48:27 -05:00
--wifi5|--ieee80211ac)
2021-01-19 23:10:02 -06:00
shift
IEEE80211AC=1
;;
2023-10-04 08:48:27 -05:00
--req-vht|--require-vht)
2023-09-28 01:46:59 -05:00
shift
REQUIREVHT=1
;;
2023-10-03 22:43:24 -05:00
--ht-capab)
2021-01-19 23:10:02 -06:00
shift
HT_CAPAB="$1"
shift
;;
2023-10-03 22:43:24 -05:00
--vht-capab)
2021-01-19 23:10:02 -06:00
shift
VHT_CAPAB="$1"
shift
;;
2023-10-04 08:48:27 -05:00
--vht-ch-width|--vht-channel-width)
2023-09-28 01:46:59 -05:00
shift
VHTCHANNELWIDTH="$1"
shift
;;
2023-10-04 08:48:27 -05:00
--vht-seg0-ch|--vht-seg0-channel)
2023-09-28 01:46:59 -05:00
shift
VHTSEG0CHINDEX="$1"
shift
;;
2023-10-04 08:48:27 -05:00
--vht-seg1-ch|--vht-seg1-channel)
2023-09-28 01:46:59 -05:00
shift
VHTSEG1CHINDEX="$1"
shift
;;
2021-01-19 23:10:02 -06:00
--driver)
shift
DRIVER="$1"
shift
;;
--no-virt)
shift
NO_VIRT=1
;;
2022-09-26 14:26:50 -05:00
--virt-name)
shift
2023-01-04 14:04:00 -06:00
VIRT_NAME="$1"
2022-09-26 14:26:50 -05:00
shift
2023-01-04 14:04:00 -06:00
;;
2021-01-19 23:10:02 -06:00
--country)
shift
COUNTRY="$1"
shift
;;
--freq-band)
shift
FREQ_BAND="$1"
shift
;;
--no-haveged)
shift
NO_HAVEGED=1
;;
--hostapd-debug)
shift
2024-01-13 18:49:38 -06:00
if [ "$1" = "1" ]; then
2021-01-19 23:10:02 -06:00
HOSTAPD_DEBUG_ARGS="-d"
2024-01-13 18:49:38 -06:00
elif [ "$1" = "2" ]; then
2021-01-19 23:10:02 -06:00
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
;;
2021-02-21 19:20:41 -06:00
-l|--list-running)
2021-01-19 23:10:02 -06:00
shift
LIST_RUNNING=1
;;
2021-01-19 23:10:02 -06:00
--lc|--list-clients)
2021-01-19 23:10:02 -06:00
shift
LIST_CLIENTS_ID="$1"
shift
;;
*)
echo "Invalid parameter: $1" 1>&2
2018-08-31 05:41:06 -05:00
exit 1
2021-01-19 23:10:02 -06:00
;;
esac
done
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# seperate ip and port
2018-08-31 05:41:06 -05:00
sep_ip_port() {
2021-01-19 23:10:02 -06:00
# usage: sep_ip_port <ip:port> <var for ip> <var for port>
# input <ip:port> can be:
# port (ip is 127.0.0.1)
# ipv4
# [ipv6]
# ipv4:port
# [ipv6]:port
2018-08-31 05:41:06 -05:00
local IP
local PORT
local INPUT
INPUT="$1"
2020-12-24 06:36:09 -06:00
if (echo "$INPUT" | grep '\.' >/dev/null 2>&1) ;then
if (echo "$INPUT" | grep ':' >/dev/null 2>&1) ;then
2018-08-31 05:41:06 -05:00
# ipv4 + port
2024-01-13 18:49:38 -06:00
IP="$(echo "$INPUT" | cut -d: -f1)"
PORT="$(echo "$INPUT" | cut -d: -f2)"
2018-08-31 05:41:06 -05:00
else
# ipv4
IP="$INPUT"
fi
2020-12-24 06:36:09 -06:00
elif (echo "$INPUT" | grep '\]' >/dev/null 2>&1) ;then
if (echo "$INPUT" | grep '\]\:' >/dev/null 2>&1) ;then
2018-08-31 05:41:06 -05:00
# ipv6 + port
2024-01-13 18:49:38 -06:00
IP="$(echo "$INPUT" | cut -d']' -f1 | cut -d'[' -f2)"
PORT="$(echo "$INPUT" | cut -d']' -f2 |cut -d: -f2)"
2018-08-31 05:41:06 -05:00
else
# ipv6
2024-01-13 18:49:38 -06:00
IP="$(echo "$INPUT" | cut -d']' -f1 | cut -d'[' -f2)"
2018-08-31 05:41:06 -05:00
fi
else
# port
IP='127.0.0.1'
PORT="$INPUT"
fi
printf -v "$2" %s "$IP"
printf -v "$3" %s "$PORT"
}
2021-01-19 23:10:02 -06:00
#=========================
2018-08-31 05:41:06 -05:00
is_interface() {
[[ -z "$1" ]] && return 1
[[ -d "/sys/class/net/${1}" ]]
}
2023-01-04 14:04:00 -06:00
is_vface_name_allocated(){
is_interface "$1" || [[ -f "$COMMON_CONFDIR/vfaces/${1}" ]]
}
2021-01-19 23:10:02 -06:00
get_interface_phy_device() { # only for wifi interface
2018-08-31 05:41:06 -05:00
local x
for x in /sys/class/ieee80211/*; do
[[ ! -e "$x" ]] && continue
if [[ "${x##*/}" = "$1" ]]; then
2020-12-24 06:36:09 -06:00
echo "$1"
2018-08-31 05:41:06 -05:00
return 0
elif [[ -e "$x/device/net/$1" ]]; then
2024-01-13 18:49:38 -06:00
echo "${x##*/}"
2018-08-31 05:41:06 -05:00
return 0
elif [[ -e "$x/device/net:$1" ]]; then
2024-01-13 18:49:38 -06:00
echo "${x##*/}"
2018-08-31 05:41:06 -05:00
return 0
fi
done
echo "Failed to get phy interface" >&2
return 1
}
get_adapter_info() { # only for wifi interface
2021-01-19 23:10:02 -06:00
local iPHY
iPHY=$(get_interface_phy_device "$1")
2018-08-31 05:41:06 -05:00
[[ $? -ne 0 ]] && return 1
2024-01-13 18:49:38 -06:00
iw phy "$iPHY" info
2018-08-31 05:41:06 -05:00
}
get_adapter_kernel_module() {
local MODULE
MODULE=$(readlink -f "/sys/class/net/$1/device/driver/module")
2024-01-13 18:49:38 -06:00
echo "${MODULE##*/}"
2018-08-31 05:41:06 -05:00
}
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
2024-01-18 23:50:34 -06:00
CHANNEL_INFO=$(get_adapter_info "${IFACE}" | grep -E " [0-9]+(\.[0-9]+){0,1} MHz \[${CHANNEL_NUM}\]")
2018-08-31 05:41:06 -05:00
[[ -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})
2024-01-13 18:49:38 -06:00
CHANNEL_INFO=$(iwlist "${IFACE}" channel | grep -E "Channel[[:blank:]]${CHANNEL_NUM}[[:blank:]]?:")
2018-08-31 05:41:06 -05:00
[[ -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
}
2021-01-19 23:10:02 -06:00
is_interface_wifi_connected() {
2018-08-31 05:41:06 -05:00
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 ]]
}
2021-01-19 23:10:02 -06:00
get_interface_mac() {
2018-08-31 05:41:06 -05:00
is_interface "$1" || return
cat "/sys/class/net/${1}/address"
}
2024-01-14 06:25:06 -06:00
show_interface_pci_info() { # pci id / model / virtual
2022-01-05 06:01:38 -06:00
is_interface "$1" || return
local device_path
2024-01-14 06:25:06 -06:00
local bus_id=""
local device_type_and_bus_id="unknown"
local driver=""
local device_fullname=""
2024-01-13 19:27:36 -06:00
2022-01-05 06:01:38 -06:00
device_path="$(readlink -f /sys/class/net/$1)"
if [[ "$device_path" == "/sys/devices/pci"* ]]; then
2024-01-14 06:25:06 -06:00
local pci_path
pci_path=$device_path/../..
if [[ -d "$pci_path/driver" ]] ; then
driver=$(readlink -f "$pci_path/driver" | sed 's/\//\n/g' | tail -n 1)
fi
bus_id="$(echo "$device_path" | sed 's/\//\n/g' | tail -n 3 |sed -n 1p)"
device_type_and_bus_id="PCI: $bus_id"
2022-01-05 06:01:38 -06:00
if which lspci >/dev/null 2>&1 ; then
2024-01-14 06:25:06 -06:00
device_fullname="$( lspci -D -nn -s "$bus_id" | awk '{$1="" ; print $0}' )"
2022-01-05 06:01:38 -06:00
fi
2024-01-14 06:25:06 -06:00
2022-01-05 06:01:38 -06:00
elif [[ "$device_path" == *"/virtual/"* ]]; then
2024-01-14 06:25:06 -06:00
device_type_and_bus_id="virtual interface"
2022-01-05 06:01:38 -06:00
fi
2024-01-13 19:27:36 -06:00
2024-01-14 06:25:06 -06:00
echo "$device_type_and_bus_id"
[[ -n "$driver" ]] && echo "System-already-loaded driver: $driver"
[[ -n "$device_fullname" ]] && echo "$device_fullname"
2024-01-13 19:27:36 -06:00
echo ""
2022-01-05 06:01:38 -06:00
# TODO usb
}
2021-01-19 23:10:02 -06:00
alloc_new_vface_name() { # only for wifi
2018-08-31 05:41:06 -05:00
local i=0
2023-01-04 14:04:00 -06:00
local v_iface_name="$VIRT_NAME"
if [[ -z $VIRT_NAME ]]; then
while :; do
2022-09-26 14:26:50 -05:00
v_iface_name="x$i${WIFI_IFACE}"
2023-01-04 14:04:00 -06:00
i=$((i + 1))
2024-01-13 18:49:38 -06:00
is_vface_name_allocated "${v_iface_name}" || break
2023-01-04 14:04:00 -06:00
done
fi
2024-01-13 18:49:38 -06:00
mkdir -p "$COMMON_CONFDIR/vfaces"
touch "$COMMON_CONFDIR/vfaces/${v_iface_name}"
2023-01-04 14:04:00 -06:00
echo "${v_iface_name}"
2018-08-31 05:41:06 -05:00
}
2021-01-19 23:10:02 -06:00
dealloc_vface_name() {
2024-01-13 18:49:38 -06:00
rm -f "$COMMON_CONFDIR/vfaces/$1"
2018-08-31 05:41:06 -05:00
}
#======
2021-01-19 23:10:02 -06:00
get_all_mac_in_system() {
2018-08-31 05:41:06 -05:00
cat /sys/class/net/*/address
}
2021-01-19 23:10:02 -06:00
get_new_macaddr_according_to_existing() {
local REALDEV OLDMAC NEWMAC LAST_BYTE i
REALDEV=$1
2021-01-19 23:10:02 -06:00
OLDMAC=$(get_interface_mac "$REALDEV")
2021-01-19 23:10:02 -06:00
NEWMAC=""
2018-08-31 05:41:06 -05:00
LAST_BYTE=$(printf %d 0x${OLDMAC##*:})
for i in {10..240}; do
NEWMAC="${OLDMAC%:*}:$(printf %02x $(( ($LAST_BYTE + $i) % 256 )))"
2021-01-19 23:10:02 -06:00
(get_all_mac_in_system | grep "$NEWMAC" > /dev/null 2>&1) || break
2018-08-31 05:41:06 -05:00
done
2020-12-24 06:36:09 -06:00
echo "$NEWMAC"
2018-08-31 05:41:06 -05:00
}
2020-12-24 06:36:09 -06:00
generate_random_mac() {
local r1 r2 r3 r4 r5 r6
2021-01-19 23:10:02 -06:00
local RAND_MAC
2020-12-24 06:36:09 -06:00
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"
2024-01-13 18:49:38 -06:00
( ! ip link | grep "link" | grep "$RAND_MAC" > /dev/null 2>&1 ) && \
( ! ip maddress | grep "link" | grep "$RAND_MAC" > /dev/null 2>&1 ) && \
2020-12-24 06:36:09 -06:00
( ! ip neigh | grep "lladdr $RAND_MAC" > /dev/null 2>&1 ) && \
2024-01-13 18:49:38 -06:00
( ! get_all_mac_in_system | grep "$RAND_MAC" ) && \
2020-12-24 06:36:09 -06:00
break
done
2021-01-19 23:10:02 -06:00
echo "$RAND_MAC"
2020-12-24 06:36:09 -06:00
}
2021-01-19 23:10:02 -06:00
is_ip4_lan_range_available() { # checks 192.168.x.x
2018-08-31 05:41:06 -05:00
( 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
2020-12-24 06:36:09 -06:00
( 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
2018-08-31 05:41:06 -05:00
return 1
}
2021-01-19 23:10:02 -06:00
is_ip6_lan_range_available() { # checks fdxx::
2018-08-31 05:41:06 -05:00
( 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
2020-12-24 06:36:09 -06:00
( 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
2018-08-31 05:41:06 -05:00
return 1
}
generate_random_ip4() {
local random_ip4
while :; do
random_ip4=$(($RANDOM%256))
2021-01-19 23:10:02 -06:00
is_ip4_lan_range_available $random_ip4 && break
2018-08-31 05:41:06 -05:00
done
2021-01-19 23:10:02 -06:00
echo "192.168.$random_ip4.1"
2018-08-31 05:41:06 -05:00
}
2021-01-19 23:10:02 -06:00
generate_random_lan_ip6_prefix() {
2018-08-31 05:41:06 -05:00
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)) )
2024-01-13 18:49:38 -06:00
is_ip6_lan_range_available "$r1" "$r2" "$r3" "$r4" "$r5" "$r6" "$r7" && break
2018-08-31 05:41:06 -05:00
done
2021-01-19 23:10:02 -06:00
echo "fd$r1:$r2$r3:$r4$r5:$r6$r7::"
2018-08-31 05:41:06 -05:00
}
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
# 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
2021-04-16 23:29:53 -05:00
elif ! pidof haveged > /dev/null 2>&1; then # TODO judge zombie ?
2018-08-31 05:41:06 -05:00
echo "Low entropy detected, starting haveged" 1>&2
# boost low-entropy
haveged -w 1024 -p $COMMON_CONFDIR/haveged.pid
fi
fi
sleep 2
done
}
2021-04-16 23:29:53 -05:00
pid_watchdog() {
local PID="$1"
local SLEEP="$2"
local ERR_MSG="$3"
local ST
while true
do
if [[ -e "/proc/$PID" ]]; then
ST="$(cat "/proc/$PID/status" | grep "^State:" | awk '{print $2}')"
if [[ "$ST" != 'Z' ]]; then
2024-01-13 18:49:38 -06:00
sleep "$SLEEP"
2021-04-16 23:29:53 -05:00
continue
fi
fi
die "$ERR_MSG"
done
}
2018-08-31 05:41:06 -05:00
#========
# only support NetworkManager >= 0.9.9
2021-01-19 23:10:02 -06:00
is_nm_running() {
2021-01-19 23:10:02 -06:00
if (which nmcli >/dev/null 2>&1 ) && (nmcli -t -f RUNNING g 2>&1 | grep -E '^running$' >/dev/null 2>&1 ) ; then
2021-01-19 23:10:02 -06:00
echo 1
else
echo 0
2021-01-19 23:10:02 -06:00
fi
}
2018-08-31 05:41:06 -05:00
nm_knows() {
2024-01-13 18:49:38 -06:00
(nmcli dev show "$1" | grep -E "^GENERAL.STATE:" >/dev/null 2>&1 ) && return 0 # nm sees
2018-08-31 05:41:06 -05:00
return 1 # nm doesn't see this interface
}
nm_get_manage() { # get an interface's managed state
local s
2024-01-13 18:49:38 -06:00
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
2018-08-31 05:41:06 -05:00
return 0 # managed
}
nm_set_unmanaged() {
2024-01-13 18:49:38 -06:00
while ! nm_knows "$1" ; do # wait for virtual wifi interface seen by NM
2018-08-31 05:41:06 -05:00
sleep 0.5
done
2024-01-13 18:49:38 -06:00
if nm_get_manage "$1" ;then
2018-08-31 05:41:06 -05:00
echo "Set $1 unmanaged by NetworkManager"
2024-01-13 18:49:38 -06:00
nmcli dev set "$1" managed no || die "Failed to set $1 unmanaged by NetworkManager"
2018-08-31 05:41:06 -05:00
NM_UNM_LIST=$1
sleep 1
fi
}
nm_set_managed() {
2024-01-13 18:49:38 -06:00
nmcli dev set "$1" managed yes
2018-08-31 05:41:06 -05:00
NM_UNM_LIST=
}
nm_restore_manage() {
if [[ $NM_UNM_LIST ]]; then
echo "Restore $NM_UNM_LIST managed by NetworkManager"
2024-01-13 18:49:38 -06:00
nm_set_managed "$NM_UNM_LIST"
2018-08-31 05:41:06 -05:00
sleep 0.5
fi
}
#=========
2021-08-28 21:27:17 -05:00
check_iptables()
{
echo
iptables --version
if which firewall-cmd > /dev/null 2>&1; then
2021-11-06 21:28:36 -05:00
if [[ "$(firewall-cmd --state 2>&1)" == "running" ]]; then
2021-08-28 21:27:17 -05:00
echo "firewalld is running ($(firewall-cmd --version))"
2021-10-22 21:16:12 -05:00
echo -e "\nWARN: We haven't completed the compatibility with firewalld.\nWARN: If you see any trouble, try:\nWARN: 1) 'firewall-cmd --zone=trusted --add-interface=<SUBN_IFACE>'\nWARN: 2) disable firewalld\n" >&2
# TODO
2021-08-28 21:27:17 -05:00
fi
fi
}
2021-11-06 21:30:56 -05:00
CUSTOM_CHAINS_4_filter=
CUSTOM_CHAINS_4_nat=
CUSTOM_CHAINS_6_filter=
CUSTOM_CHAINS_6_nat=
iptb()
2018-08-31 05:41:06 -05:00
{
2021-11-06 21:30:56 -05:00
local FoS=$1 # 4 | 6
shift
local Vis=$1 # 'v' | 'n'
shift
local T=$1 # table
shift
local ACT=$1 # action: I | A | N . On undo: I or A -> D , N -> F+X
shift
local CH=$1 # chain
shift
[[ "$IPV6" -ne 1 && "$FoS" == "6" ]] && return
local CMD_HEAD=""
local MOUTH=""
local NECK=""
local HAND_UN_NC=0
local TAIL=""
local FULL=""
local ADD_TO_UNDO=1
for arr_name in CUSTOM_CHAINS_4_filter CUSTOM_CHAINS_4_nat CUSTOM_CHAINS_6_filter CUSTOM_CHAINS_6_nat
do
local arr_content
eval arr_content=\"\${$arr_name}\"
#echo $arr_content
for w in $arr_content
do
if [[ "$arr_name" =~ "$FoS" && "$arr_name" =~ "$T" && "$w" == "$CH" ]]; then
ADD_TO_UNDO=0
fi
done
done
[[ "$FoS" == "4" ]] && CMD_HEAD="iptables -w "
[[ "$FoS" == "6" ]] && CMD_HEAD="ip6tables -w "
[[ "$Vis" == 'v' ]] && MOUTH="-v"
NECK="-t ${T}"
if [[ "$ACT" == "N" ]]; then
eval CUSTOM_CHAINS_${FoS}_${T}=\"\${CUSTOM_CHAINS_${FoS}_${T}} ${CH}\"
HAND_UN_NC=1
2021-04-16 23:29:53 -05:00
fi
2021-11-06 21:30:56 -05:00
[[ ! "$NETFILTER_XT_MATCH_COMMENT" == "0" ]] && TAIL="-m comment --comment lrt${$}${SUBNET_IFACE}"
if [[ "$ADD_TO_UNDO" -eq 1 ]]; then
if [[ "$ACT" == "I" || "$ACT" == "A" ]]; then
echo "$CMD_HEAD $NECK -D ${CH} $@ $TAIL" >> $CONFDIR/undo_iptables.sh
fi
if [[ "$HAND_UN_NC" -eq 1 ]]; then
echo "$CMD_HEAD $NECK -F ${CH} $@ $TAIL" >> $CONFDIR/undo_iptables_2.sh
echo "$CMD_HEAD $NECK -X ${CH} $@ $TAIL" >> $CONFDIR/undo_iptables_2.sh
fi
2021-04-16 23:29:53 -05:00
fi
2021-11-06 21:30:56 -05:00
FULL="$CMD_HEAD $MOUTH $NECK -${ACT} ${CH} $@ $TAIL"
#echo $FULL
$FULL
2018-08-31 05:41:06 -05:00
return $?
}
2024-02-24 20:00:00 -06:00
IP_VERs=("4" "6")
2018-08-31 05:41:06 -05:00
start_nat() {
2024-02-24 20:00:00 -06:00
local SUBNET_NET
2018-08-31 05:41:06 -05:00
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 "
2024-02-24 20:00:00 -06:00
for iv in "${IP_VERs[@]}"; do
[[ "$iv" -eq "4" && ! $NO4 -eq 0 ]] && continue
[[ "$iv" -eq "4" ]] && SUBNET_NET="$SUBNET_NET4"
[[ "$iv" -eq "6" ]] && SUBNET_NET="$SUBNET_NET6"
iptb "$iv" v nat I POSTROUTING -s "$SUBNET_NET" $IPTABLES_NAT_OUT $MASQUERADE_NOTOUT ! -d "$SUBNET_NET" -j MASQUERADE || die
iptb "$iv" v filter I FORWARD -i "$SUBNET_IFACE" $IPTABLES_NAT_OUT -s "$SUBNET_NET" -j ACCEPT || die
iptb "$iv" v filter I FORWARD -o "$SUBNET_IFACE" $IPTABLES_NAT_IN -d "$SUBNET_NET" -j ACCEPT || die
done
2018-08-31 05:41:06 -05:00
}
2020-12-24 06:36:09 -06:00
start_ban_lan() {
2024-02-24 20:00:00 -06:00
local arr_nets_to_protect
2020-12-24 06:36:09 -06:00
echo
echo "iptables: Disallow clients to access LAN"
2021-11-06 21:30:56 -05:00
iptb 4 n filter N lrt${$}${SUBNET_IFACE}-BLF || die
2021-11-06 21:28:36 -05:00
# TODO: allow '--dhcp-dns(6)' address port 53, which can be something needed, e.g. a VPN's internal private IP
2024-02-24 20:00:00 -06:00
arr_nets_to_protect=("0.0.0.0/8" "10.0.0.0/8" "100.64.0.0/10" "127.0.0.0/8" "169.254.0.0/16" "172.16.0.0/12" "192.168.0.0/16" "224.0.0.0/4" "255.255.255.255")
for s in "${arr_nets_to_protect[@]}"; do
iptb 4 v filter I lrt${$}${SUBNET_IFACE}-BLF -d "$s" -j REJECT || die
done
2020-12-24 06:36:09 -06:00
2021-11-06 21:30:56 -05:00
iptb 4 n filter I FORWARD -i ${SUBNET_IFACE} -j lrt${$}${SUBNET_IFACE}-BLF || die
2020-12-24 06:36:09 -06:00
2021-11-06 21:30:56 -05:00
iptb 4 n filter N lrt${$}${SUBNET_IFACE}-BLI || die
iptb 4 v filter I lrt${$}${SUBNET_IFACE}-BLI -i ${SUBNET_IFACE} ! -p icmp -j REJECT || die # ipv6 need icmp to function. TODO: maybe we can block some unneeded icmp to improve security
2020-12-24 06:36:09 -06:00
2021-11-06 21:30:56 -05:00
iptb 4 n filter I INPUT -i ${SUBNET_IFACE} -j lrt${$}${SUBNET_IFACE}-BLI || die
2020-12-24 06:36:09 -06:00
2021-11-06 21:30:56 -05:00
iptb 6 n filter N lrt${$}${SUBNET_IFACE}-BLF || die
2024-02-24 20:00:00 -06:00
arr_nets_to_protect=("fc00::/7" "fe80::/10" "ff00::/8" "::1" "::/128" "::ffff:0:0/96" "::ffff:0:0:0/96")
for s in "${arr_nets_to_protect[@]}"; do
iptb 6 v filter I lrt${$}${SUBNET_IFACE}-BLF -d "$s" -j REJECT || die
done
2021-11-06 21:30:56 -05:00
iptb 6 n filter I FORWARD -i ${SUBNET_IFACE} -j lrt${$}${SUBNET_IFACE}-BLF || die
2020-12-24 06:36:09 -06:00
2021-11-06 21:30:56 -05:00
iptb 6 n filter N lrt${$}${SUBNET_IFACE}-BLI || die
iptb 6 v filter I lrt${$}${SUBNET_IFACE}-BLI -i ${SUBNET_IFACE} ! -p icmpv6 -j REJECT || die
iptb 6 n filter I INPUT -i ${SUBNET_IFACE} -j lrt${$}${SUBNET_IFACE}-BLI || die
2020-12-24 06:36:09 -06:00
}
2018-08-31 05:41:06 -05:00
allow_dns_port() {
echo
2021-01-19 23:10:02 -06:00
echo "iptables: allow DNS"
2024-02-24 20:00:00 -06:00
iptb 4 v filter I INPUT -i ${SUBNET_IFACE} -s "$SUBNET_NET4" -d ${GATEWAY} -p tcp -m tcp --dport 53 -j ACCEPT || die
iptb 4 v filter I INPUT -i ${SUBNET_IFACE} -s "$SUBNET_NET4" -d ${GATEWAY} -p udp -m udp --dport 53 -j ACCEPT || die
iptb 6 v filter I INPUT -i ${SUBNET_IFACE} -s "$SUBNET_NET6" -d ${GATEWAY6} -p tcp -m tcp --dport 53 -j ACCEPT || die
iptb 6 v filter I INPUT -i ${SUBNET_IFACE} -s "$SUBNET_NET6" -d ${GATEWAY6} -p udp -m udp --dport 53 -j ACCEPT || die
2018-08-31 05:41:06 -05:00
}
2021-11-06 21:30:56 -05:00
2018-08-31 05:41:06 -05:00
start_catch_dns() {
echo
2021-11-06 21:30:56 -05:00
echo "iptables: redirect DNS queries to this host"
iptb 4 v nat I PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY} -p udp -m udp --dport 53 -j REDIRECT --to-ports 53 || die
iptb 4 v nat I PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY} -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 53 || die
iptb 6 v nat I PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY6} -p udp -m udp --dport 53 -j REDIRECT --to-ports 53 || die
iptb 6 v nat I PREROUTING -i ${SUBNET_IFACE} ! -d ${GATEWAY6} -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 53 || die
2018-08-31 05:41:06 -05:00
}
2021-11-06 21:30:56 -05:00
2021-01-19 23:10:02 -06:00
allow_dhcp() {
2018-08-31 05:41:06 -05:00
echo
2021-01-19 23:10:02 -06:00
echo "iptables: allow dhcp"
2021-11-06 21:30:56 -05:00
iptb 4 v filter I INPUT -i ${SUBNET_IFACE} -p udp -m udp --dport 67 -j ACCEPT || die
iptb 6 v filter I INPUT -i ${SUBNET_IFACE} -p udp -m udp --dport 547 -j ACCEPT || die
2018-08-31 05:41:06 -05:00
}
2021-02-21 19:20:41 -06:00
# TODO: use 'DNAT' instead of '--to-ports' to support other IP
2018-08-31 05:41:06 -05:00
start_redsocks() {
2024-02-24 20:00:00 -06:00
local arr_nets_to_ignore
2018-08-31 05:41:06 -05:00
echo
2023-01-06 19:07:18 -06:00
echo "iptables: transparent proxy non-LAN TCP and UDP(not tested) traffic to port ${TP_PORT}"
2020-12-24 06:36:09 -06:00
if [[ $NO4 -eq 0 ]]; then
2021-11-06 21:30:56 -05:00
iptb 4 n nat N lrt${$}${SUBNET_IFACE}-TP || die
2020-12-24 06:36:09 -06:00
2024-02-24 20:00:00 -06:00
arr_nets_to_ignore=("0.0.0.0/8" "10.0.0.0/8" "100.64.0.0/10" "127.0.0.0/8" "169.254.0.0/16" "172.16.0.0/12" "192.168.0.0/16" "224.0.0.0/4" "255.255.255.255")
for s in "${arr_nets_to_ignore[@]}"; do
iptb 4 n nat A lrt${$}${SUBNET_IFACE}-TP -d "$s" -j RETURN || die
done
2021-11-06 21:30:56 -05:00
iptb 4 v nat A lrt${$}${SUBNET_IFACE}-TP -p tcp -j REDIRECT --to-ports ${TP_PORT} || die
iptb 4 v nat A lrt${$}${SUBNET_IFACE}-TP -p udp -j REDIRECT --to-ports ${TP_PORT} || die
2018-08-31 05:41:06 -05:00
2024-02-24 20:00:00 -06:00
iptb 4 v nat I PREROUTING -i ${SUBNET_IFACE} -s "$SUBNET_NET4" -j lrt${$}${SUBNET_IFACE}-TP || die
2018-08-31 05:41:06 -05:00
2024-02-24 20:00:00 -06:00
iptb 4 v filter I INPUT -i ${SUBNET_IFACE} -s "$SUBNET_NET4" -p tcp -m tcp --dport ${TP_PORT} -j ACCEPT || die
iptb 4 v filter I INPUT -i ${SUBNET_IFACE} -s "$SUBNET_NET4" -p udp -m udp --dport ${TP_PORT} -j ACCEPT || die
2020-12-24 06:36:09 -06:00
fi
2018-08-31 05:41:06 -05:00
2021-11-06 21:30:56 -05:00
iptb 6 n nat N lrt${$}${SUBNET_IFACE}-TP || die
2024-02-24 20:00:00 -06:00
arr_nets_to_ignore=("fc00::/7" "fe80::/10" "ff00::/8" "::1" "::")
for s in "${arr_nets_to_ignore[@]}"; do
iptb 6 n nat A lrt${$}${SUBNET_IFACE}-TP -d "$s" -j RETURN || die
done
2018-08-31 05:41:06 -05:00
2021-11-06 21:30:56 -05:00
iptb 6 v nat A lrt${$}${SUBNET_IFACE}-TP -p tcp -j REDIRECT --to-ports ${TP_PORT} || die
iptb 6 v nat A lrt${$}${SUBNET_IFACE}-TP -p udp -j REDIRECT --to-ports ${TP_PORT} || die
2024-02-24 20:00:00 -06:00
iptb 6 v nat I PREROUTING -i ${SUBNET_IFACE} -s "$SUBNET_NET6" -j lrt${$}${SUBNET_IFACE}-TP || die
2021-11-06 21:30:56 -05:00
2024-02-24 20:00:00 -06:00
iptb 6 v filter I INPUT -i ${SUBNET_IFACE} -s "$SUBNET_NET6" -p tcp -m tcp --dport ${TP_PORT} -j ACCEPT || die
iptb 6 v filter I INPUT -i ${SUBNET_IFACE} -s "$SUBNET_NET6" -p udp -m udp --dport ${TP_PORT} -j ACCEPT || die
2018-08-31 05:41:06 -05:00
}
2021-11-06 21:30:56 -05:00
2021-01-19 23:10:02 -06:00
#---------------------------------------
2021-01-19 23:10:02 -06:00
backup_ipv6_bits() {
2021-01-19 23:10:02 -06:00
mkdir "$CONFDIR/sys_6_conf_iface" || die "Failed making dir to save interface IPv6 status"
cp "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/disable_ipv6" \
"/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" \
2021-02-21 19:20:41 -06:00
"$CONFDIR/sys_6_conf_iface/" || die "Failed backing up interface ipv6 bits"
2021-01-19 23:10:02 -06:00
if [[ "$SHARE_METHOD" == 'redsocks' ]] ; then
cp "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/forwarding" \
"$CONFDIR/sys_6_conf_iface/" || die "Failed backking up interface ipv6 bits"
fi
2021-01-19 23:10:02 -06:00
}
2021-01-19 23:10:02 -06:00
set_ipv6_bits() {
2021-01-19 23:10:02 -06:00
if [[ $IPV6 -eq 1 ]]; then
echo 0 > "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/disable_ipv6"
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"
else
echo 1 > "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/disable_ipv6"
fi
}
2021-01-19 23:10:02 -06:00
restore_ipv6_bits() {
2021-01-19 23:10:02 -06:00
if [[ -d "$CONFDIR/sys_6_conf_iface" ]]; then
cp -f "$CONFDIR/sys_6_conf_iface/*" "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/"
fi
}
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
set_interface_mac() {
local INTERFACE
local MAC
INTERFACE=$1
MAC=$2
2024-01-13 18:49:38 -06:00
ip link set dev "${INTERFACE}" address "${MAC}"
2021-01-19 23:10:02 -06:00
}
2021-01-19 23:10:02 -06:00
backup_interface_status() {
# virtual wifi interface will be destroyed, so no need to save status
# backup interface up or down status
2024-01-13 18:49:38 -06:00
(ip link show "${SUBNET_IFACE}" |grep -q "state UP") && SUBNET_IFACE_ORIGINAL_UP_STATUS=1
2021-01-19 23:10:02 -06:00
# save interface old mac
2021-02-21 19:20:41 -06:00
#if [[ -n "$NEW_MACADDR" ]]; then
2024-01-13 18:49:38 -06:00
OLD_MACADDR=$(get_interface_mac "$SUBNET_IFACE")
2021-02-21 19:20:41 -06:00
#echo "Saved ${SUBNET_IFACE} old MAC address ${OLD_MACADDR} into RAM"
#fi
2021-01-19 23:10:02 -06:00
backup_ipv6_bits
2021-11-06 21:31:15 -05:00
# TODO : ? backup ip and others???
2021-01-19 23:10:02 -06:00
# nm managing status is saved when nm_set_unmanaged()
}
restore_interface_status() {
# virtual wifi interface will be destroyed, so no need to restore status
# don't use [[ $VWIFI_IFACE ]] to judge, if creating virtual wifi failed, VWIFI_IFACE is empty
[[ "$WIFI_IFACE" && "$NO_VIRT" -eq 0 ]] && return
restore_ipv6_bits
2024-01-13 18:49:38 -06:00
if [[ -n "$OLD_MACADDR" && "$(get_interface_mac "$SUBNET_IFACE")" != "$OLD_MACADDR" ]] ; then
2021-01-19 23:10:02 -06:00
echo "Restoring ${SUBNET_IFACE} to old MAC address ${OLD_MACADDR} ..."
2024-01-13 18:49:38 -06:00
set_interface_mac "${SUBNET_IFACE}" "${OLD_MACADDR}" || echo "Failed restoring ${SUBNET_IFACE} to old MAC address ${OLD_MACADDR}" >&2
2021-01-19 23:10:02 -06:00
fi
nm_restore_manage
2024-01-13 18:49:38 -06:00
[[ $SUBNET_IFACE_ORIGINAL_UP_STATUS -eq 1 ]] && ip link set up dev "${SUBNET_IFACE}" && echo "Restore ${SUBNET_IFACE} to link up"
2021-01-19 23:10:02 -06:00
}
2021-01-19 23:10:02 -06:00
#---------------------------------------
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
kill_processes() { # for this instance
2018-08-31 05:41:06 -05:00
#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
2024-01-13 18:49:38 -06:00
pid=$(cat "$x")
pn=$( ps -p "$pid" -o comm= )
2018-08-31 05:41:06 -05:00
#echo "Killing $pid $pn ... "
2024-01-13 18:49:38 -06:00
pkill -P "$pid"
kill "$pid" 2>/dev/null && ( echo "Killed $(basename "$x") $pid $pn" && rm "$x" ) || echo "Failed to kill $(basename "$x") $pid $pn, it may have exited"
2018-08-31 05:41:06 -05:00
fi
done
}
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
_cleanup() {
local x
2024-01-13 18:49:38 -06:00
ip addr flush "${SUBNET_IFACE}"
2018-08-31 05:41:06 -05:00
2024-01-13 18:49:38 -06:00
rm -rf "$CONFDIR"
2018-08-31 05:41:06 -05:00
2024-01-13 18:49:38 -06:00
ip link set down dev "${SUBNET_IFACE}"
2021-01-19 23:10:02 -06:00
if [[ $VWIFI_IFACE ]]; then # the subnet interface (virtual wifi interface) will be removed
2024-01-13 18:49:38 -06:00
iw dev "${VWIFI_IFACE}" del
dealloc_vface_name "$VWIFI_IFACE"
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
restore_interface_status
2018-08-31 05:41:06 -05:00
if ! has_running_instance; then
echo "Exiting: This is the only running instance"
# kill common processes
for x in $COMMON_CONFDIR/*.pid; do
2024-01-13 18:49:38 -06:00
[[ -f $x ]] && kill -9 $(cat "$x") && rm "$x"
2018-08-31 05:41:06 -05:00
done
2024-01-13 18:49:38 -06:00
rm -d "$COMMON_CONFDIR/vfaces"
rm -d "$COMMON_CONFDIR"
rm -d "$TMPDIR"
2018-08-31 05:41:06 -05:00
else
echo "Exiting: This is NOT the only running instance"
fi
}
clean_iptables() {
2021-11-20 06:32:38 -06:00
[[ -f $CONFDIR/undo_iptables.sh ]] && bash $CONFDIR/undo_iptables.sh
2020-12-24 06:36:09 -06:00
2021-11-06 21:30:56 -05:00
[[ -f $CONFDIR/undo_iptables_2.sh ]] && bash $CONFDIR/undo_iptables_2.sh
2018-08-31 05:41:06 -05:00
}
cleanup() {
trap "" SIGINT SIGUSR1 SIGUSR2 EXIT SIGTERM
echo
echo
echo "Doing cleanup.. "
kill_processes
2021-11-06 21:31:15 -05:00
echo "Undoing iptables changes .."
2021-11-06 21:30:56 -05:00
clean_iptables > /dev/null
2018-08-31 05:41:06 -05:00
_cleanup 2> /dev/null
2021-11-20 06:32:38 -06:00
#pgid=$(ps opgid= $$ |awk '{print $1}' )
#echo "Killing PGID $pgid ..."
#kill -15 -$pgid
#sleep 1
2018-08-31 05:41:06 -05:00
echo "Cleaning up done"
2020-12-24 06:36:09 -06:00
#kill -9 -$pgid
2018-08-31 05:41:06 -05:00
}
2023-01-06 19:08:18 -06:00
# NOTE function die() is designed NOT to be used before init_trap() executed
2018-08-31 05:41:06 -05:00
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
}
2021-01-19 23:10:02 -06:00
init_trap(){
trap "cleanup" EXIT
trap "clean_exit" SIGINT SIGUSR1 SIGTERM
trap "die" SIGUSR2
}
init_conf_dirs() {
mkdir -p "$TMPDIR" || die "Couldn't make linux-router's temporary dir"
chmod 755 "$TMPDIR" 2>/dev/null
cd "$TMPDIR" || die "Couldn't change directory to linux-router's temporary path"
2021-08-21 20:27:49 -05:00
CONFDIR="$(mktemp -d $TMPDIR/lnxrouter.${TARGET_IFACE}.conf.XXXXXX)" || die "Instance couldn't make config dir" # config dir for one instance
2021-01-19 23:10:02 -06:00
chmod 755 "$CONFDIR"
#echo "Config dir: $CONFDIR"
echo $$ > "$CONFDIR/pid"
COMMON_CONFDIR="$TMPDIR/lnxrouter_common.conf" # config dir for all instances
mkdir -p "$COMMON_CONFDIR"
}
2021-01-19 23:10:02 -06:00
#== functions to deal with running instances
2018-08-31 05:41:06 -05:00
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
2020-12-24 06:36:09 -06:00
echo "$x"
2018-08-31 05:41:06 -05:00
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)
2024-01-13 18:49:38 -06:00
if [[ "$IFACE" == "$subn_iface" ]]; then
2018-08-31 05:41:06 -05:00
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
2020-12-24 06:36:09 -06:00
echo "$x"
2018-08-31 05:41:06 -05:00
break
fi
done
}
2021-01-19 23:10:02 -06:00
#======================================================
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
print_clients_from_leases() { # MAC|IP|HOST|lease
local LEASE_FILE="$1"
local FILEC
local line
local LEASEstr LEASEstamp
FILEC="$(cat "$LEASE_FILE" | grep -v -E "^duid\b" | sed -r '/^\s*$/d' )"
2021-02-21 19:20:41 -06:00
# TODO: duid is somewhat related to ipv6. I don't know about it. Not sure excluding it miss some info or not
2024-01-13 18:49:38 -06:00
echo "$FILEC" | while read -r line
2021-01-19 23:10:02 -06:00
do
#echo aa$line
LEASEstamp="$(echo "$line" | awk '{print $1}')"
MAC="$(echo "$line" | awk '{print $2}')"
IP="$(echo "$line" | awk '{print $3}' | sed 's/\[//g' | sed 's/\]//g')"
2021-02-21 19:20:41 -06:00
HOST="$(echo "$line" | awk '{print $4}' | sed 's/*/?/g' | sed 's/|/_/g' | sed 's/ /_/g' )"
2021-01-19 23:10:02 -06:00
if [[ -n "$MAC" ]]; then
LEASEstr="$(date -d @${LEASEstamp} +%m-%d_%X)"
2018-08-31 05:41:06 -05:00
2021-02-21 19:20:41 -06:00
echo "$MAC|$IP|$HOST|lease_$LEASEstr"
2021-01-19 23:10:02 -06:00
fi
done
}
print_interface_neighbors_via_iproute() { # MAC|IP|_|STATUS
local IFACE=$1
local line
2024-01-13 18:49:38 -06:00
ip n | grep -E "\bdev $IFACE\b" | sed 's/ /|/g' | while read -r line
2021-01-19 23:10:02 -06:00
do
local MAC IP STATUS
2024-01-13 18:49:38 -06:00
IP="$(echo "$line" | awk -F'|' '{print $1}')"
2021-01-19 23:10:02 -06:00
2024-01-13 18:49:38 -06:00
if [[ "$(echo "$line" | awk -F'|' '{print $4}')" == "lladdr" ]]; then # has mac
2021-01-19 23:10:02 -06:00
# if has mac, $4="lladdr" and $5=macaddress and $6+=status
2024-01-13 18:49:38 -06:00
MAC="$(echo "$line" | awk -F'|' '{print $5}')"
STATUS="$(echo "$line" | awk -F'|' '$1="";$2="";$3="";$4="";$5="";{print}' | awk '{$1=$1;print}'| sed 's/ /,/g')"
2021-01-19 23:10:02 -06:00
else # no mac
# if no mac, $4="" and $5+=status
MAC="?"
2024-01-13 18:49:38 -06:00
STATUS="$(echo "$line" | awk -F'|' '$1="";$2="";$3="";$4="";{print}' | awk '{$1=$1;print}' | sed 's/ /,/g')"
2021-01-19 23:10:02 -06:00
fi
2021-02-21 19:20:41 -06:00
if [[ -n "$IP" && ( "$MAC" != "?" || "$STATUS" != "FAILED" ) ]]; then
2021-01-19 23:10:02 -06:00
echo "$MAC|$IP|?|$STATUS"
fi
done
}
print_interface_neighbors_via_iw() { # MAC|_|_|signal
local IFACE=$1
local MAC SIGNAL
2024-01-13 18:49:38 -06:00
iw dev "$IFACE" station dump | awk '($1 ~ /Station$/) {print $2}' | while read -r MAC
2021-01-19 23:10:02 -06:00
do
if [[ -n "$MAC" ]]; then
2024-01-13 18:49:38 -06:00
SIGNAL="$(iw dev "$IFACE" station get "$MAC" | grep "signal:" | awk '{print $2}')"
2021-02-21 19:20:41 -06:00
echo "${MAC}|?|?|${SIGNAL}_dBm"
2021-01-19 23:10:02 -06:00
fi
done
2018-08-31 05:41:06 -05:00
}
2021-02-21 19:20:41 -06:00
list_clients() { # passive mode. (use 'arp-scan' or 'netdiscover' if want active mode)
2021-01-19 23:10:02 -06:00
local IFACE pid
local CONFDIR
local output=""
# If number (PID) is given, get the associated wifi iface
2018-08-31 05:41:06 -05:00
if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then
pid="$1"
2021-01-19 23:10:02 -06:00
IFACE=$(get_subn_iface_from_pid "$pid")
if [[ -z "$IFACE" ]] ; then
echo "'$pid' is not the pid of a running $PROGNAME instance." >&2
exit 1
fi
else # non-number given
IFACE="$1"
2024-01-13 18:49:38 -06:00
if ( ! is_interface "$IFACE" ) ; then
2021-02-21 19:20:41 -06:00
echo "'$IFACE' is not an interface or PID" >&2
2021-01-19 23:10:02 -06:00
exit 1
fi
pid=$(get_pid_from_subn_iface "$IFACE")
if [[ -n "$pid" ]] ; then # if this interface is hosted by us
CONFDIR=$(get_confdir_from_pid "$pid")
output="$(print_clients_from_leases "$CONFDIR/dnsmasq.leases" )"
else # this interface NOT hosted by us
echo "Tip: '$IFACE' is not an interface hosted by $PROGNAME" >&2
fi
2018-08-31 05:41:06 -05:00
fi
2024-01-13 18:49:38 -06:00
output="$(echo "$output" ; print_interface_neighbors_via_iw "$IFACE") "
output="$(echo "$output" ; print_interface_neighbors_via_iproute "$IFACE")"
2021-01-19 23:10:02 -06:00
output="$(echo "$output" | sort -k 1 -k 2 -t '|' | uniq | sed -r '/^\s*$/d')"
2018-08-31 05:41:06 -05:00
2024-01-13 18:49:38 -06:00
echo "$IFACE ($(get_interface_mac "$IFACE")) neighbors:"
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
local fmt="%-19s%-41s%-20s%s" # string length: MAC 17, ipv4 15, ipv6 39, hostname ?
printf "$fmt\n" "MAC" "IP" "HOSTNAME" "INFO"
local line
2024-01-13 18:49:38 -06:00
echo "$output"| while read -r line
2021-01-19 23:10:02 -06:00
do
if [[ -n "$line" ]]; then
echo "$line" | awk -F'|' "{printf \"$fmt\n\",\$1,\$2,\$3,\$4}"
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
done
# TODO : merge same mac and same ip line
2018-08-31 05:41:06 -05:00
}
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
2024-01-13 18:49:38 -06:00
if is_running_pid "$1"; then
kill -USR1 "$1"
2018-08-31 05:41:06 -05:00
return
fi
# send stop signal to specific interface
for x in $(list_running | grep -E " \(?${1}( |\)?\$)" | cut -f1 -d' '); do
2024-01-13 18:49:38 -06:00
kill -USR1 "$x"
2018-08-31 05:41:06 -05:00
done
}
## ========================================================
## ========================================================
2021-01-19 23:10:02 -06:00
# 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
2021-01-19 23:10:02 -06:00
decide_tmpdir(){
local TMPD
2021-01-19 23:10:02 -06:00
if [[ -d /dev/shm ]]; then
TMPD=/dev/shm
elif [[ -d /run/shm ]]; then
TMPD=/run/shm
else
TMPD=/tmp
fi
2021-01-19 23:10:02 -06:00
#TMPDIR=$TMPD/lnxrouter_tmp
echo "$TMPD/lnxrouter_tmp"
2021-01-19 23:10:02 -06:00
}
2018-08-31 05:41:06 -05:00
#======
2021-01-19 23:10:02 -06:00
check_other_functions(){
if [[ $LIST_RUNNING -eq 1 ]]; then
echo -e "List of running $PROGNAME instances:\n"
list_running
exit 0
fi
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
if [[ -n "$LIST_CLIENTS_ID" ]]; then
list_clients "$LIST_CLIENTS_ID"
exit 0
fi
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
##### root test ##### NOTE above don't require root ##########
2021-01-19 23:10:02 -06:00
if [[ $(id -u) -ne 0 ]]; then
2021-11-06 21:31:15 -05:00
echo "ERROR: Need root to continue" >&2
2021-01-19 23:10:02 -06:00
exit 1
fi
2021-01-19 23:10:02 -06:00
###### NOTE below require root ##########
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
if [[ -n "$STOP_ID" ]]; then
echo "Trying to kill $PROGNAME instance associated with $STOP_ID..."
send_stop "$STOP_ID"
exit 0
fi
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
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
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
#============================
2021-01-19 23:10:02 -06:00
check_wifi_settings() {
2021-02-21 19:20:41 -06:00
2024-01-13 18:49:38 -06:00
if ! ( which iw > /dev/null 2>&1 && iw dev "$WIFI_IFACE" info > /dev/null 2>&1 ); then
2023-10-03 22:43:35 -05:00
echo "WARN: Can't use 'iw' to operate interfce '$WIFI_IFACE', trying 'iwconfig' (not as good as 'iw') ... (Did you spell the interface name right?)" >&2
2021-04-16 23:29:53 -05:00
USE_IWCONFIG=1
fi
if [[ $USE_IWCONFIG -eq 1 ]]; then
2024-01-13 18:49:38 -06:00
if ! (which iwconfig > /dev/null 2>&1 && iwconfig "$WIFI_IFACE" > /dev/null 2>&1); then
2021-04-16 23:29:53 -05:00
echo "ERROR: Can't use 'iwconfig' to operate interfce '$WIFI_IFACE'" >&2
2021-02-21 19:20:41 -06:00
exit 1
fi
fi
2021-01-19 23:10:02 -06:00
if [[ $FREQ_BAND != 2.4 && $FREQ_BAND != 5 ]]; then
echo "ERROR: Invalid frequency band" >&2
exit 1
fi
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
if [[ $FREQ_BAND != 5 && $CHANNEL -gt 14 ]]; then
echo "Channel number is greater than 14, assuming 5GHz frequency band"
FREQ_BAND=5
fi
2018-08-31 05:41:06 -05:00
2024-01-13 18:49:38 -06:00
if ! can_be_ap "${WIFI_IFACE}"; then
2021-01-19 23:10:02 -06:00
echo "ERROR: Your adapter does not support AP (master) mode" >&2
exit 1
fi
2018-08-31 05:41:06 -05:00
2024-01-13 18:49:38 -06:00
if ! can_be_sta_and_ap "${WIFI_IFACE}"; then
if is_interface_wifi_connected "${WIFI_IFACE}"; then
2021-01-19 23:10:02 -06:00
echo "ERROR: Your adapter can not be a station (i.e. be connected) and an AP at the same time" >&2
2018-08-31 05:41:06 -05:00
exit 1
2021-01-19 23:10:02 -06:00
elif [[ $NO_VIRT -eq 0 ]]; then
echo "WARN: Your adapter does not fully support AP virtual interface, enabling --no-virt" >&2
NO_VIRT=1
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
fi
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
HOSTAPD=$(which hostapd)
2021-01-19 23:10:02 -06:00
2024-01-13 18:49:38 -06:00
if [[ $(get_adapter_kernel_module "${WIFI_IFACE}") =~ ^(8192[cd][ue]|8723a[sue])$ ]]; then
2021-01-19 23:10:02 -06:00
if ! strings "$HOSTAPD" | grep -m1 rtl871xdrv > /dev/null 2>&1; then
echo "ERROR: You need to patch your hostapd with rtl871xdrv patches." >&2
2021-01-19 23:10:02 -06:00
exit 1
fi
2021-01-19 23:10:02 -06:00
if [[ $DRIVER != "rtl871xdrv" ]]; then
echo "WARN: Your adapter needs rtl871xdrv, enabling --driver=rtl871xdrv" >&2
DRIVER=rtl871xdrv
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
fi
if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then
echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2
exit 1
fi
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
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
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
elif [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then
echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2
exit 1
fi
2021-01-19 23:10:02 -06:00
2024-01-13 18:49:38 -06:00
if [[ $(get_adapter_kernel_module "${WIFI_IFACE}") =~ ^rtl[0-9].*$ ]]; then
2021-01-19 23:10:02 -06:00
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
2018-08-31 05:41:06 -05:00
fi
2022-09-26 14:26:50 -05:00
2023-01-04 14:04:00 -06:00
if [[ -z $VIRT_NAME ]]; then
2022-09-26 14:26:50 -05:00
if [[ ${#WIFI_IFACE} -gt 13 ]]; then
echo "WARN: $WIFI_IFACE has ${#WIFI_IFACE} characters which might be too long. If AP doesn't work, see --virt-name and https://github.com/garywill/linux-router/issues/44" >&2
fi
2023-01-04 14:04:00 -06:00
elif [[ ${#VIRT_NAME} -gt 15 ]]; then
echo "WARN: option --virt-name $VIRT_NAME has ${#VIRT_NAME} characters which might be too long, consider making it shorter in case of errors" >&2
fi
2024-01-13 18:49:38 -06:00
if [[ ! -z $VIRT_NAME ]] && is_vface_name_allocated "$VIRT_NAME"; then
2023-01-04 14:04:00 -06:00
echo "WARN: interface $VIRT_NAME aleady exists, this will cause an error"
2022-09-26 14:26:50 -05:00
fi
2021-01-19 23:10:02 -06:00
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
check_if_new_mac_valid() {
2021-01-19 23:10:02 -06:00
if ! is_unicast_macaddr "$NEW_MACADDR"; then
echo "ERROR: The first byte of MAC address (${NEW_MACADDR}) must be even" >&2
exit 1
fi
2018-08-31 05:41:06 -05:00
2024-01-13 18:49:38 -06:00
if [[ $(get_all_mac_in_system | grep -c "${NEW_MACADDR}") -ne 0 ]]; then
2021-01-19 23:10:02 -06:00
echo "WARN: MAC address '${NEW_MACADDR}' already exists" >&2
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
decide_target_interface() {
2021-01-19 23:10:02 -06:00
# TARGET_IFACE is a existing physical interface
if [[ "$CONN_IFACE" ]]; then
echo "$CONN_IFACE"
elif [[ "$WIFI_IFACE" ]]; then
echo "$WIFI_IFACE"
2021-01-19 23:10:02 -06:00
else
2021-02-21 19:20:41 -06:00
echo "No target interface specified" >&2
return 1
2021-01-19 23:10:02 -06:00
fi
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
decide_ip_addresses() {
if [[ ! -n $GATEWAY ]]; then
2021-01-19 23:10:02 -06:00
GATEWAY="$(generate_random_ip4)"
echo "Use random LAN IPv4 address $GATEWAY"
2021-04-16 23:29:53 -05:00
elif [[ ! "$GATEWAY" =~ "." ]]; then
GATEWAY="192.168.${GATEWAY}.1"
2021-01-19 23:10:02 -06:00
fi
2020-12-24 06:36:09 -06:00
2021-01-19 23:10:02 -06:00
if [[ $IPV6 -eq 1 && ! -n $PREFIX6 ]]; then
2021-01-19 23:10:02 -06:00
PREFIX6="$(generate_random_lan_ip6_prefix)"
echo "Use random LAN IPv6 address ${PREFIX6}${IID6}"
2021-04-16 23:29:53 -05:00
elif [[ ! "$PREFIX6" =~ ":" ]]; then
PREFIX6="fd00:0:0:${PREFIX6}::"
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
if [[ $IPV6 -eq 1 ]]; then
2021-01-19 23:10:02 -06:00
GATEWAY6="${PREFIX6}${IID6}"
2021-01-19 23:10:02 -06:00
fi
2024-02-24 20:00:00 -06:00
SUBNET_NET4="${GATEWAY%.*}.0/24"
[[ $IPV6 -eq 1 ]] && SUBNET_NET6="${PREFIX6}/64"
2021-01-19 23:10:02 -06:00
}
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
prepare_wifi_interface() {
2021-01-19 23:10:02 -06:00
if [[ $USE_IWCONFIG -eq 0 ]]; then
2024-01-13 18:49:38 -06:00
iw dev "${WIFI_IFACE}" set power_save off
2021-01-19 23:10:02 -06:00
fi
if [[ $NO_VIRT -eq 0 ]]; then
2021-01-19 23:10:02 -06:00
## Will generate virtual wifi interface
2023-10-03 22:21:00 -05:00
# TODO move this to check_wifi_settings() ?
2024-01-13 18:49:38 -06:00
if is_interface_wifi_connected "${WIFI_IFACE}"; then
2024-01-18 23:50:34 -06:00
WIFI_IFACE_FREQ=$(iw dev "${WIFI_IFACE}" link | grep -i freq | awk '{print $2}' | sed 's/\.00*$//g') # NOTE we assume integer currently, which can be right, or wrong in the future
2024-01-13 18:49:38 -06:00
WIFI_IFACE_CHANNEL=$(ieee80211_frequency_to_channel "${WIFI_IFACE_FREQ}")
2023-10-03 22:21:00 -05:00
echo "${WIFI_IFACE} already working in channel ${WIFI_IFACE_CHANNEL} (${WIFI_IFACE_FREQ} MHz)"
if [[ $CHANNEL == default ]]; then
echo "Use wifi adapter current channel $WIFI_IFACE_CHANNEL as target channel"
CHANNEL=$WIFI_IFACE_CHANNEL
2021-01-19 23:10:02 -06:00
fi
2023-10-03 22:21:00 -05:00
2021-01-19 23:10:02 -06:00
if [[ $WIFI_IFACE_CHANNEL -ne $CHANNEL ]]; then
2023-10-03 22:21:00 -05:00
echo "WARN: Wifi adapter already working in channel ${WIFI_IFACE_CHANNEL}, which is different than target channel $CHANNEL" >&2
2021-01-19 23:10:02 -06:00
fi
fi
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
echo "Creating a virtual WiFi interface... "
2021-01-19 23:10:02 -06:00
VWIFI_IFACE=$(alloc_new_vface_name)
2024-01-13 18:49:38 -06:00
if iw dev "${WIFI_IFACE}" interface add "${VWIFI_IFACE}" type __ap; then
2021-01-19 23:10:02 -06:00
# Successfully created virtual wifi interface
2021-02-21 19:20:41 -06:00
# if NM running, it will give the new virtual interface a random MAC. MAC will go back after setting NM unmanaged
sleep 2
echo "${VWIFI_IFACE} created"
2021-01-19 23:10:02 -06:00
else
VWIFI_IFACE=
2023-01-04 14:04:00 -06:00
if [[ ! -z ${VIRT_NAME} ]] && [[ ${#VIRT_NAME} -gt 15 ]]; then
die "Failed creating virtual WiFi interface. This is likely because you have set a long name for your virtual interface using --virt-name, try making it shorter'"
elif [[ -z ${VIRT_NAME} ]] && [[ ${#WIFI_IFACE} -gt 13 ]]; then
die "Failed creating virtual WiFi interface. This is likely because your interface name is too long. Try using '--virt-name <shorter interface name>'"
else
die "Failed creating virtual WiFi interface. Maybe your WiFi adapter does not fully support virtual interfaces. Try again with '--no-virt'"
fi
2021-01-19 23:10:02 -06:00
fi
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
AP_IFACE=${VWIFI_IFACE}
2021-01-19 23:10:02 -06:00
else # no virtual wifi interface, use wifi device interface itself
2021-01-19 23:10:02 -06:00
AP_IFACE=${WIFI_IFACE}
2018-08-31 05:41:06 -05:00
fi
2023-10-03 22:21:00 -05:00
if [[ $CHANNEL == default ]]; then
echo "Channel not specified, use default"
if [[ $FREQ_BAND == 2.4 ]]; then
CHANNEL=1
else
CHANNEL=36
fi
fi
2021-01-19 23:10:02 -06:00
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
decide_subnet_interface() {
if [[ $WIFI_IFACE ]]; then
2021-01-19 23:10:02 -06:00
echo "${AP_IFACE}"
2021-01-19 23:10:02 -06:00
else
2021-01-19 23:10:02 -06:00
echo "${TARGET_IFACE}"
fi
}
dealwith_mac() {
local VMAC
if [[ -n "$NEW_MACADDR" ]] ; then # user choose to set subnet mac
echo "Setting ${SUBNET_IFACE} new MAC address ${NEW_MACADDR} ..."
2024-01-13 18:49:38 -06:00
set_interface_mac "${SUBNET_IFACE}" "${NEW_MACADDR}" || die "Failed setting new MAC address"
2021-01-19 23:10:02 -06:00
elif [[ $VWIFI_IFACE ]]; then # user didn't choose to set mac, but using virtual wifi interface
2024-01-13 18:49:38 -06:00
VMAC=$(get_new_macaddr_according_to_existing "${WIFI_IFACE}")
2021-01-19 23:10:02 -06:00
if [[ "$VMAC" ]]; then
echo "Assigning MAC address $VMAC to virtual interface $VWIFI_IFACE according to $WIFI_IFACE ..."
2024-01-13 18:49:38 -06:00
set_interface_mac "$VWIFI_IFACE" "$VMAC"
2021-01-19 23:10:02 -06:00
fi
2021-01-19 23:10:02 -06:00
fi
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
write_hostapd_conf() {
2021-01-19 23:10:02 -06:00
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
2021-01-19 23:10:02 -06:00
EOF
2021-01-19 23:10:02 -06:00
fi
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
if [[ $FREQ_BAND == 2.4 ]]; then
echo "hw_mode=g" >> "$CONFDIR/hostapd.conf"
else
echo "hw_mode=a" >> "$CONFDIR/hostapd.conf"
fi
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
if [[ $MAC_FILTER -eq 1 ]]; then
cat <<- EOF >> "$CONFDIR/hostapd.conf"
macaddr_acl=${MAC_FILTER}
accept_mac_file=${MAC_FILTER_ACCEPT}
EOF
fi
2018-08-31 05:41:06 -05:00
2023-09-28 01:12:00 -05:00
if [[ $HOTSPOT20 -eq 1 ]]; then
echo "hs20=1" >> "$CONFDIR/hostapd.conf"
fi
2021-01-19 23:10:02 -06:00
if [[ $IEEE80211N -eq 1 ]]; then
cat <<- EOF >> "$CONFDIR/hostapd.conf"
ieee80211n=1
ht_capab=${HT_CAPAB}
EOF
fi
2018-08-31 05:41:06 -05:00
2023-09-28 01:46:59 -05:00
if [[ $REQUIREHT -eq 1 ]]; then
echo "require_ht=1" >> "$CONFDIR/hostapd.conf"
fi
2021-01-19 23:10:02 -06:00
if [[ $IEEE80211AC -eq 1 ]]; then
echo "ieee80211ac=1" >> "$CONFDIR/hostapd.conf"
fi
2018-08-31 05:41:06 -05:00
2023-09-28 01:46:59 -05:00
if [[ $REQUIREVHT -eq 1 ]]; then
echo "require_vht=1" >> "$CONFDIR/hostapd.conf"
fi
2021-01-19 23:10:02 -06:00
if [[ -n "$VHT_CAPAB" ]]; then
echo "vht_capab=${VHT_CAPAB}" >> "$CONFDIR/hostapd.conf"
fi
2018-08-31 05:41:06 -05:00
2023-09-28 01:46:59 -05:00
if [[ $VHTCHANNELWIDTH -gt 0 ]]; then
cat <<- EOF >> "$CONFDIR/hostapd.conf"
vht_oper_chwidth=${VHTCHANNELWIDTH}
EOF
fi
if [[ $VHTSEG0CHINDEX -gt 0 ]]; then
cat <<- EOF >> "$CONFDIR/hostapd.conf"
vht_oper_centr_freq_seg0_idx=${VHTSEG0CHINDEX}
EOF
fi
if [[ $VHTSEG1CHINDEX -gt 0 ]]; then
cat <<- EOF >> "$CONFDIR/hostapd.conf"
2023-10-02 02:34:41 -05:00
vht_oper_centr_freq_seg1_idx=${VHTSEG1CHINDEX}
2023-09-28 01:46:59 -05:00
EOF
fi
2021-01-19 23:10:02 -06:00
if [[ $IEEE80211N -eq 1 ]] || [[ $IEEE80211AC -eq 1 ]]; then
echo "wmm_enabled=1" >> "$CONFDIR/hostapd.conf"
fi
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
if [[ -n "$PASSPHRASE" ]]; then
[[ "$WPA_VERSION" == "1+2" ]] && WPA_VERSION=3
if [[ $USE_PSK -eq 0 ]]; then
WPA_KEY_TYPE=passphrase
2021-01-19 23:10:02 -06:00
else
2021-01-19 23:10:02 -06:00
WPA_KEY_TYPE=psk
2021-01-19 23:10:02 -06:00
fi
2021-01-19 23:10:02 -06:00
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
2021-11-06 21:31:15 -05:00
echo "WARN: WiFi is not protected by password" >&2
2021-01-19 23:10:02 -06:00
fi
2021-01-19 23:10:02 -06:00
chmod 600 "$CONFDIR/hostapd.conf"
2021-01-19 23:10:02 -06:00
}
write_dnsmasq_conf() {
2021-01-19 23:10:02 -06:00
if grep "^nobody:" /etc/group >/dev/null 2>&1 ; then
NOBODY_GROUP="nobody"
else
NOBODY_GROUP="nogroup"
fi
2021-04-16 23:29:53 -05:00
mkfifo "$CONFDIR/dnsmasq.log" || die "Failed creating pipe file for dnsmasq"
chown nobody "$CONFDIR/dnsmasq.log" || die "Failed changing dnsmasq log file owner"
cat "$CONFDIR/dnsmasq.log" &
2021-01-19 23:10:02 -06:00
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
2021-04-16 23:29:53 -05:00
log-facility=$CONFDIR/dnsmasq.log
2021-01-19 23:10:02 -06:00
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"
2021-01-19 23:10:02 -06:00
else
2021-01-19 23:10:02 -06:00
dns_offer="$DHCP_DNS"
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
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
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
[[ -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
2024-01-13 18:49:38 -06:00
sep_ip_port "$(echo "$DNS" | cut -d, -f$i)" DNS_IP DNS_PORT
2021-01-19 23:10:02 -06:00
[[ "$DNS_PORT" ]] && DNS_PORT_D="#$DNS_PORT"
echo "server=${DNS_IP}${DNS_PORT_D}" >> "$CONFDIR/dnsmasq.conf"
done
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
cat <<- EOF >> "$CONFDIR/dnsmasq.conf"
no-resolv
no-poll
EOF
fi
2022-04-03 05:03:51 -05:00
if [[ $DNS_NOCACHE -eq 1 ]]; then
echo "cache-size=0" >> "$CONFDIR/dnsmasq.conf"
echo "no-negcache" >> "$CONFDIR/dnsmasq.conf"
fi
2021-01-19 23:10:02 -06:00
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"
2021-01-19 23:10:02 -06:00
fi
2021-01-19 23:10:02 -06:00
echo "dhcp-option=option6:dns-server,${dns_offer6}" >> "$CONFDIR/dnsmasq.conf"
2021-01-19 23:10:02 -06:00
fi
fi
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
run_wifi_ap_processes() {
2021-01-19 23:10:02 -06:00
if [[ $NO_HAVEGED -eq 0 ]]; then
haveged_watchdog &
HAVEGED_WATCHDOG_PID=$!
echo "$HAVEGED_WATCHDOG_PID" > "$CONFDIR/haveged_watchdog.pid"
2021-08-28 21:24:50 -05:00
echo
2021-01-19 23:10:02 -06:00
echo "haveged_watchdog PID: $HAVEGED_WATCHDOG_PID"
fi
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
# 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)
2024-01-13 18:49:38 -06:00
STDBUF_PATH=$(which stdbuf)
2021-01-19 23:10:02 -06:00
if [ $? -eq 0 ]; then
STDBUF_PATH=$STDBUF_PATH" -oL"
2021-01-19 23:10:02 -06:00
fi
2021-01-19 23:10:02 -06:00
echo
echo "Starting hostapd"
2021-08-28 21:24:50 -05:00
if which complain > /dev/null 2>&1; then
complain hostapd
fi
2021-01-19 23:10:02 -06:00
# 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
2024-01-13 18:49:38 -06:00
pid_watchdog "$HOSTAPD_PID" 10 "hostapd failed. (tip: try '--hostapd-debug' to get some debug info)" &
2021-01-19 23:10:02 -06:00
sleep 3
2021-01-19 23:10:02 -06:00
}
start_dnsmasq() {
2021-08-28 21:24:50 -05:00
echo
echo "Starting dnsmasq"
2021-01-19 23:10:02 -06:00
if which complain > /dev/null 2>&1; then
# openSUSE's apparmor does not allow dnsmasq to read files.
# remove restriction.
complain dnsmasq
2018-08-31 05:41:06 -05:00
fi
2021-08-28 21:24:50 -05:00
2021-01-19 23:10:02 -06:00
# 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
2021-04-16 23:29:53 -05:00
DNSMASQ_PID="$(cat "$CONFDIR/dnsmasq.pid" )"
echo "dnsmasq PID: $DNSMASQ_PID"
2021-01-19 23:10:02 -06:00
######(wait $DNSMASQ_PID ; die "dnsmasq failed") & # wait can't deal with non-child
2024-01-13 18:49:38 -06:00
pid_watchdog "$DNSMASQ_PID" 9 "dnsmasq failed" &
2021-01-19 23:10:02 -06:00
sleep 2
2021-01-19 23:10:02 -06:00
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
check_rfkill_unblock_wifi() {
local PHY
2021-01-19 23:10:02 -06:00
if which rfkill > /dev/null 2>&1 ; then
2024-01-13 18:49:38 -06:00
PHY=$(get_interface_phy_device "${SUBNET_IFACE}")
[[ -n $PHY ]] && rfkill unblock $(rfkill | grep "$PHY" | awk '{print $1}') >/dev/null 2>&1
2021-01-19 23:10:02 -06:00
fi
}
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
#=========== Above are functions ======================
#=========== Executing begin ==============================
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# if empty option, show usage and exit
2021-01-19 23:10:02 -06:00
check_empty_option "$@"
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# TODO: are some global variables are still defined in those following code?
2021-01-19 23:10:02 -06:00
define_global_variables
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
ARGS=( "$@" )
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
parse_user_options "$@"
2021-01-19 23:10:02 -06:00
# TODO: detect user option conflict
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# check if networkManager running
2021-01-19 23:10:02 -06:00
NM_RUNNING="$(is_nm_running)"
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
TMPDIR="$(decide_tmpdir)"
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# if user choose to deal with running instances, will output some info then exit after this
# NOTE above don't require root
2021-01-19 23:10:02 -06:00
check_other_functions
2021-01-19 23:10:02 -06:00
# NOTE below require root
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# if user choose to daemonize, will start new background process and exit this
2021-01-19 23:10:02 -06:00
daemonizing_check
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# check if wifi will work on this system and user settings
2021-01-19 23:10:02 -06:00
[[ $WIFI_IFACE ]] && check_wifi_settings
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
[[ -n "$NEW_MACADDR" ]] && check_if_new_mac_valid # check NEW_MACADDR. will exit if not valid
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# checks finished
## ===== Above don't echo anything if no warning or error====================
## ========================================================
2021-08-28 21:24:50 -05:00
phead
2021-11-06 21:28:36 -05:00
phead2
echo
2021-01-19 23:10:02 -06:00
echo "PID: $$"
2021-02-21 19:20:41 -06:00
TARGET_IFACE="$(decide_target_interface)" || exit 1 # judge wired (-i CONN_IFACE) or wireless hotspot (--ap $WIFI_IFACE)
2024-01-14 06:25:06 -06:00
echo "Target interface is ${TARGET_IFACE} ($(get_interface_mac "$TARGET_IFACE")) "
show_interface_pci_info "$TARGET_IFACE"
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
if [[ "$MAC_USE_RANDOM" -eq 1 ]] ; then
NEW_MACADDR="$(generate_random_mac)"
echo "Use random MAC address $NEW_MACADDR"
fi
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
decide_ip_addresses # ip 4 & 6 lan addresses
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
# 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
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
#===========================================================
#==== begin to do some change on config files and system===
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
init_trap
2021-02-21 19:20:41 -06:00
# NOTE function die() is designed not to be used before init_trap() executed
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
init_conf_dirs # CONFDIR , COMMON_CONFDIR . make dir
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
[[ $WIFI_IFACE ]] && prepare_wifi_interface # this will create virtual ap interface (if needed) and set VWIFI_IFACE and AP_IFACE (if success)
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
SUBNET_IFACE="$(decide_subnet_interface)" # SUBNET_IFACE can be TARGET_IFACE (wired) or AP_IFACE (ap) .this is after prepare_wifi_interface()
2021-01-19 23:10:02 -06:00
echo "$SUBNET_IFACE" > "$CONFDIR/subn_iface"
2021-01-19 23:10:02 -06:00
# if virtual wifi interface, will be destroyed, so only need to save status when not
[[ -z $VWIFI_IFACE ]] && backup_interface_status
# TODO: should these 2 before calling prepare_wifi_interface ? in check_wifi_settings() ?
# set iw country code
2021-01-19 23:10:02 -06:00
if [[ $WIFI_IFACE && -n "$COUNTRY" && $USE_IWCONFIG -eq 0 ]]; then
iw reg set "$COUNTRY" || die "Failed setting country code"
fi
2021-01-19 23:10:02 -06:00
# judge channel availability after changing country code
2021-01-19 23:10:02 -06:00
if [[ $WIFI_IFACE ]] ; then
2024-01-13 18:49:38 -06:00
can_transmit_to_channel "${AP_IFACE}" ${CHANNEL} || die "Your adapter can not transmit to channel ${CHANNEL}, frequency band ${FREQ_BAND}GHz."
2021-01-19 23:10:02 -06:00
fi
2021-01-19 23:10:02 -06:00
[[ $WIFI_IFACE ]] && write_hostapd_conf
2018-08-31 05:41:06 -05:00
#===================================================
#===================================================
2021-01-19 23:10:02 -06:00
# set interface unmanaged by networkManager
2024-01-13 18:49:38 -06:00
if [[ $NM_RUNNING -eq 1 ]] && nm_knows "$TARGET_IFACE"; then # if nm knows target iface, should know subnet iface too. but need to wait until nm finds subnet iface (waiting code is in nm_set_unmanaged()
nm_set_unmanaged "${SUBNET_IFACE}" # will write NM_UNM_LIST
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
[[ $NO_DNSMASQ -eq 0 ]] && write_dnsmasq_conf
2018-08-31 05:41:06 -05:00
#===========================
# initialize subnet interface
2021-01-19 23:10:02 -06:00
# take subnet interface down first
2024-01-13 18:49:38 -06:00
ip link set down dev "${SUBNET_IFACE}" || die "Failed setting ${SUBNET_IFACE} down"
2021-01-19 23:10:02 -06:00
# flush old IPs of subnet interface
2024-01-13 18:49:38 -06:00
ip addr flush "${SUBNET_IFACE}" || die "Failed flush ${SUBNET_IFACE} IP"
2018-08-31 05:41:06 -05:00
2021-02-21 19:20:41 -06:00
dealwith_mac # setting MAC should be after setting NM unmanaged
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
[[ $WIFI_IFACE ]] && check_rfkill_unblock_wifi
2021-01-19 23:10:02 -06:00
2021-01-19 23:10:02 -06:00
# bring subnet interface up
2024-01-13 18:49:38 -06:00
ip link set up dev "${SUBNET_IFACE}" || die "Failed bringing ${SUBNET_IFACE} up"
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# hostapd , haveged
[[ $WIFI_IFACE ]] && run_wifi_ap_processes
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
# add ipv4 address to subnet interface
2021-01-19 23:10:02 -06:00
ip -4 addr add ${GATEWAY}/24 broadcast ${GATEWAY%.*}.255 dev ${SUBNET_IFACE} || die "Failed setting ${SUBNET_IFACE} IPv4 address"
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
set_ipv6_bits
2021-01-19 23:10:02 -06:00
# add ipv6 address to subnet interface
if [[ $IPV6 -eq 1 ]] ; then
ip -6 addr add ${GATEWAY6}/64 dev ${SUBNET_IFACE} || die "Failed setting ${SUBNET_IFACE} IPv6 address"
fi
2020-12-24 06:36:09 -06:00
2021-04-16 23:29:53 -05:00
check_iptables
2021-11-06 21:31:15 -05:00
echo "NOTICE: Not showing all operations done to iptables rules"
[[ "$NO4" -eq 1 ]] && echo -e "\nWARN: Since you're using in this mode (no IPv4 Internet), make sure you've read Notice 1\n" >&2
2018-08-31 05:41:06 -05:00
# enable Internet sharing
if [[ "$SHARE_METHOD" == "none" ]]; then
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
echo "No Internet sharing"
2021-01-19 23:10:02 -06:00
2021-11-06 21:31:15 -05:00
echo -e "\nWARN: Since you're using in this mode (no Internet share), make sure you've read Notice 1\n" >&2
2020-12-24 06:36:09 -06:00
[[ "$BANLAN" -eq 1 ]] && start_ban_lan
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
elif [[ "$SHARE_METHOD" == "nat" ]]; then
2021-01-19 23:10:02 -06:00
2021-11-06 21:31:15 -05:00
[[ "$INTERNET_IFACE" ]] && echo -e "\nWARN: Since you're using in this mode (specify Internet interface), make sure you've read Notice 1\n" >&2
2021-11-06 21:28:36 -05:00
[[ "$INTERNET_IFACE" && "$dnsmasq_NO_DNS" -eq 0 ]] && echo -e "\nWARN: You specified Internet interface but this host is providing local DNS. In some unexpected case (eg. mistaken configurations), queries may leak to other interfaces, which you should be aware of.\n" >&2
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
start_nat
2021-01-19 23:10:02 -06:00
2020-12-24 06:36:09 -06:00
[[ "$BANLAN" -eq 1 ]] && start_ban_lan
2021-01-19 23:10:02 -06:00
2021-11-06 21:31:15 -05:00
echo 1 > "/proc/sys/net/ipv4/ip_forward" || die "Failed enabling system ipv4 forwarding" # TODO maybe uneeded in '--no4' mode
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
if [[ $IPV6 -eq 1 ]]; then
2021-11-06 21:31:15 -05:00
echo 1 > "/proc/sys/net/ipv6/conf/all/forwarding" || die "Failed enabling system ipv6 forwarding" # TODO if '-o' used, set only 2 interfaces' bits
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
# to enable clients to establish PPTP connections we must
# load nf_nat_pptp module
2021-01-19 23:10:02 -06:00
modprobe nf_nat_pptp > /dev/null 2>&1 && echo "Loaded kernel module nf_nat_pptp"
2018-08-31 05:41:06 -05:00
elif [[ "$SHARE_METHOD" == "redsocks" ]]; then
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
if [[ $IPV6 -eq 1 ]]; then
2021-01-19 23:10:02 -06:00
echo 1 > "/proc/sys/net/ipv6/conf/$SUBNET_IFACE/forwarding" || die "Failed enabling $SUBNET_IFACE ipv6 forwarding" # to set NA router bit
2018-08-31 05:41:06 -05:00
fi
2021-01-19 23:10:02 -06:00
2021-11-06 21:28:36 -05:00
[[ "$dnsmasq_NO_DNS" -eq 0 && ! $DNS ]] && echo -e "\nWARN: You are using in transparent proxy mode but this host is providing local DNS. In some unexpected case (eg. mistaken configurations), queries may leak to other interfaces, which you should be aware of.\n" >&2
2018-08-31 05:41:06 -05:00
2020-12-24 06:36:09 -06:00
[[ "$BANLAN" -eq 1 ]] && start_ban_lan
2021-01-19 23:10:02 -06:00
2018-08-31 05:41:06 -05:00
start_redsocks
fi
# start dhcp + dns (optional)
2021-01-19 23:10:02 -06:00
# 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
2018-08-31 05:41:06 -05:00
[[ "$CATCH_DNS" -eq 1 ]] && start_catch_dns
2021-01-19 23:10:02 -06:00
[[ $NO_DNSMASQ -eq 0 ]] && ( allow_dhcp ; start_dnsmasq )
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
echo
2022-01-05 06:01:38 -06:00
echo "== Setting up completed, now linux-router should be working =="
2018-08-31 05:41:06 -05:00
2021-01-19 23:10:02 -06:00
#============================================================
#============================================================
#============================================================
2018-08-31 05:41:06 -05:00
2020-12-24 06:36:09 -06:00
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};\""
2021-01-19 23:10:02 -06:00
echo
2020-12-24 06:36:09 -06:00
}
[[ "$QR" -eq 1 ]] && show_qr
2018-08-31 05:41:06 -05:00
# need loop to keep this script running
bash -c "while :; do sleep 8000 ; done " &
KEEP_RUNNING_PID=$!
2021-01-19 23:10:02 -06:00
echo "$KEEP_RUNNING_PID" > "$CONFDIR/keep_running.pid"
2024-01-13 18:49:38 -06:00
wait "$KEEP_RUNNING_PID"
2018-08-31 05:41:06 -05:00
clean_exit