|
#!/bin/bash |
|
|
|
# general dependencies: |
|
# bash (to run this script) |
|
# util-linux (for getopt) |
|
# procps or procps-ng |
|
# hostapd |
|
# iproute2 |
|
# iw |
|
# iwconfig (you only need this if 'iw' can not recognize your adapter) |
|
# haveged (optional) |
|
|
|
# dependencies for 'nat' or 'none' Internet sharing method |
|
# dnsmasq |
|
# iptables |
|
|
|
VERSION=0.4.6 |
|
PROGNAME="$(basename $0)" |
|
|
|
# make sure that all command outputs are in english |
|
# so we can parse them correctly |
|
export LC_ALL=C |
|
|
|
# all new files and directories must be readable only by root. |
|
# in special cases we must use chmod to give any other permissions. |
|
SCRIPT_UMASK=0077 |
|
umask $SCRIPT_UMASK |
|
|
|
usage() { |
|
echo "Usage: "$PROGNAME" [options] <wifi-interface> [<interface-with-internet>] [<access-point-name> [<passphrase>]]" |
|
echo |
|
echo "Options:" |
|
echo " -h, --help Show this help" |
|
echo " --version Print version number" |
|
echo " -c <channel> Channel number (default: 1)" |
|
echo " -w <WPA version> Use 1 for WPA, use 2 for WPA2, use 1+2 for both (default: 1+2)" |
|
echo " -n Disable Internet sharing (if you use this, don't pass" |
|
echo " the <interface-with-internet> argument)" |
|
echo " -m <method> Method for Internet sharing." |
|
echo " Use: 'nat' for NAT (default)" |
|
echo " 'bridge' for bridging" |
|
echo " 'none' for no Internet sharing (equivalent to -n)" |
|
echo " --psk Use 64 hex digits pre-shared-key instead of passphrase" |
|
echo " --hidden Make the Access Point hidden (do not broadcast the SSID)" |
|
echo " --mac-filter Enable MAC address filtering" |
|
echo " --mac-filter-accept Location of MAC address filter list (defaults to /etc/hostapd/hostapd.accept)" |
|
echo " --redirect-to-localhost If -n is set, redirect every web request to localhost (useful for public information networks)" |
|
echo " --hostapd-debug <level> With level between 1 and 2, passes arguments -d or -dd to hostapd for debugging." |
|
echo " --isolate-clients Disable communication between clients" |
|
echo " --ieee80211n Enable IEEE 802.11n (HT)" |
|
echo " --ieee80211ac Enable IEEE 802.11ac (VHT)" |
|
echo " --ht_capab <HT> HT capabilities (default: [HT40+])" |
|
echo " --vht_capab <VHT> VHT capabilities" |
|
echo " --country <code> Set two-letter country code for regularity (example: US)" |
|
echo " --freq-band <GHz> Set frequency band. Valid inputs: 2.4, 5 (default: 2.4)" |
|
echo " --driver Choose your WiFi adapter driver (default: nl80211)" |
|
echo " --no-virt Do not create virtual interface" |
|
echo " --no-haveged Do not run 'haveged' automatically when needed" |
|
echo " --fix-unmanaged If NetworkManager shows your interface as unmanaged after you" |
|
echo " close create_ap, then use this option to switch your interface" |
|
echo " back to managed" |
|
echo " --mac <MAC> Set MAC address" |
|
echo " --dhcp-dns <IP1[,IP2]> Set DNS returned by DHCP" |
|
echo " --daemon Run create_ap in the background" |
|
echo " --stop <id> Send stop command to an already running create_ap. For an <id>" |
|
echo " you can put the PID of create_ap or the WiFi interface. You can" |
|
echo " get them with --list-running" |
|
echo " --list-running Show the create_ap processes that are already running" |
|
echo " --list-clients <id> List the clients connected to create_ap instance associated with <id>." |
|
echo " For an <id> you can put the PID of create_ap or the WiFi interface." |
|
echo " If virtual WiFi interface was created, then use that one." |
|
echo " You can get them with --list-running" |
|
echo " --mkconfig <conf_file> Store configs in conf_file" |
|
echo " --config <conf_file> Load configs from conf_file" |
|
echo |
|
echo "Non-Bridging Options:" |
|
echo " --no-dns Disable dnsmasq DNS server" |
|
echo " --no-dnsmasq Disable dnsmasq server completely" |
|
echo " -g <gateway> IPv4 Gateway for the Access Point (default: 192.168.12.1)" |
|
echo " -d DNS server will take into account /etc/hosts" |
|
echo " -e <hosts_file> DNS server will take into account additional hosts file" |
|
echo |
|
echo "Useful informations:" |
|
echo " * If you're not using the --no-virt option, then you can create an AP with the same" |
|
echo " interface you are getting your Internet connection." |
|
echo " * You can pass your SSID and password through pipe or through arguments (see examples)." |
|
echo " * On bridge method if the <interface-with-internet> is not a bridge interface, then" |
|
echo " a bridge interface is created automatically." |
|
echo |
|
echo "Examples:" |
|
echo " "$PROGNAME" wlan0 eth0 MyAccessPoint MyPassPhrase" |
|
echo " echo -e 'MyAccessPoint\nMyPassPhrase' | "$PROGNAME" wlan0 eth0" |
|
echo " "$PROGNAME" wlan0 eth0 MyAccessPoint" |
|
echo " echo 'MyAccessPoint' | "$PROGNAME" wlan0 eth0" |
|
echo " "$PROGNAME" wlan0 wlan0 MyAccessPoint MyPassPhrase" |
|
echo " "$PROGNAME" -n wlan0 MyAccessPoint MyPassPhrase" |
|
echo " "$PROGNAME" -m bridge wlan0 eth0 MyAccessPoint MyPassPhrase" |
|
echo " "$PROGNAME" -m bridge wlan0 br0 MyAccessPoint MyPassPhrase" |
|
echo " "$PROGNAME" --driver rtl871xdrv wlan0 eth0 MyAccessPoint MyPassPhrase" |
|
echo " "$PROGNAME" --daemon wlan0 eth0 MyAccessPoint MyPassPhrase" |
|
echo " "$PROGNAME" --stop wlan0" |
|
} |
|
|
|
# on success it echos a non-zero unused FD |
|
# on error it echos 0 |
|
get_avail_fd() { |
|
local x |
|
for x in $(seq 1 $(ulimit -n)); do |
|
if [[ ! -a "/proc/$BASHPID/fd/$x" ]]; then |
|
echo $x |
|
return |
|
fi |
|
done |
|
echo 0 |
|
} |
|
|
|
# lock file for the mutex counter |
|
COUNTER_LOCK_FILE=/tmp/create_ap.$$.lock |
|
|
|
cleanup_lock() { |
|
rm -f $COUNTER_LOCK_FILE |
|
} |
|
|
|
init_lock() { |
|
local LOCK_FILE=/tmp/create_ap.all.lock |
|
|
|
# we initialize only once |
|
[[ $LOCK_FD -ne 0 ]] && return 0 |
|
|
|
LOCK_FD=$(get_avail_fd) |
|
[[ $LOCK_FD -eq 0 ]] && return 1 |
|
|
|
# open/create lock file with write access for all users |
|
# otherwise normal users will not be able to use it. |
|
# to avoid race conditions on creation, we need to |
|
# use umask to set the permissions. |
|
umask 0555 |
|
eval "exec $LOCK_FD>$LOCK_FILE" > /dev/null 2>&1 || return 1 |
|
umask $SCRIPT_UMASK |
|
|
|
# there is a case where lock file was created from a normal |
|
# user. change the owner to root as soon as we can. |
|
[[ $(id -u) -eq 0 ]] && chown 0:0 $LOCK_FILE |
|
|
|
# create mutex counter lock file |
|
echo 0 > $COUNTER_LOCK_FILE |
|
|
|
return $? |
|
} |
|
|
|
# recursive mutex lock for all create_ap processes |
|
mutex_lock() { |
|
local counter_mutex_fd |
|
local counter |
|
|
|
# lock local mutex and read counter |
|
counter_mutex_fd=$(get_avail_fd) |
|
if [[ $counter_mutex_fd -ne 0 ]]; then |
|
eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE" |
|
flock $counter_mutex_fd |
|
read -u $counter_mutex_fd counter |
|
else |
|
echo "Failed to lock mutex counter" >&2 |
|
return 1 |
|
fi |
|
|
|
# lock global mutex and increase counter |
|
[[ $counter -eq 0 ]] && flock $LOCK_FD |
|
counter=$(( $counter + 1 )) |
|
|
|
# write counter and unlock local mutex |
|
echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd |
|
eval "exec ${counter_mutex_fd}<&-" |
|
return 0 |
|
} |
|
|
|
# recursive mutex unlock for all create_ap processes |
|
mutex_unlock() { |
|
local counter_mutex_fd |
|
local counter |
|
|
|
# lock local mutex and read counter |
|
counter_mutex_fd=$(get_avail_fd) |
|
if [[ $counter_mutex_fd -ne 0 ]]; then |
|
eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE" |
|
flock $counter_mutex_fd |
|
read -u $counter_mutex_fd counter |
|
else |
|
echo "Failed to lock mutex counter" >&2 |
|
return 1 |
|
fi |
|
|
|
# decrease counter and unlock global mutex |
|
if [[ $counter -gt 0 ]]; then |
|
counter=$(( $counter - 1 )) |
|
[[ $counter -eq 0 ]] && flock -u $LOCK_FD |
|
fi |
|
|
|
# write counter and unlock local mutex |
|
echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd |
|
eval "exec ${counter_mutex_fd}<&-" |
|
return 0 |
|
} |
|
|
|
# it takes 2 arguments |
|
# returns: |
|
# 0 if v1 (1st argument) and v2 (2nd argument) are the same |
|
# 1 if v1 is less than v2 |
|
# 2 if v1 is greater than v2 |
|
version_cmp() { |
|
local V1 V2 VN x |
|
[[ ! $1 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!" |
|
[[ ! $2 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!" |
|
|
|
V1=( $(echo $1 | tr '.' ' ') ) |
|
V2=( $(echo $2 | tr '.' ' ') ) |
|
VN=${#V1[@]} |
|
[[ $VN -lt ${#V2[@]} ]] && VN=${#V2[@]} |
|
|
|
for ((x = 0; x < $VN; x++)); do |
|
[[ ${V1[x]} -lt ${V2[x]} ]] && return 1 |
|
[[ ${V1[x]} -gt ${V2[x]} ]] && return 2 |
|
done |
|
|
|
return 0 |
|
} |
|
|
|
USE_IWCONFIG=0 |
|
|
|
is_interface() { |
|
[[ -z "$1" ]] && return 1 |
|
[[ -d "/sys/class/net/${1}" ]] |
|
} |
|
|
|
is_wifi_interface() { |
|
which iw > /dev/null 2>&1 && iw dev $1 info > /dev/null 2>&1 && return 0 |
|
if which iwconfig > /dev/null 2>&1 && iwconfig $1 > /dev/null 2>&1; then |
|
USE_IWCONFIG=1 |
|
return 0 |
|
fi |
|
return 1 |
|
} |
|
|
|
is_bridge_interface() { |
|
[[ -z "$1" ]] && return 1 |
|
[[ -d "/sys/class/net/${1}/bridge" ]] |
|
} |
|
|
|
get_phy_device() { |
|
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() { |
|
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_macaddr() { |
|
echo "$1" | grep -E "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$" > /dev/null 2>&1 |
|
} |
|
|
|
is_unicast_macaddr() { |
|
local x |
|
is_macaddr "$1" || return 1 |
|
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" |
|
} |
|
|
|
get_mtu() { |
|
is_interface "$1" || return |
|
cat "/sys/class/net/${1}/mtu" |
|
} |
|
|
|
alloc_new_iface() { |
|
local prefix=$1 |
|
local i=0 |
|
|
|
mutex_lock |
|
while :; do |
|
if ! is_interface $prefix$i && [[ ! -f $COMMON_CONFDIR/ifaces/$prefix$i ]]; then |
|
mkdir -p $COMMON_CONFDIR/ifaces |
|
touch $COMMON_CONFDIR/ifaces/$prefix$i |
|
echo $prefix$i |
|
mutex_unlock |
|
return |
|
fi |
|
i=$((i + 1)) |
|
done |
|
mutex_unlock |
|
} |
|
|
|
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##*:}) |
|
mutex_lock |
|
for i in {1..255}; do |
|
NEWMAC="${OLDMAC%:*}:$(printf %02x $(( ($LAST_BYTE + $i) % 256 )))" |
|
(get_all_macaddrs | grep "$NEWMAC" > /dev/null 2>&1) || break |
|
done |
|
mutex_unlock |
|
echo $NEWMAC |
|
} |
|
|
|
# start haveged when needed |
|
haveged_watchdog() { |
|
local show_warn=1 |
|
while :; do |
|
mutex_lock |
|
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'" |
|
show_warn=0 |
|
fi |
|
elif ! pidof haveged > /dev/null 2>&1; then |
|
echo "Low entropy detected, starting haveged" |
|
# boost low-entropy |
|
haveged -w 1024 -p $COMMON_CONFDIR/haveged.pid |
|
fi |
|
fi |
|
mutex_unlock |
|
sleep 2 |
|
done |
|
} |
|
|
|
NETWORKMANAGER_CONF=/etc/NetworkManager/NetworkManager.conf |
|
NM_OLDER_VERSION=1 |
|
|
|
networkmanager_exists() { |
|
local NM_VER |
|
which nmcli > /dev/null 2>&1 || return 1 |
|
NM_VER=$(nmcli -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+') |
|
version_cmp $NM_VER 0.9.9 |
|
if [[ $? -eq 1 ]]; then |
|
NM_OLDER_VERSION=1 |
|
else |
|
NM_OLDER_VERSION=0 |
|
fi |
|
return 0 |
|
} |
|
|
|
networkmanager_is_running() { |
|
local NMCLI_OUT |
|
networkmanager_exists || return 1 |
|
if [[ $NM_OLDER_VERSION -eq 1 ]]; then |
|
NMCLI_OUT=$(nmcli -t -f RUNNING nm 2>&1 | grep -E '^running$') |
|
else |
|
NMCLI_OUT=$(nmcli -t -f RUNNING g 2>&1 | grep -E '^running$') |
|
fi |
|
[[ -n "$NMCLI_OUT" ]] |
|
} |
|
|
|
networkmanager_iface_is_unmanaged() { |
|
is_interface "$1" || return 2 |
|
(nmcli -t -f DEVICE,STATE d 2>&1 | grep -E "^$1:unmanaged$" > /dev/null 2>&1) || return 1 |
|
} |
|
|
|
ADDED_UNMANAGED= |
|
|
|
networkmanager_add_unmanaged() { |
|
local MAC UNMANAGED WAS_EMPTY x |
|
networkmanager_exists || return 1 |
|
|
|
[[ -d ${NETWORKMANAGER_CONF%/*} ]] || mkdir -p ${NETWORKMANAGER_CONF%/*} |
|
[[ -f ${NETWORKMANAGER_CONF} ]] || touch ${NETWORKMANAGER_CONF} |
|
|
|
if [[ $NM_OLDER_VERSION -eq 1 ]]; then |
|
if [[ -z "$2" ]]; then |
|
MAC=$(get_macaddr "$1") |
|
else |
|
MAC="$2" |
|
fi |
|
[[ -z "$MAC" ]] && return 1 |
|
fi |
|
|
|
mutex_lock |
|
UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf) |
|
|
|
WAS_EMPTY=0 |
|
[[ -z "$UNMANAGED" ]] && WAS_EMPTY=1 |
|
UNMANAGED=$(echo "$UNMANAGED" | sed 's/unmanaged-devices=//' | tr ';,' ' ') |
|
|
|
# if it exists, do nothing |
|
for x in $UNMANAGED; do |
|
if [[ $x == "mac:${MAC}" ]] || |
|
[[ $NM_OLDER_VERSION -eq 0 && $x == "interface-name:${1}" ]]; then |
|
mutex_unlock |
|
return 2 |
|
fi |
|
done |
|
|
|
if [[ $NM_OLDER_VERSION -eq 1 ]]; then |
|
UNMANAGED="${UNMANAGED} mac:${MAC}" |
|
else |
|
UNMANAGED="${UNMANAGED} interface-name:${1}" |
|
fi |
|
|
|
UNMANAGED=$(echo $UNMANAGED | sed -e 's/^ //') |
|
UNMANAGED="${UNMANAGED// /;}" |
|
UNMANAGED="unmanaged-devices=${UNMANAGED}" |
|
|
|
if ! grep -E '^\[keyfile\]' ${NETWORKMANAGER_CONF} > /dev/null 2>&1; then |
|
echo -e "\n\n[keyfile]\n${UNMANAGED}" >> ${NETWORKMANAGER_CONF} |
|
elif [[ $WAS_EMPTY -eq 1 ]]; then |
|
sed -e "s/^\(\[keyfile\].*\)$/\1\n${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} |
|
else |
|
sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} |
|
fi |
|
|
|
ADDED_UNMANAGED="${ADDED_UNMANAGED} ${1} " |
|
mutex_unlock |
|
|
|
local nm_pid=$(pidof NetworkManager) |
|
[[ -n "$nm_pid" ]] && kill -HUP $nm_pid |
|
|
|
return 0 |
|
} |
|
|
|
networkmanager_rm_unmanaged() { |
|
local MAC UNMANAGED |
|
networkmanager_exists || return 1 |
|
[[ ! -f ${NETWORKMANAGER_CONF} ]] && return 1 |
|
|
|
if [[ $NM_OLDER_VERSION -eq 1 ]]; then |
|
if [[ -z "$2" ]]; then |
|
MAC=$(get_macaddr "$1") |
|
else |
|
MAC="$2" |
|
fi |
|
[[ -z "$MAC" ]] && return 1 |
|
fi |
|
|
|
mutex_lock |
|
UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf | sed 's/unmanaged-devices=//' | tr ';,' ' ') |
|
|
|
if [[ -z "$UNMANAGED" ]]; then |
|
mutex_unlock |
|
return 1 |
|
fi |
|
|
|
[[ -n "$MAC" ]] && UNMANAGED=$(echo $UNMANAGED | sed -e "s/mac:${MAC}\( \|$\)//g") |
|
UNMANAGED=$(echo $UNMANAGED | sed -e "s/interface-name:${1}\( \|$\)//g") |
|
UNMANAGED=$(echo $UNMANAGED | sed -e 's/ $//') |
|
|
|
if [[ -z "$UNMANAGED" ]]; then |
|
sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF} |
|
else |
|
UNMANAGED="${UNMANAGED// /;}" |
|
UNMANAGED="unmanaged-devices=${UNMANAGED}" |
|
sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} |
|
fi |
|
|
|
ADDED_UNMANAGED="${ADDED_UNMANAGED/ ${1} /}" |
|
mutex_unlock |
|
|
|
local nm_pid=$(pidof NetworkManager) |
|
[[ -n "$nm_pid" ]] && kill -HUP $nm_pid |
|
|
|
return 0 |
|
} |
|
|
|
networkmanager_fix_unmanaged() { |
|
[[ -f ${NETWORKMANAGER_CONF} ]] || return |
|
|
|
mutex_lock |
|
sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF} |
|
mutex_unlock |
|
|
|
local nm_pid=$(pidof NetworkManager) |
|
[[ -n "$nm_pid" ]] && kill -HUP $nm_pid |
|
} |
|
|
|
networkmanager_rm_unmanaged_if_needed() { |
|
[[ $ADDED_UNMANAGED =~ .*\ ${1}\ .* ]] && networkmanager_rm_unmanaged $1 $2 |
|
} |
|
|
|
networkmanager_wait_until_unmanaged() { |
|
local RES |
|
networkmanager_is_running || return 1 |
|
while :; do |
|
networkmanager_iface_is_unmanaged "$1" |
|
RES=$? |
|
[[ $RES -eq 0 ]] && break |
|
[[ $RES -eq 2 ]] && die "Interface '${1}' does not exist. |
|
It's probably renamed by a udev rule." |
|
sleep 1 |
|
done |
|
sleep 2 |
|
return 0 |
|
} |
|
|
|
|
|
CHANNEL=default |
|
GATEWAY=192.168.12.1 |
|
WPA_VERSION=1+2 |
|
ETC_HOSTS=0 |
|
ADDN_HOSTS= |
|
DHCP_DNS=gateway |
|
NO_DNS=0 |
|
NO_DNSMASQ=0 |
|
DNS_PORT= |
|
HIDDEN=0 |
|
MAC_FILTER=0 |
|
MAC_FILTER_ACCEPT=/etc/hostapd/hostapd.accept |
|
ISOLATE_CLIENTS=0 |
|
SHARE_METHOD=nat |
|
IEEE80211N=0 |
|
IEEE80211AC=0 |
|
HT_CAPAB='[HT40+]' |
|
VHT_CAPAB= |
|
DRIVER=nl80211 |
|
NO_VIRT=0 |
|
COUNTRY= |
|
FREQ_BAND=2.4 |
|
NEW_MACADDR= |
|
DAEMONIZE=0 |
|
NO_HAVEGED=0 |
|
USE_PSK=0 |
|
|
|
HOSTAPD_DEBUG_ARGS= |
|
REDIRECT_TO_LOCALHOST=0 |
|
|
|
CONFIG_OPTS=(CHANNEL GATEWAY WPA_VERSION ETC_HOSTS DHCP_DNS NO_DNS NO_DNSMASQ HIDDEN MAC_FILTER MAC_FILTER_ACCEPT ISOLATE_CLIENTS |
|
SHARE_METHOD IEEE80211N IEEE80211AC HT_CAPAB VHT_CAPAB DRIVER NO_VIRT COUNTRY FREQ_BAND |
|
NEW_MACADDR DAEMONIZE NO_HAVEGED WIFI_IFACE INTERNET_IFACE |
|
SSID PASSPHRASE USE_PSK) |
|
|
|
FIX_UNMANAGED=0 |
|
LIST_RUNNING=0 |
|
STOP_ID= |
|
LIST_CLIENTS_ID= |
|
|
|
STORE_CONFIG= |
|
LOAD_CONFIG= |
|
|
|
CONFDIR= |
|
WIFI_IFACE= |
|
VWIFI_IFACE= |
|
INTERNET_IFACE= |
|
BRIDGE_IFACE= |
|
OLD_MACADDR= |
|
IP_ADDRS= |
|
ROUTE_ADDRS= |
|
|
|
HAVEGED_WATCHDOG_PID= |
|
|
|
_cleanup() { |
|
local PID x |
|
|
|
trap "" SIGINT SIGUSR1 SIGUSR2 EXIT |
|
mutex_lock |
|
disown -a |
|
|
|
# kill haveged_watchdog |
|
[[ -n "$HAVEGED_WATCHDOG_PID" ]] && kill $HAVEGED_WATCHDOG_PID |
|
|
|
# kill processes |
|
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 |
|
[[ -f $x ]] && kill -9 $(cat $x) |
|
done |
|
|
|
rm -rf $CONFDIR |
|
|
|
local found=0 |
|
for x in $(list_running_conf); do |
|
if [[ -f $x/nat_internet_iface && $(cat $x/nat_internet_iface) == $INTERNET_IFACE ]]; then |
|
found=1 |
|
break |
|
fi |
|
done |
|
|
|
if [[ $found -eq 0 ]]; then |
|
cp -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding \ |
|
/proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding |
|
rm -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding |
|
fi |
|
|
|
# if we are the last create_ap instance then set back the common values |
|
if ! has_running_instance; then |
|
# kill common processes |
|
for x in $COMMON_CONFDIR/*.pid; do |
|
[[ -f $x ]] && kill -9 $(cat $x) |
|
done |
|
|
|
# set old ip_forward |
|
if [[ -f $COMMON_CONFDIR/ip_forward ]]; then |
|
cp -f $COMMON_CONFDIR/ip_forward /proc/sys/net/ipv4 |
|
rm -f $COMMON_CONFDIR/ip_forward |
|
fi |
|
|
|
# set old bridge-nf-call-iptables |
|
if [[ -f $COMMON_CONFDIR/bridge-nf-call-iptables ]]; then |
|
if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then |
|
cp -f $COMMON_CONFDIR/bridge-nf-call-iptables /proc/sys/net/bridge |
|
fi |
|
rm -f $COMMON_CONFDIR/bridge-nf-call-iptables |
|
fi |
|
|
|
rm -rf $COMMON_CONFDIR |
|
fi |
|
|
|
if [[ "$SHARE_METHOD" != "none" ]]; then |
|
if [[ "$SHARE_METHOD" == "nat" ]]; then |
|
iptables -w -t nat -D POSTROUTING -s ${GATEWAY%.*}.0/24 ! -o ${WIFI_IFACE} -j MASQUERADE || die |
|
iptables -w -D FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT |
|
iptables -w -D FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT |
|
elif [[ "$SHARE_METHOD" == "bridge" ]]; then |
|
if ! is_bridge_interface $INTERNET_IFACE; then |
|
ip link set dev $BRIDGE_IFACE down |
|
ip link set dev $INTERNET_IFACE down |
|
ip link set dev $INTERNET_IFACE promisc off |
|
ip link set dev $INTERNET_IFACE nomaster |
|
ip link delete $BRIDGE_IFACE type bridge |
|
ip addr flush $INTERNET_IFACE |
|
ip link set dev $INTERNET_IFACE up |
|
dealloc_iface $BRIDGE_IFACE |
|
|
|
for x in "${IP_ADDRS[@]}"; do |
|
x="${x/inet/}" |
|
x="${x/secondary/}" |
|
x="${x/dynamic/}" |
|
x=$(echo $x | sed 's/\([0-9]\)sec/\1/g') |
|
x="${x/${INTERNET_IFACE}/}" |
|
ip addr add $x dev $INTERNET_IFACE |
|
done |
|
|
|
ip route flush dev $INTERNET_IFACE |
|
|
|
for x in "${ROUTE_ADDRS[@]}"; do |
|
[[ -z "$x" ]] && continue |
|
[[ "$x" == default* ]] && continue |
|
ip route add $x dev $INTERNET_IFACE |
|
done |
|
|
|
for x in "${ROUTE_ADDRS[@]}"; do |
|
[[ -z "$x" ]] && continue |
|
[[ "$x" != default* ]] && continue |
|
ip route add $x dev $INTERNET_IFACE |
|
done |
|
|
|
networkmanager_rm_unmanaged_if_needed $INTERNET_IFACE |
|
fi |
|
fi |
|
fi |
|
|
|
if [[ "$SHARE_METHOD" != "bridge" ]]; then |
|
if [[ $NO_DNS -eq 0 ]]; then |
|
iptables -w -D INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT |
|
iptables -w -D INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT |
|
iptables -w -t nat -D PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ |
|
-p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT |
|
iptables -w -t nat -D PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ |
|
-p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT |
|
fi |
|
iptables -w -D INPUT -p udp -m udp --dport 67 -j ACCEPT |
|
fi |
|
|
|
if [[ $NO_VIRT -eq 0 ]]; then |
|
if [[ -n "$VWIFI_IFACE" ]]; then |
|
ip link set down dev ${VWIFI_IFACE} |
|
ip addr flush ${VWIFI_IFACE} |
|
networkmanager_rm_unmanaged_if_needed ${VWIFI_IFACE} ${OLD_MACADDR} |
|
iw dev ${VWIFI_IFACE} del |
|
dealloc_iface $VWIFI_IFACE |
|
fi |
|
else |
|
ip link set down dev ${WIFI_IFACE} |
|
ip addr flush ${WIFI_IFACE} |
|
if [[ -n "$NEW_MACADDR" ]]; then |
|
ip link set dev ${WIFI_IFACE} address ${OLD_MACADDR} |
|
fi |
|
networkmanager_rm_unmanaged_if_needed ${WIFI_IFACE} ${OLD_MACADDR} |
|
fi |
|
|
|
mutex_unlock |
|
cleanup_lock |
|
} |
|
|
|
cleanup() { |
|
echo |
|
echo -n "Doing cleanup.. " |
|
_cleanup > /dev/null 2>&1 |
|
echo "done" |
|
} |
|
|
|
die() { |
|
[[ -n "$1" ]] && echo -e "\nERROR: $1\n" >&2 |
|
# send die signal to the main process |
|
[[ $BASHPID -ne $$ ]] && kill -USR2 $$ |
|
# we don't need to call cleanup because it's traped on EXIT |
|
exit 1 |
|
} |
|
|
|
clean_exit() { |
|
# send clean_exit signal to the main process |
|
[[ $BASHPID -ne $$ ]] && kill -USR1 $$ |
|
# we don't need to call cleanup because it's traped on EXIT |
|
exit 0 |
|
} |
|
|
|
list_running_conf() { |
|
local x |
|
mutex_lock |
|
for x in /tmp/create_ap.*; do |
|
if [[ -f $x/pid && -f $x/wifi_iface && -d /proc/$(cat $x/pid) ]]; then |
|
echo $x |
|
fi |
|
done |
|
mutex_unlock |
|
} |
|
|
|
list_running() { |
|
local IFACE wifi_iface x |
|
mutex_lock |
|
for x in $(list_running_conf); do |
|
IFACE=${x#*.} |
|
IFACE=${IFACE%%.*} |
|
wifi_iface=$(cat $x/wifi_iface) |
|
|
|
if [[ $IFACE == $wifi_iface ]]; then |
|
echo $(cat $x/pid) $IFACE |
|
else |
|
echo $(cat $x/pid) $IFACE '('$(cat $x/wifi_iface)')' |
|
fi |
|
done |
|
mutex_unlock |
|
} |
|
|
|
get_wifi_iface_from_pid() { |
|
list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E "^${1} " | cut -d' ' -f2 |
|
} |
|
|
|
get_pid_from_wifi_iface() { |
|
list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E " ${1}$" | cut -d' ' -f1 |
|
} |
|
|
|
get_confdir_from_pid() { |
|
local IFACE x |
|
mutex_lock |
|
for x in $(list_running_conf); do |
|
if [[ $(cat $x/pid) == "$1" ]]; then |
|
echo $x |
|
break |
|
fi |
|
done |
|
mutex_unlock |
|
} |
|
|
|
print_client() { |
|
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" |
|
} |
|
|
|
list_clients() { |
|
local wifi_iface pid |
|
|
|
# If PID is given, get the associated wifi iface |
|
if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then |
|
pid="$1" |
|
wifi_iface=$(get_wifi_iface_from_pid "$pid") |
|
[[ -z "$wifi_iface" ]] && die "'$pid' is not the pid of a running $PROGNAME instance." |
|
fi |
|
|
|
[[ -z "$wifi_iface" ]] && wifi_iface="$1" |
|
is_wifi_interface "$wifi_iface" || die "'$wifi_iface' is not a WiFi interface." |
|
|
|
[[ -z "$pid" ]] && pid=$(get_pid_from_wifi_iface "$wifi_iface") |
|
[[ -z "$pid" ]] && die "'$wifi_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 [[ $USE_IWCONFIG -eq 0 ]]; then |
|
local awk_cmd='($1 ~ /Station$/) {print $2}' |
|
local client_list=$(iw dev "$wifi_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 $mac |
|
done |
|
else |
|
die "This option is not supported for the current driver." |
|
fi |
|
} |
|
|
|
has_running_instance() { |
|
local PID x |
|
|
|
mutex_lock |
|
for x in /tmp/create_ap.*; do |
|
if [[ -f $x/pid ]]; then |
|
PID=$(cat $x/pid) |
|
if [[ -d /proc/$PID ]]; then |
|
mutex_unlock |
|
return 0 |
|
fi |
|
fi |
|
done |
|
mutex_lock |
|
|
|
return 1 |
|
} |
|
|
|
is_running_pid() { |
|
list_running | grep -E "^${1} " > /dev/null 2>&1 |
|
} |
|
|
|
send_stop() { |
|
local x |
|
|
|
mutex_lock |
|
# send stop signal to specific pid |
|
if is_running_pid $1; then |
|
kill -USR1 $1 |
|
mutex_unlock |
|
return |
|
fi |
|
|
|
# send stop signal to specific interface |
|
for x in $(list_running | grep -E " \(?${1}( |\)?\$)" | cut -f1 -d' '); do |
|
kill -USR1 $x |
|
done |
|
mutex_unlock |
|
} |
|
|
|
# Storing configs |
|
write_config() { |
|
local i=1 |
|
|
|
if ! eval 'echo -n > "$STORE_CONFIG"' > /dev/null 2>&1; then |
|
echo "ERROR: Unable to create config file $STORE_CONFIG" >&2 |
|
exit 1 |
|
fi |
|
|
|
WIFI_IFACE=$1 |
|
if [[ "$SHARE_METHOD" == "none" ]]; then |
|
SSID="$2" |
|
PASSPHRASE="$3" |
|
else |
|
INTERNET_IFACE="$2" |
|
SSID="$3" |
|
PASSPHRASE="$4" |
|
fi |
|
|
|
for config_opt in "${CONFIG_OPTS[@]}"; do |
|
eval echo $config_opt=\$$config_opt |
|
done >> "$STORE_CONFIG" |
|
|
|
echo -e "Config options written to '$STORE_CONFIG'" |
|
exit 0 |
|
} |
|
|
|
is_config_opt() { |
|
local elem opt="$1" |
|
|
|
for elem in "${CONFIG_OPTS[@]}"; do |
|
if [[ "$elem" == "$opt" ]]; then |
|
return 0 |
|
fi |
|
done |
|
return 1 |
|
} |
|
|
|
# Load options from config file |
|
read_config() { |
|
local opt_name opt_val line |
|
|
|
while read line; do |
|
# Read switches and their values |
|
opt_name="${line%%=*}" |
|
opt_val="${line#*=}" |
|
if is_config_opt "$opt_name" ; then |
|
eval $opt_name="\$opt_val" |
|
else |
|
echo "WARN: Unrecognized configuration entry $opt_name" >&2 |
|
fi |
|
done < "$LOAD_CONFIG" |
|
} |
|
|
|
|
|
ARGS=( "$@" ) |
|
|
|
# Preprocessing for --config before option-parsing starts |
|
for ((i=0; i<$#; i++)); do |
|
if [[ "${ARGS[i]}" = "--config" ]]; then |
|
if [[ -f "${ARGS[i+1]}" ]]; then |
|
LOAD_CONFIG="${ARGS[i+1]}" |
|
read_config |
|
else |
|
echo "ERROR: No config file found at given location" >&2 |
|
exit 1 |
|
fi |
|
break |
|
fi |
|
done |
|
|
|
GETOPT_ARGS=$(getopt -o hc:w:g:de:nm: -l "help","hidden","hostapd-debug:","redirect-to-localhost","mac-filter","mac-filter-accept:","isolate-clients","ieee80211n","ieee80211ac","ht_capab:","vht_capab:","driver:","no-virt","fix-unmanaged","country:","freq-band:","mac:","dhcp-dns:","daemon","stop:","list","list-running","list-clients:","version","psk","no-haveged","no-dns","no-dnsmasq","mkconfig:","config:" -n "$PROGNAME" -- "$@") |
|
[[ $? -ne 0 ]] && exit 1 |
|
eval set -- "$GETOPT_ARGS" |
|
|
|
while :; do |
|
case "$1" in |
|
-h|--help) |
|
usage |
|
exit 0 |
|
;; |
|
--version) |
|
echo $VERSION |
|
exit 0 |
|
;; |
|
--hidden) |
|
shift |
|
HIDDEN=1 |
|
;; |
|
--mac-filter) |
|
shift |
|
MAC_FILTER=1 |
|
;; |
|
--mac-filter-accept) |
|
shift |
|
MAC_FILTER_ACCEPT="$1" |
|
shift |
|
;; |
|
--isolate-clients) |
|
shift |
|
ISOLATE_CLIENTS=1 |
|
;; |
|
-c) |
|
shift |
|
CHANNEL="$1" |
|
shift |
|
;; |
|
-w) |
|
shift |
|
WPA_VERSION="$1" |
|
[[ "$WPA_VERSION" == "2+1" ]] && WPA_VERSION=1+2 |
|
shift |
|
;; |
|
-g) |
|
shift |
|
GATEWAY="$1" |
|
shift |
|
;; |
|
-d) |
|
shift |
|
ETC_HOSTS=1 |
|
;; |
|
-e) |
|
shift |
|
ADDN_HOSTS="$1" |
|
shift |
|
;; |
|
-n) |
|
shift |
|
SHARE_METHOD=none |
|
;; |
|
-m) |
|
shift |
|
SHARE_METHOD="$1" |
|
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 |
|
;; |
|
--fix-unmanaged) |
|
shift |
|
FIX_UNMANAGED=1 |
|
;; |
|
--country) |
|
shift |
|
COUNTRY="$1" |
|
shift |
|
;; |
|
--freq-band) |
|
shift |
|
FREQ_BAND="$1" |
|
shift |
|
;; |
|
--mac) |
|
shift |
|
NEW_MACADDR="$1" |
|
shift |
|
;; |
|
--dhcp-dns) |
|
shift |
|
DHCP_DNS="$1" |
|
shift |
|
;; |
|
--daemon) |
|
shift |
|
DAEMONIZE=1 |
|
;; |
|
--stop) |
|
shift |
|
STOP_ID="$1" |
|
shift |
|
;; |
|
--list) |
|
shift |
|
LIST_RUNNING=1 |
|
echo -e "WARN: --list is deprecated, use --list-running instead.\n" >&2 |
|
;; |
|
--list-running) |
|
shift |
|
LIST_RUNNING=1 |
|
;; |
|
--list-clients) |
|
shift |
|
LIST_CLIENTS_ID="$1" |
|
shift |
|
;; |
|
--no-haveged) |
|
shift |
|
NO_HAVEGED=1 |
|
;; |
|
--psk) |
|
shift |
|
USE_PSK=1 |
|
;; |
|
--no-dns) |
|
shift |
|
NO_DNS=1 |
|
;; |
|
--no-dnsmasq) |
|
shift |
|
NO_DNSMASQ=1 |
|
;; |
|
--redirect-to-localhost) |
|
shift |
|
REDIRECT_TO_LOCALHOST=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 |
|
;; |
|
--mkconfig) |
|
shift |
|
STORE_CONFIG="$1" |
|
shift |
|
;; |
|
--config) |
|
shift |
|
shift |
|
;; |
|
--) |
|
shift |
|
break |
|
;; |
|
esac |
|
done |
|
|
|
# Load positional args from config file, if needed |
|
if [[ -n "$LOAD_CONFIG" && $# -eq 0 ]]; then |
|
i=0 |
|
# set arguments in order |
|
for x in WIFI_IFACE INTERNET_IFACE SSID PASSPHRASE; do |
|
if eval "[[ -n \"\$${x}\" ]]"; then |
|
eval "set -- \"\${@:1:$i}\" \"\$${x}\"" |
|
((i++)) |
|
fi |
|
# we unset the variable to avoid any problems later |
|
eval "unset $x" |
|
done |
|
fi |
|
|
|
# Check if required number of positional args are present |
|
if [[ $# -lt 1 && $FIX_UNMANAGED -eq 0 && -z "$STOP_ID" && |
|
$LIST_RUNNING -eq 0 && -z "$LIST_CLIENTS_ID" ]]; then |
|
usage >&2 |
|
exit 1 |
|
fi |
|
|
|
# Set NO_DNS, if dnsmasq is disabled |
|
if [[ $NO_DNSMASQ -eq 1 ]]; then |
|
NO_DNS=1 |
|
fi |
|
|
|
trap "cleanup_lock" EXIT |
|
|
|
if ! init_lock; then |
|
echo "ERROR: Failed to initialize lock" >&2 |
|
exit 1 |
|
fi |
|
|
|
# if the user press ctrl+c or we get USR1 signal |
|
# then run clean_exit() |
|
trap "clean_exit" SIGINT SIGUSR1 |
|
# if we get USR2 signal then run die(). |
|
trap "die" SIGUSR2 |
|
|
|
[[ -n "$STORE_CONFIG" ]] && write_config "$@" |
|
|
|
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 |
|
|
|
if [[ $FIX_UNMANAGED -eq 1 ]]; then |
|
echo "Trying to fix unmanaged status in NetworkManager..." |
|
networkmanager_fix_unmanaged |
|
exit 0 |
|
fi |
|
|
|
if [[ $DAEMONIZE -eq 1 && $RUNNING_AS_DAEMON -eq 0 ]]; then |
|
echo "Running as Daemon..." |
|
# run a detached create_ap |
|
RUNNING_AS_DAEMON=1 setsid "$0" "${ARGS[@]}" & |
|
exit 0 |
|
fi |
|
|
|
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 |
|
|
|
WIFI_IFACE=$1 |
|
|
|
if ! is_wifi_interface ${WIFI_IFACE}; then |
|
echo "ERROR: '${WIFI_IFACE}' is not a WiFi interface" >&2 |
|
exit 1 |
|
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 [[ ! -x "$HOSTAPD" ]]; then |
|
echo "ERROR: hostapd not found." >&2 |
|
exit 1 |
|
fi |
|
|
|
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 [[ "$SHARE_METHOD" != "nat" && "$SHARE_METHOD" != "bridge" && "$SHARE_METHOD" != "none" ]]; then |
|
echo "ERROR: Wrong Internet sharing method" >&2 |
|
echo |
|
usage >&2 |
|
exit 1 |
|
fi |
|
|
|
if [[ -n "$NEW_MACADDR" ]]; then |
|
if ! is_macaddr "$NEW_MACADDR"; then |
|
echo "ERROR: '${NEW_MACADDR}' is not a valid MAC address" >&2 |
|
exit 1 |
|
fi |
|
|
|
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. Because of this, you may encounter some problems" >&2 |
|
fi |
|
fi |
|
|
|
if [[ "$SHARE_METHOD" != "none" ]]; then |
|
MIN_REQUIRED_ARGS=2 |
|
else |
|
MIN_REQUIRED_ARGS=1 |
|
fi |
|
|
|
if [[ $# -gt $MIN_REQUIRED_ARGS ]]; then |
|
if [[ "$SHARE_METHOD" != "none" ]]; then |
|
if [[ $# -ne 3 && $# -ne 4 ]]; then |
|
usage >&2 |
|
exit 1 |
|
fi |
|
INTERNET_IFACE="$2" |
|
SSID="$3" |
|
PASSPHRASE="$4" |
|
else |
|
if [[ $# -ne 2 && $# -ne 3 ]]; then |
|
usage >&2 |
|
exit 1 |
|
fi |
|
SSID="$2" |
|
PASSPHRASE="$3" |
|
fi |
|
else |
|
if [[ "$SHARE_METHOD" != "none" ]]; then |
|
if [[ $# -ne 2 ]]; then |
|
usage >&2 |
|
exit 1 |
|
fi |
|
INTERNET_IFACE="$2" |
|
fi |
|
if tty -s; then |
|
while :; do |
|
read -p "SSID: " SSID |
|
if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then |
|
echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2 |
|
continue |
|
fi |
|
break |
|
done |
|
while :; do |
|
if [[ $USE_PSK -eq 0 ]]; then |
|
read -p "Passphrase: " -s PASSPHRASE |
|
echo |
|
if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then |
|
echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2 |
|
continue |
|
fi |
|
read -p "Retype passphrase: " -s PASSPHRASE2 |
|
echo |
|
if [[ "$PASSPHRASE" != "$PASSPHRASE2" ]]; then |
|
echo "Passphrases do not match." |
|
else |
|
break |
|
fi |
|
else |
|
read -p "PSK: " PASSPHRASE |
|
echo |
|
if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then |
|
echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2 |
|
continue |
|
fi |
|
fi |
|
done |
|
else |
|
read SSID |
|
read PASSPHRASE |
|
fi |
|
fi |
|
|
|
if [[ "$SHARE_METHOD" != "none" ]] && ! is_interface $INTERNET_IFACE; then |
|
echo "ERROR: '${INTERNET_IFACE}' is not an interface" >&2 |
|
exit 1 |
|
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 [[ -n "$PASSPHRASE" ]]; then |
|
echo "WARN: Realtek drivers usually have problems with WPA1, enabling -w 2" >&2 |
|
WPA_VERSION=2 |
|
fi |
|
echo "WARN: If AP doesn't work, please read: howto/realtek.md" >&2 |
|
fi |
|
|
|
if [[ $NO_VIRT -eq 1 && "$WIFI_IFACE" == "$INTERNET_IFACE" ]]; then |
|
echo -n "ERROR: You can not share your connection from the same" >&2 |
|
echo " interface if you are using --no-virt option." >&2 |
|
exit 1 |
|
fi |
|
|
|
mutex_lock |
|
trap "cleanup" EXIT |
|
CONFDIR=$(mktemp -d /tmp/create_ap.${WIFI_IFACE}.conf.XXXXXXXX) |
|
echo "Config dir: $CONFDIR" |
|
echo "PID: $$" |
|
echo $$ > $CONFDIR/pid |
|
|
|
# to make --list-running work from any user, we must give read |
|
# permissions to $CONFDIR and $CONFDIR/pid |
|
chmod 755 $CONFDIR |
|
chmod 444 $CONFDIR/pid |
|
|
|
COMMON_CONFDIR=/tmp/create_ap.common.conf |
|
mkdir -p $COMMON_CONFDIR |
|
|
|
if [[ "$SHARE_METHOD" == "nat" ]]; then |
|
echo $INTERNET_IFACE > $CONFDIR/nat_internet_iface |
|
cp -n /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding \ |
|
$COMMON_CONFDIR/${INTERNET_IFACE}_forwarding |
|
fi |
|
cp -n /proc/sys/net/ipv4/ip_forward $COMMON_CONFDIR |
|
if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then |
|
cp -n /proc/sys/net/bridge/bridge-nf-call-iptables $COMMON_CONFDIR |
|
fi |
|
mutex_unlock |
|
|
|
if [[ "$SHARE_METHOD" == "bridge" ]]; then |
|
if is_bridge_interface $INTERNET_IFACE; then |
|
BRIDGE_IFACE=$INTERNET_IFACE |
|
else |
|
BRIDGE_IFACE=$(alloc_new_iface br) |
|
fi |
|
fi |
|
|
|
if [[ $USE_IWCONFIG -eq 0 ]]; then |
|
iw dev ${WIFI_IFACE} set power_save off |
|
fi |
|
|
|
if [[ $NO_VIRT -eq 0 ]]; then |
|
VWIFI_IFACE=$(alloc_new_iface ap) |
|
|
|
# in NetworkManager 0.9.9 and above we can set the interface as unmanaged without |
|
# the need of MAC address, so we set it before we create the virtual interface. |
|
if networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]]; then |
|
echo -n "Network Manager found, set ${VWIFI_IFACE} as unmanaged device... " |
|
networkmanager_add_unmanaged ${VWIFI_IFACE} |
|
# do not call networkmanager_wait_until_unmanaged because interface does not |
|
# exist yet |
|
echo "DONE" |
|
fi |
|
|
|
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 -n "${WIFI_IFACE} is already associated with 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 ", fallback to channel ${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 -n "Creating a virtual WiFi interface... " |
|
|
|
if iw dev ${WIFI_IFACE} interface add ${VWIFI_IFACE} type __ap; then |
|
# now we can call networkmanager_wait_until_unmanaged |
|
networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]] && networkmanager_wait_until_unmanaged ${VWIFI_IFACE} |
|
echo "${VWIFI_IFACE} created." |
|
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 |
|
WIFI_IFACE=${VWIFI_IFACE} |
|
else |
|
OLD_MACADDR=$(get_macaddr ${WIFI_IFACE}) |
|
fi |
|
|
|
mutex_lock |
|
echo $WIFI_IFACE > $CONFDIR/wifi_iface |
|
chmod 444 $CONFDIR/wifi_iface |
|
mutex_unlock |
|
|
|
if [[ -n "$COUNTRY" && $USE_IWCONFIG -eq 0 ]]; then |
|
iw reg set "$COUNTRY" |
|
fi |
|
|
|
can_transmit_to_channel ${WIFI_IFACE} ${CHANNEL} || die "Your adapter can not transmit to channel ${CHANNEL}, frequency band ${FREQ_BAND}GHz." |
|
|
|
if networkmanager_exists && ! networkmanager_iface_is_unmanaged ${WIFI_IFACE}; then |
|
echo -n "Network Manager found, set ${WIFI_IFACE} as unmanaged device... " |
|
networkmanager_add_unmanaged ${WIFI_IFACE} |
|
|
|
if networkmanager_is_running; then |
|
networkmanager_wait_until_unmanaged ${WIFI_IFACE} |
|
fi |
|
|
|
echo "DONE" |
|
fi |
|
|
|
[[ $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!" |
|
|
|
# hostapd config |
|
cat << EOF > $CONFDIR/hostapd.conf |
|
beacon_int=100 |
|
ssid=${SSID} |
|
interface=${WIFI_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=TKIP CCMP |
|
rsn_pairwise=CCMP |
|
EOF |
|
fi |
|
|
|
if [[ "$SHARE_METHOD" == "bridge" ]]; then |
|
echo "bridge=${BRIDGE_IFACE}" >> $CONFDIR/hostapd.conf |
|
elif [[ $NO_DNSMASQ -eq 0 ]]; then |
|
# dnsmasq config (dhcp + dns) |
|
DNSMASQ_VER=$(dnsmasq -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+') |
|
version_cmp $DNSMASQ_VER 2.63 |
|
if [[ $? -eq 1 ]]; then |
|
DNSMASQ_BIND=bind-interfaces |
|
else |
|
DNSMASQ_BIND=bind-dynamic |
|
fi |
|
if [[ "$DHCP_DNS" == "gateway" ]]; then |
|
DHCP_DNS="$GATEWAY" |
|
fi |
|
cat << EOF > $CONFDIR/dnsmasq.conf |
|
listen-address=${GATEWAY} |
|
${DNSMASQ_BIND} |
|
dhcp-range=${GATEWAY%.*}.1,${GATEWAY%.*}.254,255.255.255.0,24h |
|
dhcp-option-force=option:router,${GATEWAY} |
|
dhcp-option-force=option:dns-server,${DHCP_DNS} |
|
EOF |
|
MTU=$(get_mtu $INTERNET_IFACE) |
|
[[ -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 [[ "$SHARE_METHOD" == "none" && "$REDIRECT_TO_LOCALHOST" == "1" ]]; then |
|
cat << EOF >> $CONFDIR/dnsmasq.conf |
|
address=/#/$GATEWAY |
|
EOF |
|
fi |
|
fi |
|
|
|
# initialize WiFi interface |
|
if [[ $NO_VIRT -eq 0 && -n "$NEW_MACADDR" ]]; then |
|
ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die "$VIRTDIEMSG" |
|
fi |
|
|
|
ip link set down dev ${WIFI_IFACE} || die "$VIRTDIEMSG" |
|
ip addr flush ${WIFI_IFACE} || die "$VIRTDIEMSG" |
|
|
|
if [[ $NO_VIRT -eq 1 && -n "$NEW_MACADDR" ]]; then |
|
ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die |
|
fi |
|
|
|
if [[ "$SHARE_METHOD" != "bridge" ]]; then |
|
ip link set up dev ${WIFI_IFACE} || die "$VIRTDIEMSG" |
|
ip addr add ${GATEWAY}/24 broadcast ${GATEWAY%.*}.255 dev ${WIFI_IFACE} || die "$VIRTDIEMSG" |
|
fi |
|
|
|
# enable Internet sharing |
|
if [[ "$SHARE_METHOD" != "none" ]]; then |
|
echo "Sharing Internet using method: $SHARE_METHOD" |
|
if [[ "$SHARE_METHOD" == "nat" ]]; then |
|
iptables -w -t nat -I POSTROUTING -s ${GATEWAY%.*}.0/24 ! -o ${WIFI_IFACE} -j MASQUERADE || die |
|
iptables -w -I FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT || die |
|
iptables -w -I FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT || die |
|
echo 1 > /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding || die |
|
echo 1 > /proc/sys/net/ipv4/ip_forward || die |
|
# 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" == "bridge" ]]; then |
|
# disable iptables rules for bridged interfaces |
|
if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then |
|
echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables |
|
fi |
|
|
|
# to initialize the bridge interface correctly we need to do the following: |
|
# |
|
# 1) save the IPs and route table of INTERNET_IFACE |
|
# 2) if NetworkManager is running set INTERNET_IFACE as unmanaged |
|
# 3) create BRIDGE_IFACE and attach INTERNET_IFACE to it |
|
# 4) set the previously saved IPs and route table to BRIDGE_IFACE |
|
# |
|
# we need the above because BRIDGE_IFACE is the master interface from now on |
|
# and it must know where is connected, otherwise connection is lost. |
|
if ! is_bridge_interface $INTERNET_IFACE; then |
|
echo -n "Create a bridge interface... " |
|
OLD_IFS="$IFS" |
|
IFS=$'\n' |
|
|
|
IP_ADDRS=( $(ip addr show $INTERNET_IFACE | grep -A 1 -E 'inet[[:blank:]]' | paste - -) ) |
|
ROUTE_ADDRS=( $(ip route show dev $INTERNET_IFACE) ) |
|
|
|
IFS="$OLD_IFS" |
|
|
|
if networkmanager_is_running; then |
|
networkmanager_add_unmanaged $INTERNET_IFACE |
|
networkmanager_wait_until_unmanaged $INTERNET_IFACE |
|
fi |
|
|
|
# create bridge interface |
|
ip link add name $BRIDGE_IFACE type bridge || die |
|
ip link set dev $BRIDGE_IFACE up || die |
|
# set 0ms forward delay |
|
echo 0 > /sys/class/net/$BRIDGE_IFACE/bridge/forward_delay |
|
|
|
# attach internet interface to bridge interface |
|
ip link set dev $INTERNET_IFACE promisc on || die |
|
ip link set dev $INTERNET_IFACE up || die |
|
ip link set dev $INTERNET_IFACE master $BRIDGE_IFACE || die |
|
|
|
ip addr flush $INTERNET_IFACE |
|
for x in "${IP_ADDRS[@]}"; do |
|
x="${x/inet/}" |
|
x="${x/secondary/}" |
|
x="${x/dynamic/}" |
|
x=$(echo $x | sed 's/\([0-9]\)sec/\1/g') |
|
x="${x/${INTERNET_IFACE}/}" |
|
ip addr add $x dev $BRIDGE_IFACE || die |
|
done |
|
|
|
# remove any existing entries that were added from 'ip addr add' |
|
ip route flush dev $INTERNET_IFACE |
|
ip route flush dev $BRIDGE_IFACE |
|
|
|
# we must first add the entries that specify the subnets and then the |
|
# gateway entry, otherwise 'ip addr add' will return an error |
|
for x in "${ROUTE_ADDRS[@]}"; do |
|
[[ "$x" == default* ]] && continue |
|
ip route add $x dev $BRIDGE_IFACE || die |
|
done |
|
|
|
for x in "${ROUTE_ADDRS[@]}"; do |
|
[[ "$x" != default* ]] && continue |
|
ip route add $x dev $BRIDGE_IFACE || die |
|
done |
|
|
|
echo "$BRIDGE_IFACE created." |
|
fi |
|
fi |
|
else |
|
echo "No Internet sharing" |
|
fi |
|
|
|
# start dhcp + dns (optional) |
|
if [[ "$SHARE_METHOD" != "bridge" ]]; then |
|
if [[ $NO_DNS -eq 0 ]]; then |
|
DNS_PORT=5353 |
|
iptables -w -I INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT || die |
|
iptables -w -I INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT || die |
|
iptables -w -t nat -I PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ |
|
-p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die |
|
iptables -w -t nat -I PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ |
|
-p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die |
|
else |
|
DNS_PORT=0 |
|
fi |
|
|
|
if [[ $NO_DNSMASQ -eq 0 ]]; then |
|
iptables -w -I INPUT -p udp -m udp --dport 67 -j ACCEPT || die |
|
|
|
if which complain > /dev/null 2>&1; then |
|
# openSUSE's apparmor does not allow dnsmasq to read files. |
|
# remove restriction. |
|
complain dnsmasq |
|
fi |
|
|
|
umask 0033 |
|
dnsmasq -C $CONFDIR/dnsmasq.conf -x $CONFDIR/dnsmasq.pid -l $CONFDIR/dnsmasq.leases -p $DNS_PORT || die |
|
umask $SCRIPT_UMASK |
|
fi |
|
fi |
|
|
|
# start access point |
|
echo "hostapd command-line interface: hostapd_cli -p $CONFDIR/hostapd_ctrl" |
|
|
|
if [[ $NO_HAVEGED -eq 0 ]]; then |
|
haveged_watchdog & |
|
HAVEGED_WATCHDOG_PID=$! |
|
fi |
|
|
|
# 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 |
|
$STDBUF_PATH $HOSTAPD $HOSTAPD_DEBUG_ARGS $CONFDIR/hostapd.conf & |
|
HOSTAPD_PID=$! |
|
echo $HOSTAPD_PID > $CONFDIR/hostapd.pid |
|
|
|
if ! wait $HOSTAPD_PID; then |
|
echo -e "\nError: Failed to run hostapd, maybe a program is interfering." >&2 |
|
if networkmanager_is_running; then |
|
echo "If an error like 'n80211: Could not configure driver mode' was thrown" >&2 |
|
echo "try running the following before starting create_ap:" >&2 |
|
if [[ $NM_OLDER_VERSION -eq 1 ]]; then |
|
echo " nmcli nm wifi off" >&2 |
|
else |
|
echo " nmcli r wifi off" >&2 |
|
fi |
|
echo " rfkill unblock wlan" >&2 |
|
fi |
|
die |
|
fi |
|
|
|
clean_exit |
|
|
|
# Local Variables: |
|
# tab-width: 4 |
|
# indent-tabs-mode: nil |
|
# End: |
|
|
|
# vim: et sts=4 sw=4 |