├── LICENSE ├── Makefile ├── README.md ├── bash_completion ├── create_ap ├── create_ap.conf ├── create_ap.openrc ├── create_ap.service └── howto └── realtek.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, oblique 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX=/usr 2 | MANDIR=$(PREFIX)/share/man 3 | BINDIR=$(PREFIX)/bin 4 | 5 | all: 6 | @echo "Run 'make install' for installation." 7 | @echo "Run 'make uninstall' for uninstallation." 8 | 9 | install: 10 | install -Dm755 create_ap $(DESTDIR)$(BINDIR)/create_ap 11 | install -Dm644 create_ap.conf $(DESTDIR)/etc/create_ap.conf 12 | [ ! -d /lib/systemd/system ] || install -Dm644 create_ap.service $(DESTDIR)$(PREFIX)/lib/systemd/system/create_ap.service 13 | [ ! -e /sbin/openrc-run ] || install -Dm755 create_ap.openrc $(DESTDIR)/etc/init.d/create_ap 14 | install -Dm644 bash_completion $(DESTDIR)$(PREFIX)/share/bash-completion/completions/create_ap 15 | install -Dm644 README.md $(DESTDIR)$(PREFIX)/share/doc/create_ap/README.md 16 | 17 | uninstall: 18 | rm -f $(DESTDIR)$(BINDIR)/create_ap 19 | rm -f $(DESTDIR)/etc/create_ap.conf 20 | [ ! -f /lib/systemd/system/create_ap.service ] || rm -f $(DESTDIR)$(PREFIX)/lib/systemd/system/create_ap.service 21 | [ ! -e /sbin/openrc-run ] || rm -f $(DESTDIR)/etc/init.d/create_ap 22 | rm -f $(DESTDIR)$(PREFIX)/share/bash-completion/completions/create_ap 23 | rm -f $(DESTDIR)$(PREFIX)/share/doc/create_ap/README.md 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NOT MAINTAINED 2 | 3 | This project is no longer maintained. 4 | 5 | 6 | ## Forks and continuation of this project 7 | 8 | * [linux-wifi-hotspot] - Fork that is focused on providing GUI and improvements. 9 | * [linux-router] - Fork that is focused on providing new features and 10 | improvements which are not limited to WiFi. Some interesting features are: 11 | sharing Internet to a wired interface and sharing Internet via a transparent 12 | proxy using redsocks. 13 | 14 | 15 | ## Features 16 | 17 | * Create an AP (Access Point) at any channel. 18 | * Choose one of the following encryptions: WPA, WPA2, WPA/WPA2, Open (no encryption). 19 | * Hide your SSID. 20 | * Disable communication between clients (client isolation). 21 | * IEEE 802.11n & 802.11ac support 22 | * Internet sharing methods: NATed or Bridged or None (no Internet sharing). 23 | * Choose the AP Gateway IP (only for 'NATed' and 'None' Internet sharing methods). 24 | * You can create an AP with the same interface you are getting your Internet connection. 25 | * You can pass your SSID and password through pipe or through arguments (see examples). 26 | 27 | 28 | ## Dependencies 29 | 30 | ### General 31 | 32 | * bash (to run this script) 33 | * util-linux (for getopt) 34 | * procps or procps-ng 35 | * hostapd 36 | * iproute2 37 | * iw 38 | * iwconfig (you only need this if 'iw' can not recognize your adapter) 39 | * haveged (optional) 40 | 41 | ### For 'NATed' or 'None' Internet sharing method 42 | 43 | * dnsmasq 44 | * iptables 45 | 46 | 47 | ## Installation 48 | 49 | ### Generic 50 | git clone https://github.com/oblique/create_ap 51 | cd create_ap 52 | make install 53 | 54 | ### ArchLinux 55 | pacman -S create_ap 56 | 57 | ### Gentoo 58 | emerge layman 59 | layman -f -a jorgicio 60 | emerge net-wireless/create_ap 61 | 62 | ## Examples 63 | ### No passphrase (open network): 64 | create_ap wlan0 eth0 MyAccessPoint 65 | 66 | ### WPA + WPA2 passphrase: 67 | create_ap wlan0 eth0 MyAccessPoint MyPassPhrase 68 | 69 | ### AP without Internet sharing: 70 | create_ap -n wlan0 MyAccessPoint MyPassPhrase 71 | 72 | ### Bridged Internet sharing: 73 | create_ap -m bridge wlan0 eth0 MyAccessPoint MyPassPhrase 74 | 75 | ### Bridged Internet sharing (pre-configured bridge interface): 76 | create_ap -m bridge wlan0 br0 MyAccessPoint MyPassPhrase 77 | 78 | ### Internet sharing from the same WiFi interface: 79 | create_ap wlan0 wlan0 MyAccessPoint MyPassPhrase 80 | 81 | ### Choose a different WiFi adapter driver 82 | create_ap --driver rtl871xdrv wlan0 eth0 MyAccessPoint MyPassPhrase 83 | 84 | ### No passphrase (open network) using pipe: 85 | echo -e "MyAccessPoint" | create_ap wlan0 eth0 86 | 87 | ### WPA + WPA2 passphrase using pipe: 88 | echo -e "MyAccessPoint\nMyPassPhrase" | create_ap wlan0 eth0 89 | 90 | ### Enable IEEE 802.11n 91 | create_ap --ieee80211n --ht_capab '[HT40+]' wlan0 eth0 MyAccessPoint MyPassPhrase 92 | 93 | ### Client Isolation: 94 | create_ap --isolate-clients wlan0 eth0 MyAccessPoint MyPassPhrase 95 | 96 | ## Systemd service 97 | Using the persistent [systemd](https://wiki.archlinux.org/index.php/systemd#Basic_systemctl_usage) service 98 | ### Start service immediately: 99 | systemctl start create_ap 100 | 101 | ### Start on boot: 102 | systemctl enable create_ap 103 | 104 | 105 | ## License 106 | FreeBSD 107 | 108 | 109 | [linux-wifi-hotspot]: https://github.com/lakinduakash/linux-wifi-hotspot 110 | [linux-router]: https://github.com/garywill/linux-router 111 | -------------------------------------------------------------------------------- /bash_completion: -------------------------------------------------------------------------------- 1 | # 2 | # Bash Completion routine for create_ap 3 | # 4 | 5 | _use_filedir() { 6 | if [[ $(type -t _filedir) == "function" ]]; then 7 | _filedir 8 | return 0 9 | fi 10 | return 1 11 | } 12 | 13 | _create_ap() { 14 | local awk_cmd=' 15 | ($1 ~ /^-/) { 16 | for (i = 1; i <= NF; i++) { 17 | if ($i ~ /,$/) { 18 | print substr ($i, 0, length ($i)-1) 19 | } 20 | else { 21 | print $i 22 | break 23 | } 24 | } 25 | } 26 | ' 27 | 28 | local cur prev opts 29 | COMPREPLY=() 30 | cur="$2" 31 | prev="$3" 32 | opts=$("$1" --help | awk "$awk_cmd") 33 | 34 | case "$prev" in 35 | -h|--help) 36 | # No Options 37 | ;; 38 | --version) 39 | # No Options 40 | ;; 41 | -c) 42 | # Refer http://en.wikipedia.org/wiki/List_of_WLAN_channels 43 | opts=$( 44 | iw list | grep ' MHz \[[[:digit:]]\+\] ' | 45 | grep -v 'no IR\|disabled' | 46 | sed 's/.*\[\(.*\)\].*/\1/' | sort -n | uniq 47 | ) 48 | ;; 49 | -w) 50 | opts="1 2 1+2" 51 | ;; 52 | -n) 53 | # No Options 54 | ;; 55 | -m) 56 | opts="nat bridge none" 57 | ;; 58 | --psk) 59 | # No Options 60 | ;; 61 | --hidden) 62 | # No Options 63 | ;; 64 | --mac-filter) 65 | # No Options 66 | ;; 67 | --mac-filter-accept) 68 | # No Options 69 | ;; 70 | --ieee80211n) 71 | # No Options 72 | ;; 73 | --ht_capab) 74 | # Refer http://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf 75 | opts=' 76 | [LDPC] [HT40-] [HT40+] [SMPS-STATIC] [SMPS-DYNAMIC] 77 | [GF] [SHORT-GI-20] [SHORT-GI-40] [TX-STBC] 78 | [RX-STBC1] [RX-STBC12] [RX-STBC123] [DELAYED-BA] 79 | [MAX-AMSDU-7935] [DSSS_CCK-40] [40-INTOLERANT] 80 | [LSIG-TXOP-PROT] 81 | ' 82 | ;; 83 | --country) 84 | local reg_file=/usr/lib/crda/regulatory.bin 85 | if command -v regdbdump > /dev/null && [[ -f "$reg_file" ]]; then 86 | local country_awk_cmd=' 87 | ($1 ~ /^country/) { 88 | print substr ($2, 0, length ($2)-1) 89 | } 90 | ' 91 | opts=$(regdbdump "$reg_file" 2>/dev/null | awk "$country_awk_cmd") 92 | else 93 | opts=' 94 | AD AE AF AI AL AM AN AR AS AT AU AW AZ BA BB BD BE 95 | BF BG BH BL BM BN BO BR BS BT BY BZ CA CF CH CI CL 96 | CN CO CR CX CY CZ DE DK DM DO DZ EC EE EG ES ET FI 97 | FM FR GB GD GE GF GH GL GP GR GT GU GY HK HN HR HT 98 | HU ID IE IL IN IR IS IT JM JO JP KE KH KN KP KR KW 99 | KY KZ LB LC LI LK LS LT LU LV MA MC MD ME MF MH MK 100 | MN MO MP MQ MR MT MU MW MX MY NG NI NL NO NP NZ OM 101 | PA PE PF PG PH PK PL PM PR PT PW PY QA RE RO RS RU 102 | RW SA SE SG SI SK SN SR SV SY TC TD TG TH TN TR TT 103 | TW TZ UA UG US UY UZ VC VE VI VN VU WF WS YE YT ZA 104 | ZW 00 105 | ' 106 | fi 107 | ;; 108 | --freq-band) 109 | opts="2.4 5" 110 | ;; 111 | --driver) 112 | # Refer http://w1.fi/cgit/hostap/tree/src/drivers 113 | # Not going to implement 114 | ;; 115 | --no-virt) 116 | # No Options 117 | ;; 118 | --no-haveged) 119 | # No Options 120 | ;; 121 | --fix-unmanaged) 122 | # No Options 123 | ;; 124 | --mac) 125 | # Not going to implement 126 | ;; 127 | --daemon) 128 | # No Options 129 | ;; 130 | --stop) 131 | local stop_awk_cmd='$1 ~ /^[0-9]+$/' 132 | opts=$("$1" --list-running | awk "$stop_awk_cmd") 133 | ;; 134 | --list-running) 135 | # No Options 136 | ;; 137 | --list-clients) 138 | local clients_awk_cmd='$1 ~ /^[0-9]+$/' 139 | opts=$("$1" --list-running | awk "$clients_awk_cmd") 140 | ;; 141 | --no-dns) 142 | # No Options 143 | ;; 144 | --dhcp-dns) 145 | # Not going to implement 146 | ;; 147 | --mkconfig) 148 | _use_filedir && return 0 149 | ;; 150 | --config) 151 | _use_filedir && return 0 152 | ;; 153 | -g) 154 | # Not going to implement 155 | ;; 156 | -d) 157 | # No Options 158 | ;; 159 | *) 160 | ;; 161 | esac 162 | 163 | COMPREPLY=( $(compgen -W "$opts" -- $cur) ) 164 | return 0 165 | } 166 | complete -F _create_ap create_ap 167 | 168 | # vim: set ft=sh: 169 | -------------------------------------------------------------------------------- /create_ap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # general dependencies: 4 | # bash (to run this script) 5 | # util-linux (for getopt) 6 | # procps or procps-ng 7 | # hostapd 8 | # iproute2 9 | # iw 10 | # iwconfig (you only need this if 'iw' can not recognize your adapter) 11 | # haveged (optional) 12 | 13 | # dependencies for 'nat' or 'none' Internet sharing method 14 | # dnsmasq 15 | # iptables 16 | 17 | VERSION=0.4.6 18 | PROGNAME="$(basename $0)" 19 | 20 | # make sure that all command outputs are in english 21 | # so we can parse them correctly 22 | export LC_ALL=C 23 | 24 | # all new files and directories must be readable only by root. 25 | # in special cases we must use chmod to give any other permissions. 26 | SCRIPT_UMASK=0077 27 | umask $SCRIPT_UMASK 28 | 29 | usage() { 30 | echo "Usage: "$PROGNAME" [options] [] [ []]" 31 | echo 32 | echo "Options:" 33 | echo " -h, --help Show this help" 34 | echo " --version Print version number" 35 | echo " -c Channel number (default: 1)" 36 | echo " -w Use 1 for WPA, use 2 for WPA2, use 1+2 for both (default: 1+2)" 37 | echo " -n Disable Internet sharing (if you use this, don't pass" 38 | echo " the argument)" 39 | echo " -m Method for Internet sharing." 40 | echo " Use: 'nat' for NAT (default)" 41 | echo " 'bridge' for bridging" 42 | echo " 'none' for no Internet sharing (equivalent to -n)" 43 | echo " --psk Use 64 hex digits pre-shared-key instead of passphrase" 44 | echo " --hidden Make the Access Point hidden (do not broadcast the SSID)" 45 | echo " --mac-filter Enable MAC address filtering" 46 | echo " --mac-filter-accept Location of MAC address filter list (defaults to /etc/hostapd/hostapd.accept)" 47 | echo " --redirect-to-localhost If -n is set, redirect every web request to localhost (useful for public information networks)" 48 | echo " --hostapd-debug With level between 1 and 2, passes arguments -d or -dd to hostapd for debugging." 49 | echo " --isolate-clients Disable communication between clients" 50 | echo " --ieee80211n Enable IEEE 802.11n (HT)" 51 | echo " --ieee80211ac Enable IEEE 802.11ac (VHT)" 52 | echo " --ht_capab HT capabilities (default: [HT40+])" 53 | echo " --vht_capab VHT capabilities" 54 | echo " --country Set two-letter country code for regularity (example: US)" 55 | echo " --freq-band Set frequency band. Valid inputs: 2.4, 5 (default: 2.4)" 56 | echo " --driver Choose your WiFi adapter driver (default: nl80211)" 57 | echo " --no-virt Do not create virtual interface" 58 | echo " --no-haveged Do not run 'haveged' automatically when needed" 59 | echo " --fix-unmanaged If NetworkManager shows your interface as unmanaged after you" 60 | echo " close create_ap, then use this option to switch your interface" 61 | echo " back to managed" 62 | echo " --mac Set MAC address" 63 | echo " --dhcp-dns Set DNS returned by DHCP" 64 | echo " --daemon Run create_ap in the background" 65 | echo " --pidfile Save daemon PID to file" 66 | echo " --logfile Save daemon messages to file" 67 | echo " --stop Send stop command to an already running create_ap. For an " 68 | echo " you can put the PID of create_ap or the WiFi interface. You can" 69 | echo " get them with --list-running" 70 | echo " --list-running Show the create_ap processes that are already running" 71 | echo " --list-clients List the clients connected to create_ap instance associated with ." 72 | echo " For an you can put the PID of create_ap or the WiFi interface." 73 | echo " If virtual WiFi interface was created, then use that one." 74 | echo " You can get them with --list-running" 75 | echo " --mkconfig Store configs in conf_file" 76 | echo " --config Load configs from conf_file" 77 | echo 78 | echo "Non-Bridging Options:" 79 | echo " --no-dns Disable dnsmasq DNS server" 80 | echo " --no-dnsmasq Disable dnsmasq server completely" 81 | echo " -g IPv4 Gateway for the Access Point (default: 192.168.12.1)" 82 | echo " -d DNS server will take into account /etc/hosts" 83 | echo " -e DNS server will take into account additional hosts file" 84 | echo 85 | echo "Useful informations:" 86 | echo " * If you're not using the --no-virt option, then you can create an AP with the same" 87 | echo " interface you are getting your Internet connection." 88 | echo " * You can pass your SSID and password through pipe or through arguments (see examples)." 89 | echo " * On bridge method if the is not a bridge interface, then" 90 | echo " a bridge interface is created automatically." 91 | echo 92 | echo "Examples:" 93 | echo " "$PROGNAME" wlan0 eth0 MyAccessPoint MyPassPhrase" 94 | echo " echo -e 'MyAccessPoint\nMyPassPhrase' | "$PROGNAME" wlan0 eth0" 95 | echo " "$PROGNAME" wlan0 eth0 MyAccessPoint" 96 | echo " echo 'MyAccessPoint' | "$PROGNAME" wlan0 eth0" 97 | echo " "$PROGNAME" wlan0 wlan0 MyAccessPoint MyPassPhrase" 98 | echo " "$PROGNAME" -n wlan0 MyAccessPoint MyPassPhrase" 99 | echo " "$PROGNAME" -m bridge wlan0 eth0 MyAccessPoint MyPassPhrase" 100 | echo " "$PROGNAME" -m bridge wlan0 br0 MyAccessPoint MyPassPhrase" 101 | echo " "$PROGNAME" --driver rtl871xdrv wlan0 eth0 MyAccessPoint MyPassPhrase" 102 | echo " "$PROGNAME" --daemon wlan0 eth0 MyAccessPoint MyPassPhrase" 103 | echo " "$PROGNAME" --stop wlan0" 104 | } 105 | 106 | # Busybox polyfills 107 | if cp --help 2>&1 | grep -q -- --no-clobber; then 108 | cp_n() { 109 | cp -n "$@" 110 | } 111 | else 112 | cp_n() { 113 | yes n | cp -i "$@" 114 | } 115 | fi 116 | 117 | # on success it echos a non-zero unused FD 118 | # on error it echos 0 119 | get_avail_fd() { 120 | local x 121 | for x in $(seq 1 $(ulimit -n)); do 122 | if [[ ! -a "/proc/$BASHPID/fd/$x" ]]; then 123 | echo $x 124 | return 125 | fi 126 | done 127 | echo 0 128 | } 129 | 130 | # lock file for the mutex counter 131 | COUNTER_LOCK_FILE=/tmp/create_ap.$$.lock 132 | 133 | cleanup_lock() { 134 | rm -f $COUNTER_LOCK_FILE 135 | } 136 | 137 | init_lock() { 138 | local LOCK_FILE=/tmp/create_ap.all.lock 139 | 140 | # we initialize only once 141 | [[ $LOCK_FD -ne 0 ]] && return 0 142 | 143 | LOCK_FD=$(get_avail_fd) 144 | [[ $LOCK_FD -eq 0 ]] && return 1 145 | 146 | # open/create lock file with write access for all users 147 | # otherwise normal users will not be able to use it. 148 | # to avoid race conditions on creation, we need to 149 | # use umask to set the permissions. 150 | umask 0555 151 | eval "exec $LOCK_FD>$LOCK_FILE" > /dev/null 2>&1 || return 1 152 | umask $SCRIPT_UMASK 153 | 154 | # there is a case where lock file was created from a normal 155 | # user. change the owner to root as soon as we can. 156 | [[ $(id -u) -eq 0 ]] && chown 0:0 $LOCK_FILE 157 | 158 | # create mutex counter lock file 159 | echo 0 > $COUNTER_LOCK_FILE 160 | 161 | return $? 162 | } 163 | 164 | # recursive mutex lock for all create_ap processes 165 | mutex_lock() { 166 | local counter_mutex_fd 167 | local counter 168 | 169 | # lock local mutex and read counter 170 | counter_mutex_fd=$(get_avail_fd) 171 | if [[ $counter_mutex_fd -ne 0 ]]; then 172 | eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE" 173 | flock $counter_mutex_fd 174 | read -u $counter_mutex_fd counter 175 | else 176 | echo "Failed to lock mutex counter" >&2 177 | return 1 178 | fi 179 | 180 | # lock global mutex and increase counter 181 | [[ $counter -eq 0 ]] && flock $LOCK_FD 182 | counter=$(( $counter + 1 )) 183 | 184 | # write counter and unlock local mutex 185 | echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd 186 | eval "exec ${counter_mutex_fd}<&-" 187 | return 0 188 | } 189 | 190 | # recursive mutex unlock for all create_ap processes 191 | mutex_unlock() { 192 | local counter_mutex_fd 193 | local counter 194 | 195 | # lock local mutex and read counter 196 | counter_mutex_fd=$(get_avail_fd) 197 | if [[ $counter_mutex_fd -ne 0 ]]; then 198 | eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE" 199 | flock $counter_mutex_fd 200 | read -u $counter_mutex_fd counter 201 | else 202 | echo "Failed to lock mutex counter" >&2 203 | return 1 204 | fi 205 | 206 | # decrease counter and unlock global mutex 207 | if [[ $counter -gt 0 ]]; then 208 | counter=$(( $counter - 1 )) 209 | [[ $counter -eq 0 ]] && flock -u $LOCK_FD 210 | fi 211 | 212 | # write counter and unlock local mutex 213 | echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd 214 | eval "exec ${counter_mutex_fd}<&-" 215 | return 0 216 | } 217 | 218 | # it takes 2 arguments 219 | # returns: 220 | # 0 if v1 (1st argument) and v2 (2nd argument) are the same 221 | # 1 if v1 is less than v2 222 | # 2 if v1 is greater than v2 223 | version_cmp() { 224 | local V1 V2 VN x 225 | [[ ! $1 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!" 226 | [[ ! $2 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!" 227 | 228 | V1=( $(echo $1 | tr '.' ' ') ) 229 | V2=( $(echo $2 | tr '.' ' ') ) 230 | VN=${#V1[@]} 231 | [[ $VN -lt ${#V2[@]} ]] && VN=${#V2[@]} 232 | 233 | for ((x = 0; x < $VN; x++)); do 234 | [[ ${V1[x]} -lt ${V2[x]} ]] && return 1 235 | [[ ${V1[x]} -gt ${V2[x]} ]] && return 2 236 | done 237 | 238 | return 0 239 | } 240 | 241 | USE_IWCONFIG=0 242 | 243 | is_interface() { 244 | [[ -z "$1" ]] && return 1 245 | [[ -d "/sys/class/net/${1}" ]] 246 | } 247 | 248 | is_wifi_interface() { 249 | which iw > /dev/null 2>&1 && iw dev $1 info > /dev/null 2>&1 && return 0 250 | if which iwconfig > /dev/null 2>&1 && iwconfig $1 > /dev/null 2>&1; then 251 | USE_IWCONFIG=1 252 | return 0 253 | fi 254 | return 1 255 | } 256 | 257 | is_bridge_interface() { 258 | [[ -z "$1" ]] && return 1 259 | [[ -d "/sys/class/net/${1}/bridge" ]] 260 | } 261 | 262 | get_phy_device() { 263 | local x 264 | for x in /sys/class/ieee80211/*; do 265 | [[ ! -e "$x" ]] && continue 266 | if [[ "${x##*/}" = "$1" ]]; then 267 | echo $1 268 | return 0 269 | elif [[ -e "$x/device/net/$1" ]]; then 270 | echo ${x##*/} 271 | return 0 272 | elif [[ -e "$x/device/net:$1" ]]; then 273 | echo ${x##*/} 274 | return 0 275 | fi 276 | done 277 | echo "Failed to get phy interface" >&2 278 | return 1 279 | } 280 | 281 | get_adapter_info() { 282 | local PHY 283 | PHY=$(get_phy_device "$1") 284 | [[ $? -ne 0 ]] && return 1 285 | iw phy $PHY info 286 | } 287 | 288 | get_adapter_kernel_module() { 289 | local MODULE 290 | MODULE=$(readlink -f "/sys/class/net/$1/device/driver/module") 291 | echo ${MODULE##*/} 292 | } 293 | 294 | can_be_sta_and_ap() { 295 | # iwconfig does not provide this information, assume false 296 | [[ $USE_IWCONFIG -eq 1 ]] && return 1 297 | if [[ "$(get_adapter_kernel_module "$1")" == "brcmfmac" ]]; then 298 | echo "WARN: brmfmac driver doesn't work properly with virtual interfaces and" >&2 299 | echo " it can cause kernel panic. For this reason we disallow virtual" >&2 300 | echo " interfaces for your adapter." >&2 301 | echo " For more info: https://github.com/oblique/create_ap/issues/203" >&2 302 | return 1 303 | fi 304 | get_adapter_info "$1" | grep -E '{.* managed.* AP.*}' > /dev/null 2>&1 && return 0 305 | get_adapter_info "$1" | grep -E '{.* AP.* managed.*}' > /dev/null 2>&1 && return 0 306 | return 1 307 | } 308 | 309 | can_be_ap() { 310 | # iwconfig does not provide this information, assume true 311 | [[ $USE_IWCONFIG -eq 1 ]] && return 0 312 | get_adapter_info "$1" | grep -E '\* AP$' > /dev/null 2>&1 && return 0 313 | return 1 314 | } 315 | 316 | can_transmit_to_channel() { 317 | local IFACE CHANNEL_NUM CHANNEL_INFO 318 | IFACE=$1 319 | CHANNEL_NUM=$2 320 | 321 | if [[ $USE_IWCONFIG -eq 0 ]]; then 322 | if [[ $FREQ_BAND == 2.4 ]]; then 323 | CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " 24[0-9][0-9] MHz \[${CHANNEL_NUM}\]") 324 | else 325 | CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " \(49[0-9][0-9]\|5[0-9]\{3\}\) MHz \[${CHANNEL_NUM}\]") 326 | fi 327 | [[ -z "${CHANNEL_INFO}" ]] && return 1 328 | [[ "${CHANNEL_INFO}" == *no\ IR* ]] && return 1 329 | [[ "${CHANNEL_INFO}" == *disabled* ]] && return 1 330 | return 0 331 | else 332 | CHANNEL_NUM=$(printf '%02d' ${CHANNEL_NUM}) 333 | CHANNEL_INFO=$(iwlist ${IFACE} channel | grep -E "Channel[[:blank:]]${CHANNEL_NUM}[[:blank:]]?:") 334 | [[ -z "${CHANNEL_INFO}" ]] && return 1 335 | return 0 336 | fi 337 | } 338 | 339 | # taken from iw/util.c 340 | ieee80211_frequency_to_channel() { 341 | local FREQ=$1 342 | if [[ $FREQ -eq 2484 ]]; then 343 | echo 14 344 | elif [[ $FREQ -lt 2484 ]]; then 345 | echo $(( ($FREQ - 2407) / 5 )) 346 | elif [[ $FREQ -ge 4910 && $FREQ -le 4980 ]]; then 347 | echo $(( ($FREQ - 4000) / 5 )) 348 | elif [[ $FREQ -le 45000 ]]; then 349 | echo $(( ($FREQ - 5000) / 5 )) 350 | elif [[ $FREQ -ge 58320 && $FREQ -le 64800 ]]; then 351 | echo $(( ($FREQ - 56160) / 2160 )) 352 | else 353 | echo 0 354 | fi 355 | } 356 | 357 | is_5ghz_frequency() { 358 | [[ $1 =~ ^(49[0-9]{2})|(5[0-9]{3})$ ]] 359 | } 360 | 361 | is_wifi_connected() { 362 | if [[ $USE_IWCONFIG -eq 0 ]]; then 363 | iw dev "$1" link 2>&1 | grep -E '^Connected to' > /dev/null 2>&1 && return 0 364 | else 365 | iwconfig "$1" 2>&1 | grep -E 'Access Point: [0-9a-fA-F]{2}:' > /dev/null 2>&1 && return 0 366 | fi 367 | return 1 368 | } 369 | 370 | is_macaddr() { 371 | echo "$1" | grep -E "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$" > /dev/null 2>&1 372 | } 373 | 374 | is_unicast_macaddr() { 375 | local x 376 | is_macaddr "$1" || return 1 377 | x=$(echo "$1" | cut -d: -f1) 378 | x=$(printf '%d' "0x${x}") 379 | [[ $(expr $x % 2) -eq 0 ]] 380 | } 381 | 382 | get_macaddr() { 383 | is_interface "$1" || return 384 | cat "/sys/class/net/${1}/address" 385 | } 386 | 387 | get_mtu() { 388 | is_interface "$1" || return 389 | cat "/sys/class/net/${1}/mtu" 390 | } 391 | 392 | alloc_new_iface() { 393 | local prefix=$1 394 | local i=0 395 | 396 | mutex_lock 397 | while :; do 398 | if ! is_interface $prefix$i && [[ ! -f $COMMON_CONFDIR/ifaces/$prefix$i ]]; then 399 | mkdir -p $COMMON_CONFDIR/ifaces 400 | touch $COMMON_CONFDIR/ifaces/$prefix$i 401 | echo $prefix$i 402 | mutex_unlock 403 | return 404 | fi 405 | i=$((i + 1)) 406 | done 407 | mutex_unlock 408 | } 409 | 410 | dealloc_iface() { 411 | rm -f $COMMON_CONFDIR/ifaces/$1 412 | } 413 | 414 | get_all_macaddrs() { 415 | cat /sys/class/net/*/address 416 | } 417 | 418 | get_new_macaddr() { 419 | local OLDMAC NEWMAC LAST_BYTE i 420 | OLDMAC=$(get_macaddr "$1") 421 | LAST_BYTE=$(printf %d 0x${OLDMAC##*:}) 422 | mutex_lock 423 | for i in {1..255}; do 424 | NEWMAC="${OLDMAC%:*}:$(printf %02x $(( ($LAST_BYTE + $i) % 256 )))" 425 | (get_all_macaddrs | grep "$NEWMAC" > /dev/null 2>&1) || break 426 | done 427 | mutex_unlock 428 | echo $NEWMAC 429 | } 430 | 431 | # start haveged when needed 432 | haveged_watchdog() { 433 | local show_warn=1 434 | while :; do 435 | mutex_lock 436 | if [[ $(cat /proc/sys/kernel/random/entropy_avail) -lt 1000 ]]; then 437 | if ! which haveged > /dev/null 2>&1; then 438 | if [[ $show_warn -eq 1 ]]; then 439 | echo "WARN: Low entropy detected. We recommend you to install \`haveged'" 440 | show_warn=0 441 | fi 442 | elif ! pidof haveged > /dev/null 2>&1; then 443 | echo "Low entropy detected, starting haveged" 444 | # boost low-entropy 445 | haveged -w 1024 -p $COMMON_CONFDIR/haveged.pid 446 | fi 447 | fi 448 | mutex_unlock 449 | sleep 2 450 | done 451 | } 452 | 453 | NETWORKMANAGER_CONF=/etc/NetworkManager/NetworkManager.conf 454 | NM_OLDER_VERSION=1 455 | 456 | networkmanager_exists() { 457 | local NM_VER 458 | which nmcli > /dev/null 2>&1 || return 1 459 | NM_VER=$(nmcli -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+') 460 | version_cmp $NM_VER 0.9.9 461 | if [[ $? -eq 1 ]]; then 462 | NM_OLDER_VERSION=1 463 | else 464 | NM_OLDER_VERSION=0 465 | fi 466 | return 0 467 | } 468 | 469 | networkmanager_is_running() { 470 | local NMCLI_OUT 471 | networkmanager_exists || return 1 472 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 473 | NMCLI_OUT=$(nmcli -t -f RUNNING nm 2>&1 | grep -E '^running$') 474 | else 475 | NMCLI_OUT=$(nmcli -t -f RUNNING g 2>&1 | grep -E '^running$') 476 | fi 477 | [[ -n "$NMCLI_OUT" ]] 478 | } 479 | 480 | networkmanager_knows_iface() { 481 | # check if the interface $1 is known to NetworkManager 482 | # an interface may exist but may not be known to NetworkManager if it is in a different network namespace than NetworkManager 483 | nmcli -t -f DEVICE d 2>&1 | grep -Fxq "$1" 484 | } 485 | 486 | networkmanager_iface_is_unmanaged() { 487 | is_interface "$1" || return 2 488 | networkmanager_knows_iface "$1" || return 0 489 | (nmcli -t -f DEVICE,STATE d 2>&1 | grep -E "^$1:unmanaged$" > /dev/null 2>&1) || return 1 490 | } 491 | 492 | ADDED_UNMANAGED= 493 | 494 | networkmanager_add_unmanaged() { 495 | local MAC UNMANAGED WAS_EMPTY x 496 | networkmanager_exists || return 1 497 | 498 | [[ -d ${NETWORKMANAGER_CONF%/*} ]] || mkdir -p ${NETWORKMANAGER_CONF%/*} 499 | [[ -f ${NETWORKMANAGER_CONF} ]] || touch ${NETWORKMANAGER_CONF} 500 | 501 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 502 | if [[ -z "$2" ]]; then 503 | MAC=$(get_macaddr "$1") 504 | else 505 | MAC="$2" 506 | fi 507 | [[ -z "$MAC" ]] && return 1 508 | fi 509 | 510 | mutex_lock 511 | UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf) 512 | 513 | WAS_EMPTY=0 514 | [[ -z "$UNMANAGED" ]] && WAS_EMPTY=1 515 | UNMANAGED=$(echo "$UNMANAGED" | sed 's/unmanaged-devices=//' | tr ';,' ' ') 516 | 517 | # if it exists, do nothing 518 | for x in $UNMANAGED; do 519 | if [[ $x == "mac:${MAC}" ]] || 520 | [[ $NM_OLDER_VERSION -eq 0 && $x == "interface-name:${1}" ]]; then 521 | mutex_unlock 522 | return 2 523 | fi 524 | done 525 | 526 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 527 | UNMANAGED="${UNMANAGED} mac:${MAC}" 528 | else 529 | UNMANAGED="${UNMANAGED} interface-name:${1}" 530 | fi 531 | 532 | UNMANAGED=$(echo $UNMANAGED | sed -e 's/^ //') 533 | UNMANAGED="${UNMANAGED// /;}" 534 | UNMANAGED="unmanaged-devices=${UNMANAGED}" 535 | 536 | if ! grep -E '^\[keyfile\]' ${NETWORKMANAGER_CONF} > /dev/null 2>&1; then 537 | echo -e "\n\n[keyfile]\n${UNMANAGED}" >> ${NETWORKMANAGER_CONF} 538 | elif [[ $WAS_EMPTY -eq 1 ]]; then 539 | sed -e "s/^\(\[keyfile\].*\)$/\1\n${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 540 | else 541 | sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 542 | fi 543 | 544 | ADDED_UNMANAGED="${ADDED_UNMANAGED} ${1} " 545 | mutex_unlock 546 | 547 | local nm_pid=$(pidof NetworkManager) 548 | [[ -n "$nm_pid" ]] && kill -HUP $nm_pid 549 | 550 | return 0 551 | } 552 | 553 | networkmanager_rm_unmanaged() { 554 | local MAC UNMANAGED 555 | networkmanager_exists || return 1 556 | [[ ! -f ${NETWORKMANAGER_CONF} ]] && return 1 557 | 558 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 559 | if [[ -z "$2" ]]; then 560 | MAC=$(get_macaddr "$1") 561 | else 562 | MAC="$2" 563 | fi 564 | [[ -z "$MAC" ]] && return 1 565 | fi 566 | 567 | mutex_lock 568 | UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf | sed 's/unmanaged-devices=//' | tr ';,' ' ') 569 | 570 | if [[ -z "$UNMANAGED" ]]; then 571 | mutex_unlock 572 | return 1 573 | fi 574 | 575 | [[ -n "$MAC" ]] && UNMANAGED=$(echo $UNMANAGED | sed -e "s/mac:${MAC}\( \|$\)//g") 576 | UNMANAGED=$(echo $UNMANAGED | sed -e "s/interface-name:${1}\( \|$\)//g") 577 | UNMANAGED=$(echo $UNMANAGED | sed -e 's/ $//') 578 | 579 | if [[ -z "$UNMANAGED" ]]; then 580 | sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF} 581 | else 582 | UNMANAGED="${UNMANAGED// /;}" 583 | UNMANAGED="unmanaged-devices=${UNMANAGED}" 584 | sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 585 | fi 586 | 587 | ADDED_UNMANAGED="${ADDED_UNMANAGED/ ${1} /}" 588 | mutex_unlock 589 | 590 | local nm_pid=$(pidof NetworkManager) 591 | [[ -n "$nm_pid" ]] && kill -HUP $nm_pid 592 | 593 | return 0 594 | } 595 | 596 | networkmanager_fix_unmanaged() { 597 | [[ -f ${NETWORKMANAGER_CONF} ]] || return 598 | 599 | mutex_lock 600 | sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF} 601 | mutex_unlock 602 | 603 | local nm_pid=$(pidof NetworkManager) 604 | [[ -n "$nm_pid" ]] && kill -HUP $nm_pid 605 | } 606 | 607 | networkmanager_rm_unmanaged_if_needed() { 608 | [[ $ADDED_UNMANAGED =~ .*\ ${1}\ .* ]] && networkmanager_rm_unmanaged $1 $2 609 | } 610 | 611 | networkmanager_wait_until_unmanaged() { 612 | local RES 613 | networkmanager_is_running || return 1 614 | while :; do 615 | networkmanager_iface_is_unmanaged "$1" 616 | RES=$? 617 | [[ $RES -eq 0 ]] && break 618 | [[ $RES -eq 2 ]] && die "Interface '${1}' does not exist. 619 | It's probably renamed by a udev rule." 620 | sleep 1 621 | done 622 | sleep 2 623 | return 0 624 | } 625 | 626 | 627 | CHANNEL=default 628 | GATEWAY=192.168.12.1 629 | WPA_VERSION=1+2 630 | ETC_HOSTS=0 631 | ADDN_HOSTS= 632 | DHCP_DNS=gateway 633 | NO_DNS=0 634 | NO_DNSMASQ=0 635 | DNS_PORT= 636 | HIDDEN=0 637 | MAC_FILTER=0 638 | MAC_FILTER_ACCEPT=/etc/hostapd/hostapd.accept 639 | ISOLATE_CLIENTS=0 640 | SHARE_METHOD=nat 641 | IEEE80211N=0 642 | IEEE80211AC=0 643 | HT_CAPAB='[HT40+]' 644 | VHT_CAPAB= 645 | DRIVER=nl80211 646 | NO_VIRT=0 647 | COUNTRY= 648 | FREQ_BAND=2.4 649 | NEW_MACADDR= 650 | DAEMONIZE=0 651 | DAEMON_PIDFILE= 652 | DAEMON_LOGFILE=/dev/null 653 | NO_HAVEGED=0 654 | USE_PSK=0 655 | 656 | HOSTAPD_DEBUG_ARGS= 657 | REDIRECT_TO_LOCALHOST=0 658 | 659 | CONFIG_OPTS=(CHANNEL GATEWAY WPA_VERSION ETC_HOSTS DHCP_DNS NO_DNS NO_DNSMASQ HIDDEN MAC_FILTER MAC_FILTER_ACCEPT ISOLATE_CLIENTS 660 | SHARE_METHOD IEEE80211N IEEE80211AC HT_CAPAB VHT_CAPAB DRIVER NO_VIRT COUNTRY FREQ_BAND 661 | NEW_MACADDR DAEMONIZE DAEMON_PIDFILE DAEMON_LOGFILE NO_HAVEGED WIFI_IFACE INTERNET_IFACE 662 | SSID PASSPHRASE USE_PSK) 663 | 664 | FIX_UNMANAGED=0 665 | LIST_RUNNING=0 666 | STOP_ID= 667 | LIST_CLIENTS_ID= 668 | 669 | STORE_CONFIG= 670 | LOAD_CONFIG= 671 | 672 | CONFDIR= 673 | WIFI_IFACE= 674 | VWIFI_IFACE= 675 | INTERNET_IFACE= 676 | BRIDGE_IFACE= 677 | OLD_MACADDR= 678 | IP_ADDRS= 679 | ROUTE_ADDRS= 680 | 681 | HAVEGED_WATCHDOG_PID= 682 | 683 | _cleanup() { 684 | local PID x 685 | 686 | trap "" SIGINT SIGUSR1 SIGUSR2 EXIT 687 | mutex_lock 688 | disown -a 689 | 690 | # kill haveged_watchdog 691 | [[ -n "$HAVEGED_WATCHDOG_PID" ]] && kill $HAVEGED_WATCHDOG_PID 692 | 693 | # kill processes 694 | for x in $CONFDIR/*.pid; do 695 | # even if the $CONFDIR is empty, the for loop will assign 696 | # a value in $x. so we need to check if the value is a file 697 | [[ -f $x ]] && kill -9 $(cat $x) 698 | done 699 | 700 | rm -rf $CONFDIR 701 | 702 | local found=0 703 | for x in $(list_running_conf); do 704 | if [[ -f $x/nat_internet_iface && $(cat $x/nat_internet_iface) == $INTERNET_IFACE ]]; then 705 | found=1 706 | break 707 | fi 708 | done 709 | 710 | if [[ $found -eq 0 ]]; then 711 | cp -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding \ 712 | /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding 713 | rm -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding 714 | fi 715 | 716 | # if we are the last create_ap instance then set back the common values 717 | if ! has_running_instance; then 718 | # kill common processes 719 | for x in $COMMON_CONFDIR/*.pid; do 720 | [[ -f $x ]] && kill -9 $(cat $x) 721 | done 722 | 723 | # set old ip_forward 724 | if [[ -f $COMMON_CONFDIR/ip_forward ]]; then 725 | cp -f $COMMON_CONFDIR/ip_forward /proc/sys/net/ipv4 726 | rm -f $COMMON_CONFDIR/ip_forward 727 | fi 728 | 729 | # set old bridge-nf-call-iptables 730 | if [[ -f $COMMON_CONFDIR/bridge-nf-call-iptables ]]; then 731 | if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 732 | cp -f $COMMON_CONFDIR/bridge-nf-call-iptables /proc/sys/net/bridge 733 | fi 734 | rm -f $COMMON_CONFDIR/bridge-nf-call-iptables 735 | fi 736 | 737 | rm -rf $COMMON_CONFDIR 738 | fi 739 | 740 | if [[ "$SHARE_METHOD" != "none" ]]; then 741 | if [[ "$SHARE_METHOD" == "nat" ]]; then 742 | iptables -w -t nat -D POSTROUTING -s ${GATEWAY%.*}.0/24 ! -o ${WIFI_IFACE} -j MASQUERADE 743 | iptables -w -D FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT 744 | iptables -w -D FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT 745 | elif [[ "$SHARE_METHOD" == "bridge" ]]; then 746 | if ! is_bridge_interface $INTERNET_IFACE; then 747 | ip link set dev $BRIDGE_IFACE down 748 | ip link set dev $INTERNET_IFACE down 749 | ip link set dev $INTERNET_IFACE promisc off 750 | ip link set dev $INTERNET_IFACE nomaster 751 | ip link delete $BRIDGE_IFACE type bridge 752 | ip addr flush $INTERNET_IFACE 753 | ip link set dev $INTERNET_IFACE up 754 | dealloc_iface $BRIDGE_IFACE 755 | 756 | for x in "${IP_ADDRS[@]}"; do 757 | x="${x/inet/}" 758 | x="${x/secondary/}" 759 | x="${x/dynamic/}" 760 | x=$(echo $x | sed 's/\([0-9]\)sec/\1/g') 761 | x="${x/${INTERNET_IFACE}/}" 762 | ip addr add $x dev $INTERNET_IFACE 763 | done 764 | 765 | ip route flush dev $INTERNET_IFACE 766 | 767 | for x in "${ROUTE_ADDRS[@]}"; do 768 | [[ -z "$x" ]] && continue 769 | [[ "$x" == default* ]] && continue 770 | ip route add $x dev $INTERNET_IFACE 771 | done 772 | 773 | for x in "${ROUTE_ADDRS[@]}"; do 774 | [[ -z "$x" ]] && continue 775 | [[ "$x" != default* ]] && continue 776 | ip route add $x dev $INTERNET_IFACE 777 | done 778 | 779 | networkmanager_rm_unmanaged_if_needed $INTERNET_IFACE 780 | fi 781 | fi 782 | fi 783 | 784 | if [[ "$SHARE_METHOD" != "bridge" ]]; then 785 | if [[ $NO_DNS -eq 0 ]]; then 786 | iptables -w -D INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT 787 | iptables -w -D INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT 788 | iptables -w -t nat -D PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ 789 | -p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT 790 | iptables -w -t nat -D PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ 791 | -p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT 792 | fi 793 | iptables -w -D INPUT -p udp -m udp --dport 67 -j ACCEPT 794 | fi 795 | 796 | if [[ $NO_VIRT -eq 0 ]]; then 797 | if [[ -n "$VWIFI_IFACE" ]]; then 798 | ip link set down dev ${VWIFI_IFACE} 799 | ip addr flush ${VWIFI_IFACE} 800 | networkmanager_rm_unmanaged_if_needed ${VWIFI_IFACE} ${OLD_MACADDR} 801 | iw dev ${VWIFI_IFACE} del 802 | dealloc_iface $VWIFI_IFACE 803 | fi 804 | else 805 | ip link set down dev ${WIFI_IFACE} 806 | ip addr flush ${WIFI_IFACE} 807 | if [[ -n "$NEW_MACADDR" ]]; then 808 | ip link set dev ${WIFI_IFACE} address ${OLD_MACADDR} 809 | fi 810 | networkmanager_rm_unmanaged_if_needed ${WIFI_IFACE} ${OLD_MACADDR} 811 | fi 812 | 813 | mutex_unlock 814 | cleanup_lock 815 | 816 | if [[ $RUNNING_AS_DAEMON -eq 1 && -n "$DAEMON_PIDFILE" && -f "$DAEMON_PIDFILE" ]]; then 817 | rm $DAEMON_PIDFILE 818 | fi 819 | } 820 | 821 | cleanup() { 822 | echo 823 | echo -n "Doing cleanup.. " 824 | _cleanup > /dev/null 2>&1 825 | echo "done" 826 | } 827 | 828 | die() { 829 | [[ -n "$1" ]] && echo -e "\nERROR: $1\n" >&2 830 | # send die signal to the main process 831 | [[ $BASHPID -ne $$ ]] && kill -USR2 $$ 832 | # we don't need to call cleanup because it's traped on EXIT 833 | exit 1 834 | } 835 | 836 | clean_exit() { 837 | # send clean_exit signal to the main process 838 | [[ $BASHPID -ne $$ ]] && kill -USR1 $$ 839 | # we don't need to call cleanup because it's traped on EXIT 840 | exit 0 841 | } 842 | 843 | list_running_conf() { 844 | local x 845 | mutex_lock 846 | for x in /tmp/create_ap.*; do 847 | if [[ -f $x/pid && -f $x/wifi_iface && -d /proc/$(cat $x/pid) ]]; then 848 | echo $x 849 | fi 850 | done 851 | mutex_unlock 852 | } 853 | 854 | list_running() { 855 | local IFACE wifi_iface x 856 | mutex_lock 857 | for x in $(list_running_conf); do 858 | IFACE=${x#*.} 859 | IFACE=${IFACE%%.*} 860 | wifi_iface=$(cat $x/wifi_iface) 861 | 862 | if [[ $IFACE == $wifi_iface ]]; then 863 | echo $(cat $x/pid) $IFACE 864 | else 865 | echo $(cat $x/pid) $IFACE '('$(cat $x/wifi_iface)')' 866 | fi 867 | done 868 | mutex_unlock 869 | } 870 | 871 | get_wifi_iface_from_pid() { 872 | list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E "^${1} " | cut -d' ' -f2 873 | } 874 | 875 | get_pid_from_wifi_iface() { 876 | list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E " ${1}$" | cut -d' ' -f1 877 | } 878 | 879 | get_confdir_from_pid() { 880 | local IFACE x 881 | mutex_lock 882 | for x in $(list_running_conf); do 883 | if [[ $(cat $x/pid) == "$1" ]]; then 884 | echo $x 885 | break 886 | fi 887 | done 888 | mutex_unlock 889 | } 890 | 891 | print_client() { 892 | local line ipaddr hostname 893 | local mac="$1" 894 | 895 | if [[ -f $CONFDIR/dnsmasq.leases ]]; then 896 | line=$(grep " $mac " $CONFDIR/dnsmasq.leases | tail -n 1) 897 | ipaddr=$(echo $line | cut -d' ' -f3) 898 | hostname=$(echo "$line" | cut -d' ' -f4) 899 | fi 900 | 901 | [[ -z "$ipaddr" ]] && ipaddr="*" 902 | [[ -z "$hostname" ]] && hostname="*" 903 | 904 | printf "%-20s %-18s %s\n" "$mac" "$ipaddr" "$hostname" 905 | } 906 | 907 | list_clients() { 908 | local wifi_iface pid 909 | 910 | # If PID is given, get the associated wifi iface 911 | if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then 912 | pid="$1" 913 | wifi_iface=$(get_wifi_iface_from_pid "$pid") 914 | [[ -z "$wifi_iface" ]] && die "'$pid' is not the pid of a running $PROGNAME instance." 915 | fi 916 | 917 | [[ -z "$wifi_iface" ]] && wifi_iface="$1" 918 | is_wifi_interface "$wifi_iface" || die "'$wifi_iface' is not a WiFi interface." 919 | 920 | [[ -z "$pid" ]] && pid=$(get_pid_from_wifi_iface "$wifi_iface") 921 | [[ -z "$pid" ]] && die "'$wifi_iface' is not used from $PROGNAME instance.\n\ 922 | Maybe you need to pass the virtual interface instead.\n\ 923 | Use --list-running to find it out." 924 | [[ -z "$CONFDIR" ]] && CONFDIR=$(get_confdir_from_pid "$pid") 925 | 926 | if [[ $USE_IWCONFIG -eq 0 ]]; then 927 | local awk_cmd='($1 ~ /Station$/) {print $2}' 928 | local client_list=$(iw dev "$wifi_iface" station dump | awk "$awk_cmd") 929 | 930 | if [[ -z "$client_list" ]]; then 931 | echo "No clients connected" 932 | return 933 | fi 934 | 935 | printf "%-20s %-18s %s\n" "MAC" "IP" "Hostname" 936 | 937 | local mac 938 | for mac in $client_list; do 939 | print_client $mac 940 | done 941 | else 942 | die "This option is not supported for the current driver." 943 | fi 944 | } 945 | 946 | has_running_instance() { 947 | local PID x 948 | 949 | mutex_lock 950 | for x in /tmp/create_ap.*; do 951 | if [[ -f $x/pid ]]; then 952 | PID=$(cat $x/pid) 953 | if [[ -d /proc/$PID ]]; then 954 | mutex_unlock 955 | return 0 956 | fi 957 | fi 958 | done 959 | mutex_lock 960 | 961 | return 1 962 | } 963 | 964 | is_running_pid() { 965 | list_running | grep -E "^${1} " > /dev/null 2>&1 966 | } 967 | 968 | send_stop() { 969 | local x 970 | 971 | mutex_lock 972 | # send stop signal to specific pid 973 | if is_running_pid $1; then 974 | kill -USR1 $1 975 | mutex_unlock 976 | return 977 | fi 978 | 979 | # send stop signal to specific interface 980 | for x in $(list_running | grep -E " \(?${1}( |\)?\$)" | cut -f1 -d' '); do 981 | kill -USR1 $x 982 | done 983 | mutex_unlock 984 | } 985 | 986 | # Storing configs 987 | write_config() { 988 | local i=1 989 | 990 | if ! eval 'echo -n > "$STORE_CONFIG"' > /dev/null 2>&1; then 991 | echo "ERROR: Unable to create config file $STORE_CONFIG" >&2 992 | exit 1 993 | fi 994 | 995 | WIFI_IFACE=$1 996 | if [[ "$SHARE_METHOD" == "none" ]]; then 997 | SSID="$2" 998 | PASSPHRASE="$3" 999 | else 1000 | INTERNET_IFACE="$2" 1001 | SSID="$3" 1002 | PASSPHRASE="$4" 1003 | fi 1004 | 1005 | for config_opt in "${CONFIG_OPTS[@]}"; do 1006 | eval echo $config_opt=\$$config_opt 1007 | done >> "$STORE_CONFIG" 1008 | 1009 | echo -e "Config options written to '$STORE_CONFIG'" 1010 | exit 0 1011 | } 1012 | 1013 | is_config_opt() { 1014 | local elem opt="$1" 1015 | 1016 | for elem in "${CONFIG_OPTS[@]}"; do 1017 | if [[ "$elem" == "$opt" ]]; then 1018 | return 0 1019 | fi 1020 | done 1021 | return 1 1022 | } 1023 | 1024 | # Load options from config file 1025 | read_config() { 1026 | local opt_name opt_val line 1027 | 1028 | while read line; do 1029 | # Read switches and their values 1030 | opt_name="${line%%=*}" 1031 | opt_val="${line#*=}" 1032 | if is_config_opt "$opt_name" ; then 1033 | eval $opt_name="\$opt_val" 1034 | else 1035 | echo "WARN: Unrecognized configuration entry $opt_name" >&2 1036 | fi 1037 | done < "$LOAD_CONFIG" 1038 | } 1039 | 1040 | 1041 | ARGS=( "$@" ) 1042 | 1043 | # Preprocessing for --config before option-parsing starts 1044 | for ((i=0; i<$#; i++)); do 1045 | if [[ "${ARGS[i]}" = "--config" ]]; then 1046 | if [[ -f "${ARGS[i+1]}" ]]; then 1047 | LOAD_CONFIG="${ARGS[i+1]}" 1048 | read_config 1049 | else 1050 | echo "ERROR: No config file found at given location" >&2 1051 | exit 1 1052 | fi 1053 | break 1054 | fi 1055 | done 1056 | 1057 | 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","pidfile:","logfile:","stop:","list","list-running","list-clients:","version","psk","no-haveged","no-dns","no-dnsmasq","mkconfig:","config:" -n "$PROGNAME" -- "$@") 1058 | [[ $? -ne 0 ]] && exit 1 1059 | eval set -- "$GETOPT_ARGS" 1060 | 1061 | while :; do 1062 | case "$1" in 1063 | -h|--help) 1064 | usage 1065 | exit 0 1066 | ;; 1067 | --version) 1068 | echo $VERSION 1069 | exit 0 1070 | ;; 1071 | --hidden) 1072 | shift 1073 | HIDDEN=1 1074 | ;; 1075 | --mac-filter) 1076 | shift 1077 | MAC_FILTER=1 1078 | ;; 1079 | --mac-filter-accept) 1080 | shift 1081 | MAC_FILTER_ACCEPT="$1" 1082 | shift 1083 | ;; 1084 | --isolate-clients) 1085 | shift 1086 | ISOLATE_CLIENTS=1 1087 | ;; 1088 | -c) 1089 | shift 1090 | CHANNEL="$1" 1091 | shift 1092 | ;; 1093 | -w) 1094 | shift 1095 | WPA_VERSION="$1" 1096 | [[ "$WPA_VERSION" == "2+1" ]] && WPA_VERSION=1+2 1097 | shift 1098 | ;; 1099 | -g) 1100 | shift 1101 | GATEWAY="$1" 1102 | shift 1103 | ;; 1104 | -d) 1105 | shift 1106 | ETC_HOSTS=1 1107 | ;; 1108 | -e) 1109 | shift 1110 | ADDN_HOSTS="$1" 1111 | shift 1112 | ;; 1113 | -n) 1114 | shift 1115 | SHARE_METHOD=none 1116 | ;; 1117 | -m) 1118 | shift 1119 | SHARE_METHOD="$1" 1120 | shift 1121 | ;; 1122 | --ieee80211n) 1123 | shift 1124 | IEEE80211N=1 1125 | ;; 1126 | --ieee80211ac) 1127 | shift 1128 | IEEE80211AC=1 1129 | ;; 1130 | --ht_capab) 1131 | shift 1132 | HT_CAPAB="$1" 1133 | shift 1134 | ;; 1135 | --vht_capab) 1136 | shift 1137 | VHT_CAPAB="$1" 1138 | shift 1139 | ;; 1140 | --driver) 1141 | shift 1142 | DRIVER="$1" 1143 | shift 1144 | ;; 1145 | --no-virt) 1146 | shift 1147 | NO_VIRT=1 1148 | ;; 1149 | --fix-unmanaged) 1150 | shift 1151 | FIX_UNMANAGED=1 1152 | ;; 1153 | --country) 1154 | shift 1155 | COUNTRY="$1" 1156 | shift 1157 | ;; 1158 | --freq-band) 1159 | shift 1160 | FREQ_BAND="$1" 1161 | shift 1162 | ;; 1163 | --mac) 1164 | shift 1165 | NEW_MACADDR="$1" 1166 | shift 1167 | ;; 1168 | --dhcp-dns) 1169 | shift 1170 | DHCP_DNS="$1" 1171 | shift 1172 | ;; 1173 | --daemon) 1174 | shift 1175 | DAEMONIZE=1 1176 | ;; 1177 | --pidfile) 1178 | shift 1179 | DAEMON_PIDFILE="$1" 1180 | shift 1181 | ;; 1182 | --logfile) 1183 | shift 1184 | DAEMON_LOGFILE="$1" 1185 | shift 1186 | ;; 1187 | --stop) 1188 | shift 1189 | STOP_ID="$1" 1190 | shift 1191 | ;; 1192 | --list) 1193 | shift 1194 | LIST_RUNNING=1 1195 | echo -e "WARN: --list is deprecated, use --list-running instead.\n" >&2 1196 | ;; 1197 | --list-running) 1198 | shift 1199 | LIST_RUNNING=1 1200 | ;; 1201 | --list-clients) 1202 | shift 1203 | LIST_CLIENTS_ID="$1" 1204 | shift 1205 | ;; 1206 | --no-haveged) 1207 | shift 1208 | NO_HAVEGED=1 1209 | ;; 1210 | --psk) 1211 | shift 1212 | USE_PSK=1 1213 | ;; 1214 | --no-dns) 1215 | shift 1216 | NO_DNS=1 1217 | ;; 1218 | --no-dnsmasq) 1219 | shift 1220 | NO_DNSMASQ=1 1221 | ;; 1222 | --redirect-to-localhost) 1223 | shift 1224 | REDIRECT_TO_LOCALHOST=1 1225 | ;; 1226 | --hostapd-debug) 1227 | shift 1228 | if [ "x$1" = "x1" ]; then 1229 | HOSTAPD_DEBUG_ARGS="-d" 1230 | elif [ "x$1" = "x2" ]; then 1231 | HOSTAPD_DEBUG_ARGS="-dd" 1232 | else 1233 | printf "Error: argument for --hostapd-debug expected 1 or 2, got %s\n" "$1" 1234 | exit 1 1235 | fi 1236 | shift 1237 | ;; 1238 | --mkconfig) 1239 | shift 1240 | STORE_CONFIG="$1" 1241 | shift 1242 | ;; 1243 | --config) 1244 | shift 1245 | shift 1246 | ;; 1247 | --) 1248 | shift 1249 | break 1250 | ;; 1251 | esac 1252 | done 1253 | 1254 | # Load positional args from config file, if needed 1255 | if [[ -n "$LOAD_CONFIG" && $# -eq 0 ]]; then 1256 | i=0 1257 | # set arguments in order 1258 | for x in WIFI_IFACE INTERNET_IFACE SSID PASSPHRASE; do 1259 | if eval "[[ -n \"\$${x}\" ]]"; then 1260 | eval "set -- \"\${@:1:$i}\" \"\$${x}\"" 1261 | ((i++)) 1262 | fi 1263 | # we unset the variable to avoid any problems later 1264 | eval "unset $x" 1265 | done 1266 | fi 1267 | 1268 | # Check if required number of positional args are present 1269 | if [[ $# -lt 1 && $FIX_UNMANAGED -eq 0 && -z "$STOP_ID" && 1270 | $LIST_RUNNING -eq 0 && -z "$LIST_CLIENTS_ID" ]]; then 1271 | usage >&2 1272 | exit 1 1273 | fi 1274 | 1275 | # Set NO_DNS, if dnsmasq is disabled 1276 | if [[ $NO_DNSMASQ -eq 1 ]]; then 1277 | NO_DNS=1 1278 | fi 1279 | 1280 | trap "cleanup_lock" EXIT 1281 | 1282 | if ! init_lock; then 1283 | echo "ERROR: Failed to initialize lock" >&2 1284 | exit 1 1285 | fi 1286 | 1287 | # if the user press ctrl+c or we get USR1 signal 1288 | # then run clean_exit() 1289 | trap "clean_exit" SIGINT SIGUSR1 1290 | # if we get USR2 signal then run die(). 1291 | trap "die" SIGUSR2 1292 | 1293 | [[ -n "$STORE_CONFIG" ]] && write_config "$@" 1294 | 1295 | if [[ $LIST_RUNNING -eq 1 ]]; then 1296 | echo -e "List of running $PROGNAME instances:\n" 1297 | list_running 1298 | exit 0 1299 | fi 1300 | 1301 | if [[ -n "$LIST_CLIENTS_ID" ]]; then 1302 | list_clients "$LIST_CLIENTS_ID" 1303 | exit 0 1304 | fi 1305 | 1306 | if [[ $(id -u) -ne 0 ]]; then 1307 | echo "You must run it as root." >&2 1308 | exit 1 1309 | fi 1310 | 1311 | if [[ -n "$STOP_ID" ]]; then 1312 | echo "Trying to kill $PROGNAME instance associated with $STOP_ID..." 1313 | send_stop "$STOP_ID" 1314 | exit 0 1315 | fi 1316 | 1317 | if [[ $FIX_UNMANAGED -eq 1 ]]; then 1318 | echo "Trying to fix unmanaged status in NetworkManager..." 1319 | networkmanager_fix_unmanaged 1320 | exit 0 1321 | fi 1322 | 1323 | if [[ $DAEMONIZE -eq 1 && $RUNNING_AS_DAEMON -eq 0 ]]; then 1324 | # Assume we're running underneath a service manager if PIDFILE is set 1325 | # and don't clobber it's output with a useless message 1326 | if [ -z "$DAEMON_PIDFILE" ]; then 1327 | echo "Running as Daemon..." 1328 | fi 1329 | # run a detached create_ap 1330 | RUNNING_AS_DAEMON=1 setsid "$0" "${ARGS[@]}" >>$DAEMON_LOGFILE 2>&1 & 1331 | exit 0 1332 | elif [[ $RUNNING_AS_DAEMON -eq 1 && -n "$DAEMON_PIDFILE" ]]; then 1333 | echo $$ >$DAEMON_PIDFILE 1334 | fi 1335 | 1336 | if [[ $FREQ_BAND != 2.4 && $FREQ_BAND != 5 ]]; then 1337 | echo "ERROR: Invalid frequency band" >&2 1338 | exit 1 1339 | fi 1340 | 1341 | if [[ $CHANNEL == default ]]; then 1342 | if [[ $FREQ_BAND == 2.4 ]]; then 1343 | CHANNEL=1 1344 | else 1345 | CHANNEL=36 1346 | fi 1347 | fi 1348 | 1349 | if [[ $FREQ_BAND != 5 && $CHANNEL -gt 14 ]]; then 1350 | echo "Channel number is greater than 14, assuming 5GHz frequency band" 1351 | FREQ_BAND=5 1352 | fi 1353 | 1354 | WIFI_IFACE=$1 1355 | 1356 | if ! is_wifi_interface ${WIFI_IFACE}; then 1357 | echo "ERROR: '${WIFI_IFACE}' is not a WiFi interface" >&2 1358 | exit 1 1359 | fi 1360 | 1361 | if ! can_be_ap ${WIFI_IFACE}; then 1362 | echo "ERROR: Your adapter does not support AP (master) mode" >&2 1363 | exit 1 1364 | fi 1365 | 1366 | if ! can_be_sta_and_ap ${WIFI_IFACE}; then 1367 | if is_wifi_connected ${WIFI_IFACE}; then 1368 | echo "ERROR: Your adapter can not be a station (i.e. be connected) and an AP at the same time" >&2 1369 | exit 1 1370 | elif [[ $NO_VIRT -eq 0 ]]; then 1371 | echo "WARN: Your adapter does not fully support AP virtual interface, enabling --no-virt" >&2 1372 | NO_VIRT=1 1373 | fi 1374 | fi 1375 | 1376 | HOSTAPD=$(which hostapd) 1377 | 1378 | if [[ ! -x "$HOSTAPD" ]]; then 1379 | echo "ERROR: hostapd not found." >&2 1380 | exit 1 1381 | fi 1382 | 1383 | if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^(8192[cd][ue]|8723a[sue])$ ]]; then 1384 | if ! strings "$HOSTAPD" | grep -m1 rtl871xdrv > /dev/null 2>&1; then 1385 | echo "ERROR: You need to patch your hostapd with rtl871xdrv patches." >&2 1386 | exit 1 1387 | fi 1388 | 1389 | if [[ $DRIVER != "rtl871xdrv" ]]; then 1390 | echo "WARN: Your adapter needs rtl871xdrv, enabling --driver=rtl871xdrv" >&2 1391 | DRIVER=rtl871xdrv 1392 | fi 1393 | fi 1394 | 1395 | if [[ "$SHARE_METHOD" != "nat" && "$SHARE_METHOD" != "bridge" && "$SHARE_METHOD" != "none" ]]; then 1396 | echo "ERROR: Wrong Internet sharing method" >&2 1397 | echo 1398 | usage >&2 1399 | exit 1 1400 | fi 1401 | 1402 | if [[ -n "$NEW_MACADDR" ]]; then 1403 | if ! is_macaddr "$NEW_MACADDR"; then 1404 | echo "ERROR: '${NEW_MACADDR}' is not a valid MAC address" >&2 1405 | exit 1 1406 | fi 1407 | 1408 | if ! is_unicast_macaddr "$NEW_MACADDR"; then 1409 | echo "ERROR: The first byte of MAC address (${NEW_MACADDR}) must be even" >&2 1410 | exit 1 1411 | fi 1412 | 1413 | if [[ $(get_all_macaddrs | grep -c ${NEW_MACADDR}) -ne 0 ]]; then 1414 | echo "WARN: MAC address '${NEW_MACADDR}' already exists. Because of this, you may encounter some problems" >&2 1415 | fi 1416 | fi 1417 | 1418 | if [[ "$SHARE_METHOD" != "none" ]]; then 1419 | MIN_REQUIRED_ARGS=2 1420 | else 1421 | MIN_REQUIRED_ARGS=1 1422 | fi 1423 | 1424 | if [[ $# -gt $MIN_REQUIRED_ARGS ]]; then 1425 | if [[ "$SHARE_METHOD" != "none" ]]; then 1426 | if [[ $# -ne 3 && $# -ne 4 ]]; then 1427 | usage >&2 1428 | exit 1 1429 | fi 1430 | INTERNET_IFACE="$2" 1431 | SSID="$3" 1432 | PASSPHRASE="$4" 1433 | else 1434 | if [[ $# -ne 2 && $# -ne 3 ]]; then 1435 | usage >&2 1436 | exit 1 1437 | fi 1438 | SSID="$2" 1439 | PASSPHRASE="$3" 1440 | fi 1441 | else 1442 | if [[ "$SHARE_METHOD" != "none" ]]; then 1443 | if [[ $# -ne 2 ]]; then 1444 | usage >&2 1445 | exit 1 1446 | fi 1447 | INTERNET_IFACE="$2" 1448 | fi 1449 | if tty -s; then 1450 | while :; do 1451 | read -p "SSID: " SSID 1452 | if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then 1453 | echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2 1454 | continue 1455 | fi 1456 | break 1457 | done 1458 | while :; do 1459 | if [[ $USE_PSK -eq 0 ]]; then 1460 | read -p "Passphrase: " -s PASSPHRASE 1461 | echo 1462 | if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then 1463 | echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2 1464 | continue 1465 | fi 1466 | read -p "Retype passphrase: " -s PASSPHRASE2 1467 | echo 1468 | if [[ "$PASSPHRASE" != "$PASSPHRASE2" ]]; then 1469 | echo "Passphrases do not match." 1470 | else 1471 | break 1472 | fi 1473 | else 1474 | read -p "PSK: " PASSPHRASE 1475 | echo 1476 | if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then 1477 | echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2 1478 | continue 1479 | fi 1480 | fi 1481 | done 1482 | else 1483 | read SSID 1484 | read PASSPHRASE 1485 | fi 1486 | fi 1487 | 1488 | if [[ "$SHARE_METHOD" != "none" ]] && ! is_interface $INTERNET_IFACE; then 1489 | echo "ERROR: '${INTERNET_IFACE}' is not an interface" >&2 1490 | exit 1 1491 | fi 1492 | 1493 | if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then 1494 | echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2 1495 | exit 1 1496 | fi 1497 | 1498 | if [[ $USE_PSK -eq 0 ]]; then 1499 | if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then 1500 | echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2 1501 | exit 1 1502 | fi 1503 | elif [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then 1504 | echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2 1505 | exit 1 1506 | fi 1507 | 1508 | if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^rtl[0-9].*$ ]]; then 1509 | if [[ -n "$PASSPHRASE" ]]; then 1510 | echo "WARN: Realtek drivers usually have problems with WPA1, enabling -w 2" >&2 1511 | WPA_VERSION=2 1512 | fi 1513 | echo "WARN: If AP doesn't work, please read: howto/realtek.md" >&2 1514 | fi 1515 | 1516 | if [[ $NO_VIRT -eq 1 && "$WIFI_IFACE" == "$INTERNET_IFACE" ]]; then 1517 | echo -n "ERROR: You can not share your connection from the same" >&2 1518 | echo " interface if you are using --no-virt option." >&2 1519 | exit 1 1520 | fi 1521 | 1522 | mutex_lock 1523 | trap "cleanup" EXIT 1524 | CONFDIR=$(mktemp -d /tmp/create_ap.${WIFI_IFACE}.conf.XXXXXXXX) 1525 | echo "Config dir: $CONFDIR" 1526 | echo "PID: $$" 1527 | echo $$ > $CONFDIR/pid 1528 | 1529 | # to make --list-running work from any user, we must give read 1530 | # permissions to $CONFDIR and $CONFDIR/pid 1531 | chmod 755 $CONFDIR 1532 | chmod 444 $CONFDIR/pid 1533 | 1534 | COMMON_CONFDIR=/tmp/create_ap.common.conf 1535 | mkdir -p $COMMON_CONFDIR 1536 | 1537 | if [[ "$SHARE_METHOD" == "nat" ]]; then 1538 | echo $INTERNET_IFACE > $CONFDIR/nat_internet_iface 1539 | cp_n /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding \ 1540 | $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding 1541 | fi 1542 | cp_n /proc/sys/net/ipv4/ip_forward $COMMON_CONFDIR 1543 | if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 1544 | cp_n /proc/sys/net/bridge/bridge-nf-call-iptables $COMMON_CONFDIR 1545 | fi 1546 | mutex_unlock 1547 | 1548 | if [[ "$SHARE_METHOD" == "bridge" ]]; then 1549 | if is_bridge_interface $INTERNET_IFACE; then 1550 | BRIDGE_IFACE=$INTERNET_IFACE 1551 | else 1552 | BRIDGE_IFACE=$(alloc_new_iface br) 1553 | fi 1554 | fi 1555 | 1556 | if [[ $USE_IWCONFIG -eq 0 ]]; then 1557 | iw dev ${WIFI_IFACE} set power_save off 1558 | fi 1559 | 1560 | if [[ $NO_VIRT -eq 0 ]]; then 1561 | VWIFI_IFACE=$(alloc_new_iface ap) 1562 | 1563 | # in NetworkManager 0.9.9 and above we can set the interface as unmanaged without 1564 | # the need of MAC address, so we set it before we create the virtual interface. 1565 | if networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]]; then 1566 | echo -n "Network Manager found, set ${VWIFI_IFACE} as unmanaged device... " 1567 | networkmanager_add_unmanaged ${VWIFI_IFACE} 1568 | # do not call networkmanager_wait_until_unmanaged because interface does not 1569 | # exist yet 1570 | echo "DONE" 1571 | fi 1572 | 1573 | if is_wifi_connected ${WIFI_IFACE}; then 1574 | WIFI_IFACE_FREQ=$(iw dev ${WIFI_IFACE} link | grep -i freq | awk '{print $2}') 1575 | WIFI_IFACE_CHANNEL=$(ieee80211_frequency_to_channel ${WIFI_IFACE_FREQ}) 1576 | echo -n "${WIFI_IFACE} is already associated with channel ${WIFI_IFACE_CHANNEL} (${WIFI_IFACE_FREQ} MHz)" 1577 | if is_5ghz_frequency $WIFI_IFACE_FREQ; then 1578 | FREQ_BAND=5 1579 | else 1580 | FREQ_BAND=2.4 1581 | fi 1582 | if [[ $WIFI_IFACE_CHANNEL -ne $CHANNEL ]]; then 1583 | echo ", fallback to channel ${WIFI_IFACE_CHANNEL}" 1584 | CHANNEL=$WIFI_IFACE_CHANNEL 1585 | else 1586 | echo 1587 | fi 1588 | fi 1589 | 1590 | VIRTDIEMSG="Maybe your WiFi adapter does not fully support virtual interfaces. 1591 | Try again with --no-virt." 1592 | echo -n "Creating a virtual WiFi interface... " 1593 | 1594 | if iw dev ${WIFI_IFACE} interface add ${VWIFI_IFACE} type __ap; then 1595 | # now we can call networkmanager_wait_until_unmanaged 1596 | networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]] && networkmanager_wait_until_unmanaged ${VWIFI_IFACE} 1597 | echo "${VWIFI_IFACE} created." 1598 | else 1599 | VWIFI_IFACE= 1600 | die "$VIRTDIEMSG" 1601 | fi 1602 | OLD_MACADDR=$(get_macaddr ${VWIFI_IFACE}) 1603 | if [[ -z "$NEW_MACADDR" && $(get_all_macaddrs | grep -c ${OLD_MACADDR}) -ne 1 ]]; then 1604 | NEW_MACADDR=$(get_new_macaddr ${VWIFI_IFACE}) 1605 | fi 1606 | WIFI_IFACE=${VWIFI_IFACE} 1607 | else 1608 | OLD_MACADDR=$(get_macaddr ${WIFI_IFACE}) 1609 | fi 1610 | 1611 | mutex_lock 1612 | echo $WIFI_IFACE > $CONFDIR/wifi_iface 1613 | chmod 444 $CONFDIR/wifi_iface 1614 | mutex_unlock 1615 | 1616 | if [[ -n "$COUNTRY" && $USE_IWCONFIG -eq 0 ]]; then 1617 | iw reg set "$COUNTRY" 1618 | fi 1619 | 1620 | can_transmit_to_channel ${WIFI_IFACE} ${CHANNEL} || die "Your adapter can not transmit to channel ${CHANNEL}, frequency band ${FREQ_BAND}GHz." 1621 | 1622 | if networkmanager_exists && ! networkmanager_iface_is_unmanaged ${WIFI_IFACE}; then 1623 | echo -n "Network Manager found, set ${WIFI_IFACE} as unmanaged device... " 1624 | networkmanager_add_unmanaged ${WIFI_IFACE} 1625 | 1626 | if networkmanager_is_running; then 1627 | networkmanager_wait_until_unmanaged ${WIFI_IFACE} 1628 | fi 1629 | 1630 | echo "DONE" 1631 | fi 1632 | 1633 | [[ $HIDDEN -eq 1 ]] && echo "Access Point's SSID is hidden!" 1634 | 1635 | [[ $MAC_FILTER -eq 1 ]] && echo "MAC address filtering is enabled!" 1636 | 1637 | [[ $ISOLATE_CLIENTS -eq 1 ]] && echo "Access Point's clients will be isolated!" 1638 | 1639 | # hostapd config 1640 | cat << EOF > $CONFDIR/hostapd.conf 1641 | beacon_int=100 1642 | ssid=${SSID} 1643 | interface=${WIFI_IFACE} 1644 | driver=${DRIVER} 1645 | channel=${CHANNEL} 1646 | ctrl_interface=$CONFDIR/hostapd_ctrl 1647 | ctrl_interface_group=0 1648 | ignore_broadcast_ssid=$HIDDEN 1649 | ap_isolate=$ISOLATE_CLIENTS 1650 | EOF 1651 | 1652 | if [[ -n "$COUNTRY" ]]; then 1653 | cat << EOF >> $CONFDIR/hostapd.conf 1654 | country_code=${COUNTRY} 1655 | ieee80211d=1 1656 | EOF 1657 | fi 1658 | 1659 | if [[ $FREQ_BAND == 2.4 ]]; then 1660 | echo "hw_mode=g" >> $CONFDIR/hostapd.conf 1661 | else 1662 | echo "hw_mode=a" >> $CONFDIR/hostapd.conf 1663 | fi 1664 | 1665 | if [[ $MAC_FILTER -eq 1 ]]; then 1666 | cat << EOF >> $CONFDIR/hostapd.conf 1667 | macaddr_acl=${MAC_FILTER} 1668 | accept_mac_file=${MAC_FILTER_ACCEPT} 1669 | EOF 1670 | fi 1671 | 1672 | if [[ $IEEE80211N -eq 1 ]]; then 1673 | cat << EOF >> $CONFDIR/hostapd.conf 1674 | ieee80211n=1 1675 | ht_capab=${HT_CAPAB} 1676 | EOF 1677 | fi 1678 | 1679 | if [[ $IEEE80211AC -eq 1 ]]; then 1680 | echo "ieee80211ac=1" >> $CONFDIR/hostapd.conf 1681 | fi 1682 | 1683 | if [[ -n "$VHT_CAPAB" ]]; then 1684 | echo "vht_capab=${VHT_CAPAB}" >> $CONFDIR/hostapd.conf 1685 | fi 1686 | 1687 | if [[ $IEEE80211N -eq 1 ]] || [[ $IEEE80211AC -eq 1 ]]; then 1688 | echo "wmm_enabled=1" >> $CONFDIR/hostapd.conf 1689 | fi 1690 | 1691 | if [[ -n "$PASSPHRASE" ]]; then 1692 | [[ "$WPA_VERSION" == "1+2" ]] && WPA_VERSION=3 1693 | if [[ $USE_PSK -eq 0 ]]; then 1694 | WPA_KEY_TYPE=passphrase 1695 | else 1696 | WPA_KEY_TYPE=psk 1697 | fi 1698 | cat << EOF >> $CONFDIR/hostapd.conf 1699 | wpa=${WPA_VERSION} 1700 | wpa_${WPA_KEY_TYPE}=${PASSPHRASE} 1701 | wpa_key_mgmt=WPA-PSK 1702 | wpa_pairwise=TKIP CCMP 1703 | rsn_pairwise=CCMP 1704 | EOF 1705 | fi 1706 | 1707 | if [[ "$SHARE_METHOD" == "bridge" ]]; then 1708 | echo "bridge=${BRIDGE_IFACE}" >> $CONFDIR/hostapd.conf 1709 | elif [[ $NO_DNSMASQ -eq 0 ]]; then 1710 | # dnsmasq config (dhcp + dns) 1711 | DNSMASQ_VER=$(dnsmasq -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+') 1712 | version_cmp $DNSMASQ_VER 2.63 1713 | if [[ $? -eq 1 ]]; then 1714 | DNSMASQ_BIND=bind-interfaces 1715 | else 1716 | DNSMASQ_BIND=bind-dynamic 1717 | fi 1718 | if [[ "$DHCP_DNS" == "gateway" ]]; then 1719 | DHCP_DNS="$GATEWAY" 1720 | fi 1721 | cat << EOF > $CONFDIR/dnsmasq.conf 1722 | listen-address=${GATEWAY} 1723 | ${DNSMASQ_BIND} 1724 | dhcp-range=${GATEWAY%.*}.1,${GATEWAY%.*}.254,255.255.255.0,24h 1725 | dhcp-option-force=option:router,${GATEWAY} 1726 | dhcp-option-force=option:dns-server,${DHCP_DNS} 1727 | EOF 1728 | MTU=$(get_mtu $INTERNET_IFACE) 1729 | [[ -n "$MTU" ]] && echo "dhcp-option-force=option:mtu,${MTU}" >> $CONFDIR/dnsmasq.conf 1730 | [[ $ETC_HOSTS -eq 0 ]] && echo no-hosts >> $CONFDIR/dnsmasq.conf 1731 | [[ -n "$ADDN_HOSTS" ]] && echo "addn-hosts=${ADDN_HOSTS}" >> $CONFDIR/dnsmasq.conf 1732 | if [[ "$SHARE_METHOD" == "none" && "$REDIRECT_TO_LOCALHOST" == "1" ]]; then 1733 | cat << EOF >> $CONFDIR/dnsmasq.conf 1734 | address=/#/$GATEWAY 1735 | EOF 1736 | fi 1737 | fi 1738 | 1739 | # initialize WiFi interface 1740 | if [[ $NO_VIRT -eq 0 && -n "$NEW_MACADDR" ]]; then 1741 | ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die "$VIRTDIEMSG" 1742 | fi 1743 | 1744 | ip link set down dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1745 | ip addr flush ${WIFI_IFACE} || die "$VIRTDIEMSG" 1746 | 1747 | if [[ $NO_VIRT -eq 1 && -n "$NEW_MACADDR" ]]; then 1748 | ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die 1749 | fi 1750 | 1751 | if [[ "$SHARE_METHOD" != "bridge" ]]; then 1752 | ip link set up dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1753 | ip addr add ${GATEWAY}/24 broadcast ${GATEWAY%.*}.255 dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1754 | fi 1755 | 1756 | # enable Internet sharing 1757 | if [[ "$SHARE_METHOD" != "none" ]]; then 1758 | echo "Sharing Internet using method: $SHARE_METHOD" 1759 | if [[ "$SHARE_METHOD" == "nat" ]]; then 1760 | iptables -w -t nat -I POSTROUTING -s ${GATEWAY%.*}.0/24 ! -o ${WIFI_IFACE} -j MASQUERADE || die 1761 | iptables -w -I FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT || die 1762 | iptables -w -I FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT || die 1763 | echo 1 > /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding || die 1764 | echo 1 > /proc/sys/net/ipv4/ip_forward || die 1765 | # to enable clients to establish PPTP connections we must 1766 | # load nf_nat_pptp module 1767 | modprobe nf_nat_pptp > /dev/null 2>&1 1768 | elif [[ "$SHARE_METHOD" == "bridge" ]]; then 1769 | # disable iptables rules for bridged interfaces 1770 | if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 1771 | echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables 1772 | fi 1773 | 1774 | # to initialize the bridge interface correctly we need to do the following: 1775 | # 1776 | # 1) save the IPs and route table of INTERNET_IFACE 1777 | # 2) if NetworkManager is running set INTERNET_IFACE as unmanaged 1778 | # 3) create BRIDGE_IFACE and attach INTERNET_IFACE to it 1779 | # 4) set the previously saved IPs and route table to BRIDGE_IFACE 1780 | # 1781 | # we need the above because BRIDGE_IFACE is the master interface from now on 1782 | # and it must know where is connected, otherwise connection is lost. 1783 | if ! is_bridge_interface $INTERNET_IFACE; then 1784 | echo -n "Create a bridge interface... " 1785 | OLD_IFS="$IFS" 1786 | IFS=$'\n' 1787 | 1788 | IP_ADDRS=( $(ip addr show $INTERNET_IFACE | grep -A 1 -E 'inet[[:blank:]]' | paste - -) ) 1789 | ROUTE_ADDRS=( $(ip route show dev $INTERNET_IFACE) ) 1790 | 1791 | IFS="$OLD_IFS" 1792 | 1793 | if networkmanager_is_running; then 1794 | networkmanager_add_unmanaged $INTERNET_IFACE 1795 | networkmanager_wait_until_unmanaged $INTERNET_IFACE 1796 | fi 1797 | 1798 | # create bridge interface 1799 | ip link add name $BRIDGE_IFACE type bridge || die 1800 | ip link set dev $BRIDGE_IFACE up || die 1801 | # set 0ms forward delay 1802 | echo -n 0 > /sys/class/net/$BRIDGE_IFACE/bridge/forward_delay 1803 | 1804 | # attach internet interface to bridge interface 1805 | ip link set dev $INTERNET_IFACE promisc on || die 1806 | ip link set dev $INTERNET_IFACE up || die 1807 | ip link set dev $INTERNET_IFACE master $BRIDGE_IFACE || die 1808 | 1809 | ip addr flush $INTERNET_IFACE 1810 | for x in "${IP_ADDRS[@]}"; do 1811 | x="${x/inet/}" 1812 | x="${x/secondary/}" 1813 | x="${x/dynamic/}" 1814 | x=$(echo $x | sed 's/\([0-9]\)sec/\1/g') 1815 | x="${x/${INTERNET_IFACE}/}" 1816 | ip addr add $x dev $BRIDGE_IFACE || die 1817 | done 1818 | 1819 | # remove any existing entries that were added from 'ip addr add' 1820 | ip route flush dev $INTERNET_IFACE 1821 | ip route flush dev $BRIDGE_IFACE 1822 | 1823 | # we must first add the entries that specify the subnets and then the 1824 | # gateway entry, otherwise 'ip addr add' will return an error 1825 | for x in "${ROUTE_ADDRS[@]}"; do 1826 | [[ "$x" == default* ]] && continue 1827 | ip route add $x dev $BRIDGE_IFACE || die 1828 | done 1829 | 1830 | for x in "${ROUTE_ADDRS[@]}"; do 1831 | [[ "$x" != default* ]] && continue 1832 | ip route add $x dev $BRIDGE_IFACE || die 1833 | done 1834 | 1835 | echo "$BRIDGE_IFACE created." 1836 | fi 1837 | fi 1838 | else 1839 | echo "No Internet sharing" 1840 | fi 1841 | 1842 | # start dhcp + dns (optional) 1843 | if [[ "$SHARE_METHOD" != "bridge" ]]; then 1844 | if [[ $NO_DNS -eq 0 ]]; then 1845 | DNS_PORT=5353 1846 | iptables -w -I INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT || die 1847 | iptables -w -I INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT || die 1848 | iptables -w -t nat -I PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ 1849 | -p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die 1850 | iptables -w -t nat -I PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ 1851 | -p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die 1852 | else 1853 | DNS_PORT=0 1854 | fi 1855 | 1856 | if [[ $NO_DNSMASQ -eq 0 ]]; then 1857 | iptables -w -I INPUT -p udp -m udp --dport 67 -j ACCEPT || die 1858 | 1859 | if which complain > /dev/null 2>&1; then 1860 | # openSUSE's apparmor does not allow dnsmasq to read files. 1861 | # remove restriction. 1862 | complain dnsmasq 1863 | fi 1864 | 1865 | umask 0033 1866 | dnsmasq -C $CONFDIR/dnsmasq.conf -x $CONFDIR/dnsmasq.pid -l $CONFDIR/dnsmasq.leases -p $DNS_PORT || die 1867 | umask $SCRIPT_UMASK 1868 | fi 1869 | fi 1870 | 1871 | # start access point 1872 | echo "hostapd command-line interface: hostapd_cli -p $CONFDIR/hostapd_ctrl" 1873 | 1874 | if [[ $NO_HAVEGED -eq 0 ]]; then 1875 | haveged_watchdog & 1876 | HAVEGED_WATCHDOG_PID=$! 1877 | fi 1878 | 1879 | # start hostapd (use stdbuf when available for no delayed output in programs that redirect stdout) 1880 | STDBUF_PATH=`which stdbuf` 1881 | if [ $? -eq 0 ]; then 1882 | STDBUF_PATH=$STDBUF_PATH" -oL" 1883 | fi 1884 | $STDBUF_PATH $HOSTAPD $HOSTAPD_DEBUG_ARGS $CONFDIR/hostapd.conf & 1885 | HOSTAPD_PID=$! 1886 | echo $HOSTAPD_PID > $CONFDIR/hostapd.pid 1887 | 1888 | if ! wait $HOSTAPD_PID; then 1889 | echo -e "\nError: Failed to run hostapd, maybe a program is interfering." >&2 1890 | if networkmanager_is_running; then 1891 | echo "If an error like 'n80211: Could not configure driver mode' was thrown" >&2 1892 | echo "try running the following before starting create_ap:" >&2 1893 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 1894 | echo " nmcli nm wifi off" >&2 1895 | else 1896 | echo " nmcli r wifi off" >&2 1897 | fi 1898 | echo " rfkill unblock wlan" >&2 1899 | fi 1900 | die 1901 | fi 1902 | 1903 | clean_exit 1904 | 1905 | # Local Variables: 1906 | # tab-width: 4 1907 | # indent-tabs-mode: nil 1908 | # End: 1909 | 1910 | # vim: et sts=4 sw=4 1911 | -------------------------------------------------------------------------------- /create_ap.conf: -------------------------------------------------------------------------------- 1 | CHANNEL=default 2 | GATEWAY=10.0.0.1 3 | WPA_VERSION=2 4 | ETC_HOSTS=0 5 | DHCP_DNS=gateway 6 | NO_DNS=0 7 | NO_DNSMASQ=0 8 | HIDDEN=0 9 | MAC_FILTER=0 10 | MAC_FILTER_ACCEPT=/etc/hostapd/hostapd.accept 11 | ISOLATE_CLIENTS=0 12 | SHARE_METHOD=nat 13 | IEEE80211N=0 14 | IEEE80211AC=0 15 | HT_CAPAB=[HT40+] 16 | VHT_CAPAB= 17 | DRIVER=nl80211 18 | NO_VIRT=0 19 | COUNTRY= 20 | FREQ_BAND=2.4 21 | NEW_MACADDR= 22 | DAEMONIZE=0 23 | NO_HAVEGED=0 24 | WIFI_IFACE=wlan0 25 | INTERNET_IFACE=eth0 26 | SSID=MyAccessPoint 27 | PASSPHRASE=12345678 28 | USE_PSK=0 29 | -------------------------------------------------------------------------------- /create_ap.openrc: -------------------------------------------------------------------------------- 1 | #!/sbin/openrc-run 2 | 3 | name=$RC_SVCNAME 4 | 5 | cfgfile=/etc/$RC_SVCNAME.conf 6 | pidfile=/run/$RC_SVCNAME.pid 7 | 8 | command=/usr/bin/create_ap 9 | command_args="--config $cfgfile" 10 | command_args_background="--daemon --pidfile $pidfile" 11 | stopsig=USR1 12 | -------------------------------------------------------------------------------- /create_ap.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Create AP Service 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/create_ap --config /etc/create_ap.conf 8 | KillSignal=SIGINT 9 | Restart=on-failure 10 | RestartSec=5 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /howto/realtek.md: -------------------------------------------------------------------------------- 1 | ## Try this first 2 | 3 | If you are facing any problems with Realtek adapters (e.g. Edimax EW-7811Un) 4 | first try to run create_ap with `-w 2` (i.e. use WPA2 only) or use it 5 | without passphrase. If you are still facing any problems or you want to 6 | also use WPA1, then follow the instructions below. 7 | 8 | NOTE: The instructions below are only valid for Realtek adapters with 8192 chipset. 9 | 10 | ## Before installation 11 | 12 | If you're using ArchLinux, run: 13 | 14 | ``` 15 | pacman -S base-devel linux-headers dkms git 16 | pacman -R hostapd 17 | ``` 18 | 19 | If you're using Debian, Ubuntu, or any Debian-based distribution, run: 20 | 21 | ``` 22 | apt-get install build-essential linux-headers-generic dkms git 23 | apt-get remove hostapd 24 | apt-get build-dep hostapd 25 | ``` 26 | 27 | ## Install driver 28 | 29 | The driver in the mainline of Linux kernel doesn't work well with the 8192 adapters. 30 | For this reason you need to install the driver that is provided from Realtek. Their 31 | driver can not be compiled with newer kernels, but since it was an open-source 32 | release under GPL license some people were able to fixed it and make it compile. 33 | 34 | With the following commands you can install a fixed version of Realtek's driver: 35 | 36 | ``` 37 | git clone https://github.com/pvaret/rtl8192cu-fixes.git 38 | dkms add rtl8192cu-fixes 39 | dkms install 8192cu/1.9 40 | cp rtl8192cu-fixes/blacklist-native-rtl8192.conf /etc/modprobe.d 41 | cp rtl8192cu-fixes/8192cu-disable-power-management.conf /etc/modprobe.d 42 | ``` 43 | 44 | After installation, unload the previous driver and load the new one, or just reboot. 45 | 46 | ## Install hostapd 47 | 48 | Realtek's driver is using an old subsystem which is called `wireless-extensions` 49 | (or `wext`). Hostapd works only with the new subsystem (which is called `nl80211`). 50 | For this reason Realtek wrote a patch for hostapd. You can install it with the 51 | following commands: 52 | 53 | If you have ArchLinux install [hostapd-rtl871xdrv](https://aur.archlinux.org/packages/hostapd-rtl871xdrv) 54 | from AUR or just run: 55 | 56 | ``` 57 | yaourt -S hostapd-rtl871xdrv 58 | ``` 59 | 60 | If you're using any other distribution, run: 61 | 62 | ``` 63 | git clone https://github.com/pritambaral/hostapd-rtl871xdrv.git 64 | wget http://w1.fi/releases/hostapd-2.2.tar.gz 65 | tar zxvf hostapd-2.2.tar.gz 66 | cd hostapd-2.2 67 | patch -p1 -i ../hostapd-rtl871xdrv/rtlxdrv.patch 68 | cp ../hostapd-rtl871xdrv/driver_* src/drivers 69 | cd hostapd 70 | cp defconfig .config 71 | echo CONFIG_DRIVER_RTW=y >> .config 72 | make 73 | make install 74 | ``` 75 | --------------------------------------------------------------------------------