├── create_ap.openrc ├── create_ap.service ├── create_ap.resume ├── LICENSE ├── Makefile ├── howto └── realtek.md ├── README.md ├── create_ap.conf ├── bash_completion └── create_ap /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 | -------------------------------------------------------------------------------- /create_ap.resume: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # After resume, restart create_ap service (if it was already running) 5 | # See: 6 | # https://www.freedesktop.org/software/systemd/man/systemd-suspend.service.html 7 | 8 | case "$1" in 9 | pre) ;; 10 | post) /bin/systemctl try-restart create_ap.service ;; 11 | esac 12 | -------------------------------------------------------------------------------- /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 --backup=existing --suffix=.orig 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 | [ ! -d /lib/systemd/system-sleep ] || install -Dm755 create_ap.resume $(DESTDIR)/lib/systemd/system-sleep/create_ap.resume 14 | [ ! -e /sbin/openrc-run ] || install -Dm755 create_ap.openrc $(DESTDIR)/etc/init.d/create_ap 15 | install -Dm644 bash_completion $(DESTDIR)$(PREFIX)/share/bash-completion/completions/create_ap 16 | install -Dm644 README.md $(DESTDIR)$(PREFIX)/share/doc/create_ap/README.md 17 | 18 | uninstall: 19 | rm -f $(DESTDIR)$(BINDIR)/create_ap 20 | rm -f $(DESTDIR)/etc/create_ap.conf 21 | [ ! -f /lib/systemd/system/create_ap.service ] || rm -f $(DESTDIR)$(PREFIX)/lib/systemd/system/create_ap.service 22 | [ ! -f /lib/systemd/system-sleep/create_ap.resume ] || rm -f $(DESTDIR)/lib/systemd/system/create_ap.resume 23 | [ ! -e /sbin/openrc-run ] || rm -f $(DESTDIR)/etc/init.d/create_ap 24 | rm -f $(DESTDIR)$(PREFIX)/share/bash-completion/completions/create_ap 25 | rm -f $(DESTDIR)$(PREFIX)/share/doc/create_ap/README.md 26 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is a fork of the no-longer-maintained [oblique/create_ap](//github.com/oblique/create_ap). 2 | 3 | 4 | ## Features 5 | * Create an AP (Access Point) at any channel. 6 | * Choose one of the following encryptions: WPA, WPA2, WPA/WPA2, Open (no encryption). 7 | * Hide your SSID. 8 | * Enable [Wi-Fi Protected Setup](https://en.wikipedia.org/wiki/Wi-Fi_Protected_Setup) (WPS) with push-button or PIN methods. 9 | * Disable communication between clients (client isolation). 10 | * IEEE 802.11n & 802.11ac support 11 | * Internet sharing methods: NATed or Bridged or None (no Internet sharing). 12 | * Choose the AP Gateway IP (only for 'NATed' and 'None' Internet sharing methods). 13 | * You can create an AP with the same interface you are getting your Internet connection. 14 | * You can pass your SSID and password through pipe or through arguments (see examples). 15 | 16 | 17 | ## Dependencies 18 | ### General 19 | * bash (to run this script) 20 | * util-linux (for getopt) 21 | * procps or procps-ng 22 | * hostapd 23 | * iproute2 24 | * iw 25 | * iwconfig (you only need this if 'iw' can not recognize your adapter) 26 | * haveged (optional) 27 | 28 | ### For 'NATed' or 'None' Internet sharing method 29 | * dnsmasq 30 | * iptables 31 | 32 | 33 | ## Installation 34 | ### Generic 35 | git clone https://github.com/oblique/create_ap 36 | cd create_ap 37 | make install 38 | 39 | ### ArchLinux 40 | pacman -S create_ap 41 | 42 | ### Gentoo 43 | emerge layman 44 | layman -f -a jorgicio 45 | emerge net-wireless/create_ap 46 | 47 | ## Examples 48 | ### No passphrase (open network): 49 | create_ap wlan0 eth0 MyAccessPoint 50 | 51 | ### WPA + WPA2 passphrase: 52 | create_ap wlan0 eth0 MyAccessPoint MyPassPhrase 53 | 54 | ### WPS Push Button or PIN entry: 55 | create_ap wlan0 eth0 MyAccessPoint MyPassPhrase 56 | create_ap --wps-pbc wlan0 # Device will have 2 minutes to connect 57 | create_ap --wps-pin wlan0,12345678 # Enroll PIN 12345678 for device requesting it 58 | 59 | ### AP without Internet sharing: 60 | create_ap -n wlan0 MyAccessPoint MyPassPhrase 61 | 62 | ### Bridged Internet sharing: 63 | create_ap -m bridge wlan0 eth0 MyAccessPoint MyPassPhrase 64 | 65 | ### Bridged Internet sharing (pre-configured bridge interface): 66 | create_ap -m bridge wlan0 br0 MyAccessPoint MyPassPhrase 67 | 68 | ### Internet sharing from the same WiFi interface: 69 | create_ap wlan0 wlan0 MyAccessPoint MyPassPhrase 70 | 71 | ### Choose a different WiFi adapter driver 72 | create_ap --driver rtl871xdrv wlan0 eth0 MyAccessPoint MyPassPhrase 73 | 74 | ### No passphrase (open network) using pipe: 75 | echo -e "MyAccessPoint" | create_ap wlan0 eth0 76 | 77 | ### WPA + WPA2 passphrase using pipe: 78 | echo -e "MyAccessPoint\nMyPassPhrase" | create_ap wlan0 eth0 79 | 80 | ### Enable IEEE 802.11n 81 | create_ap --ieee80211n --ht_capab '[HT40+]' wlan0 eth0 MyAccessPoint MyPassPhrase 82 | 83 | ### Client Isolation: 84 | create_ap --isolate-clients wlan0 eth0 MyAccessPoint MyPassPhrase 85 | 86 | ## Systemd service 87 | Using the persistent [systemd](https://wiki.archlinux.org/index.php/systemd#Basic_systemctl_usage) service 88 | ### Start service immediately: 89 | systemctl start create_ap 90 | 91 | ### Start on boot: 92 | systemctl enable create_ap 93 | 94 | 95 | ## License 96 | FreeBSD 97 | 98 | -------------------------------------------------------------------------------- /create_ap.conf: -------------------------------------------------------------------------------- 1 | # Configuration file for create_ap (https://github.com/oblique/create_ap) 2 | 3 | ########## Access Point Information ########## 4 | 5 | # Name of access point 6 | SSID=MyAccessPoint 7 | # Pre-shared key or passphrase of access point (see USE_PSK option) 8 | PASSPHRASE=12345678 9 | # Type of key in PASSPHRASE 10 | # - Set to "0" if the content of PASSPHRASE is an 8..63 character long ASCII 11 | # passphrase (i.e. what you type when you connect) 12 | # - Set to "1" if the content of PASSPHRASE is a 256-bit secret in hex format 13 | # (64 hex digits) 14 | USE_PSK=0 15 | # Set whether the access point should hide its SSID or not 16 | HIDDEN=0 17 | 18 | ########## Sharing Options ########## 19 | 20 | # The network interface where create_ap creates access point 21 | WIFI_IFACE=wlan0 22 | # The network interface to be shared 23 | INTERNET_IFACE=eth0 24 | # Method to share the network 25 | # Available options: "nat", "bridge" 26 | SHARE_METHOD=nat 27 | # IP address of the gateway in NAT mode (the subnet is /24) 28 | GATEWAY=10.0.0.1 29 | # Set whether IPv6 should be enabled 30 | IPV6=0 31 | # Set whether the connected clients can see each other or not 32 | ISOLATE_CLIENTS=0 33 | 34 | ########## Wireless Options ########## 35 | 36 | # The channel number on which the access point is served 37 | # Set to "default" will set channel to 1 in 2.4Ghz band and 36 in 5GHz band 38 | CHANNEL=default 39 | # Wireless frequency (band) 40 | # Available options: "2.4", "5" 41 | FREQ_BAND=2.4 42 | # Country code (ISO/IEC 3166-1) to set regulatory domain 43 | COUNTRY= 44 | # WPA version 45 | # Available options: "1", "2", "1+2" (or "3") 46 | WPA_VERSION=2 47 | # Whether IEEE 802.11n (HT) is enabled 48 | IEEE80211N=0 49 | # Whether IEEE 802.11ac (VHT) is enabled 50 | IEEE80211AC=0 51 | # Whether Wi-Fi Protected Setup (WPS) is enabled 52 | # Use '--wps-pbc' to simulate the push button, or '--wps-pin' for PIN entry. 53 | WPS=0 54 | # HT / VHT capabilities 55 | # For all possible options please look at 56 | # https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf 57 | HT_CAPAB=[HT40+] 58 | VHT_CAPAB= 59 | # Beacon interval, and DTIM period (https://en.wikipedia.org/wiki/DTIM) 60 | # in multiples of the beacon interval. 61 | BEACON_INTERVAL=100 62 | DTIM_INTERVAL=2 63 | 64 | ########## Network Options ########## 65 | 66 | # DNS servers to be pushed by DHCP server; separate multiple with , 67 | # Include "gateway" to use the gateway itself 68 | DHCP_DNS=gateway 69 | # IPv6 DNS servers to be pushed by DHCP server; enclose IPv6 70 | # addresses in [], and separate multiple with , 71 | # Include "gateway" to use the gateway itself 72 | DHCP_DNS6= 73 | # Set to 1 to disable DNS 74 | NO_DNS=0 75 | # Set to 1 to disable dnsmasq completely (DHCP and DNS) 76 | NO_DNSMASQ=0 77 | # Set to 1 to make dnsmasq DNS server include /etc/hosts in its results 78 | ETC_HOSTS=0 79 | # Additional hosts file that dnsmasq should include in its results 80 | ADDN_HOSTS= 81 | # Set to 1 to enable MAC address based authentication 82 | MAC_FILTER=0 83 | # Acceptance list for MAC address based authentication 84 | MAC_FILTER_ACCEPT=/etc/hostapd/hostapd.accept 85 | # New MAC address for the access point, leave it blank to keep it as-is 86 | NEW_MACADDR= 87 | 88 | ########## Miscellaneous ########## 89 | 90 | # Specify the driver to use 91 | DRIVER=nl80211 92 | # Set whether create_ap should run in the background 93 | DAEMONIZE=0 94 | # Set to 1 to disable virtual interface creation 95 | NO_VIRT=0 96 | # Set to 1 to disable haveged random number generation 97 | NO_HAVEGED=0 98 | -------------------------------------------------------------------------------- /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 " --wps Enable WPS (Wireless Protected Setup) with push button (PBC) and PIN modes" 44 | echo " --psk Use 64 hex digits pre-shared-key instead of passphrase" 45 | echo " --hidden Make the Access Point hidden (do not broadcast the SSID)" 46 | echo " --mac-filter Enable MAC address filtering" 47 | echo " --mac-filter-accept Location of MAC address filter list (defaults to /etc/hostapd/hostapd.accept)" 48 | echo " --redirect-to-localhost If -n is set, redirect every web request to localhost (useful for public information networks)" 49 | echo " --hostapd-debug With level between 1 and 2, passes arguments -d or -dd to hostapd for debugging." 50 | echo " --isolate-clients Disable communication between clients" 51 | echo " --ieee80211n Enable IEEE 802.11n (HT)" 52 | echo " --ieee80211ac Enable IEEE 802.11ac (VHT)" 53 | echo " --ht_capab HT capabilities (default: [HT40+])" 54 | echo " --vht_capab VHT capabilities" 55 | echo " --beacon-interval Set beacon interval in milliseconds (default 100)" 56 | echo " --dtim-period Set DTIM period in numbers of beacons (default 2)" 57 | echo " --country Set two-letter country code for 802.11d regulatory domain (example: US)" 58 | echo " --freq-band Set frequency band. Valid inputs: 2.4, 5 (default: 2.4)" 59 | echo " --driver Choose your WiFi adapter driver (default: nl80211)" 60 | echo " --no-virt Do not create virtual interface" 61 | echo " --no-haveged Do not run 'haveged' automatically when needed" 62 | echo " --fix-unmanaged If NetworkManager shows your interface as unmanaged after you" 63 | echo " close create_ap, then use this option to switch your interface" 64 | echo " back to managed" 65 | echo " --mac Set MAC address of WiFi interface" 66 | echo " --dhcp-dns Set DNS returned by DHCP (default is gateway; see also --no-dns below)" 67 | echo " Multiple servers may be specified (example: 'gateway,8.8.8.8')" 68 | echo " --dhcp-dns6 Set DNSv6 returned by DHCP (default is gateway; see also --no-dns below)" 69 | echo " Multiple servers may be specified (example: 'gateway,2001:4860:4860::8888')" 70 | echo " --daemon Run create_ap in the background" 71 | echo " --pidfile Save daemon PID to file" 72 | echo " --logfile Save daemon messages to file" 73 | echo " --stop Send stop command to an already running create_ap. For an " 74 | echo " you can put the PID of create_ap or the WiFi interface. You can" 75 | echo " get them with --list-running" 76 | echo " --list-running Show the create_ap processes that are already running" 77 | echo " --list-clients List the clients connected to create_ap instance associated with ." 78 | echo " For an you can put the PID of create_ap or the WiFi interface." 79 | echo " If virtual WiFi interface was created, then use that one." 80 | echo " You can get them with --list-running" 81 | echo " --wps-pbc Simulate WPS push button (PBC)" 82 | echo " --wps-pin , Enroll PIN for WPS" 83 | echo " --mkconfig Store configs in conf_file" 84 | echo " --config Load configs from conf_file" 85 | echo 86 | echo "Non-bridging options:" 87 | echo " --no-dns Disable dnsmasq DNS server (prevents clients from using Access Point for DNS)" 88 | echo " --no-dnsmasq Disable dnsmasq completely (implies --no-dns and also disables DHCP)" 89 | echo " -g IPv4 Gateway address for the Access Point (default: 192.168.12.1)" 90 | echo " --ipv6 Enable IPv6 support" 91 | echo " -d DNS server will include /etc/hosts in its results" 92 | echo " -e DNS server will include additional hosts file in its results" 93 | echo 94 | echo "Useful informations:" 95 | echo " * If you're not using the --no-virt option, then you can create an AP with the same" 96 | echo " interface you are getting your Internet connection." 97 | echo " * You can pass your SSID and password through pipe or through arguments (see examples)." 98 | echo " * With bridge method, if the is not a bridge interface, then" 99 | echo " a bridge interface is created automatically." 100 | echo " * With other methods, the default behavior is to run dnsmasq as a DNS and DHCP server." 101 | echo " It will assign client addresses in the /24 address range matching the gateway." 102 | echo 103 | echo "Examples:" 104 | echo " "$PROGNAME" wlan0 eth0 MyAccessPoint MyPassPhrase" 105 | echo " echo -e 'MyAccessPoint\nMyPassPhrase' | "$PROGNAME" wlan0 eth0" 106 | echo " "$PROGNAME" wlan0 eth0 MyAccessPoint" 107 | echo " echo 'MyAccessPoint' | "$PROGNAME" wlan0 eth0" 108 | echo " "$PROGNAME" wlan0 wlan0 MyAccessPoint MyPassPhrase" 109 | echo " "$PROGNAME" -n wlan0 MyAccessPoint MyPassPhrase" 110 | echo " "$PROGNAME" -m bridge wlan0 eth0 MyAccessPoint MyPassPhrase" 111 | echo " "$PROGNAME" -m bridge wlan0 br0 MyAccessPoint MyPassPhrase" 112 | echo " "$PROGNAME" --driver rtl871xdrv wlan0 eth0 MyAccessPoint MyPassPhrase" 113 | echo " "$PROGNAME" --daemon wlan0 eth0 MyAccessPoint MyPassPhrase" 114 | echo " "$PROGNAME" --stop wlan0" 115 | } 116 | 117 | # Busybox polyfills 118 | if cp --help 2>&1 | grep -q -- --no-clobber; then 119 | cp_n() { 120 | cp -n "$@" 121 | } 122 | else 123 | cp_n() { 124 | yes n | cp -i "$@" 125 | } 126 | fi 127 | 128 | # on success it echos a non-zero unused FD 129 | # on error it echos 0 130 | get_avail_fd() { 131 | local x 132 | for x in $(seq 1 $(ulimit -n)); do 133 | if [[ ! -a "/proc/$BASHPID/fd/$x" ]]; then 134 | echo $x 135 | return 136 | fi 137 | done 138 | echo 0 139 | } 140 | 141 | # lock file for the mutex counter 142 | COUNTER_LOCK_FILE=/tmp/create_ap.$$.lock 143 | 144 | cleanup_lock() { 145 | rm -f $COUNTER_LOCK_FILE 146 | } 147 | 148 | init_lock() { 149 | local LOCK_FILE=/tmp/create_ap.all.lock 150 | 151 | # we initialize only once 152 | [[ $LOCK_FD -ne 0 ]] && return 0 153 | 154 | LOCK_FD=$(get_avail_fd) 155 | [[ $LOCK_FD -eq 0 ]] && return 1 156 | 157 | # open/create lock file with write access for all users 158 | # otherwise normal users will not be able to use it. 159 | # to avoid race conditions on creation, we need to 160 | # use umask to set the permissions. 161 | umask 0555 162 | eval "exec $LOCK_FD>$LOCK_FILE" > /dev/null 2>&1 || return 1 163 | umask $SCRIPT_UMASK 164 | 165 | # there is a case where lock file was created from a normal 166 | # user. change the owner to root as soon as we can. 167 | [[ $(id -u) -eq 0 ]] && chown 0:0 $LOCK_FILE 168 | 169 | # create mutex counter lock file 170 | echo 0 > $COUNTER_LOCK_FILE 171 | 172 | return $? 173 | } 174 | 175 | # recursive mutex lock for all create_ap processes 176 | mutex_lock() { 177 | local counter_mutex_fd 178 | local counter 179 | 180 | # lock local mutex and read counter 181 | counter_mutex_fd=$(get_avail_fd) 182 | if [[ $counter_mutex_fd -ne 0 ]]; then 183 | eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE" 184 | flock $counter_mutex_fd 185 | read -u $counter_mutex_fd counter 186 | else 187 | echo "Failed to lock mutex counter" >&2 188 | return 1 189 | fi 190 | 191 | # lock global mutex and increase counter 192 | [[ $counter -eq 0 ]] && flock $LOCK_FD 193 | counter=$(( $counter + 1 )) 194 | 195 | # write counter and unlock local mutex 196 | echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd 197 | eval "exec ${counter_mutex_fd}<&-" 198 | return 0 199 | } 200 | 201 | # recursive mutex unlock for all create_ap processes 202 | mutex_unlock() { 203 | local counter_mutex_fd 204 | local counter 205 | 206 | # lock local mutex and read counter 207 | counter_mutex_fd=$(get_avail_fd) 208 | if [[ $counter_mutex_fd -ne 0 ]]; then 209 | eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE" 210 | flock $counter_mutex_fd 211 | read -u $counter_mutex_fd counter 212 | else 213 | echo "Failed to lock mutex counter" >&2 214 | return 1 215 | fi 216 | 217 | # decrease counter and unlock global mutex 218 | if [[ $counter -gt 0 ]]; then 219 | counter=$(( $counter - 1 )) 220 | [[ $counter -eq 0 ]] && flock -u $LOCK_FD 221 | fi 222 | 223 | # write counter and unlock local mutex 224 | echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd 225 | eval "exec ${counter_mutex_fd}<&-" 226 | return 0 227 | } 228 | 229 | # it takes 2 arguments 230 | # returns: 231 | # 0 if v1 (1st argument) and v2 (2nd argument) are the same 232 | # 1 if v1 is less than v2 233 | # 2 if v1 is greater than v2 234 | version_cmp() { 235 | local V1 V2 VN x 236 | [[ ! $1 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!" 237 | [[ ! $2 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!" 238 | 239 | V1=( $(echo $1 | tr '.' ' ') ) 240 | V2=( $(echo $2 | tr '.' ' ') ) 241 | VN=${#V1[@]} 242 | [[ $VN -lt ${#V2[@]} ]] && VN=${#V2[@]} 243 | 244 | for ((x = 0; x < $VN; x++)); do 245 | [[ ${V1[x]} -lt ${V2[x]} ]] && return 1 246 | [[ ${V1[x]} -gt ${V2[x]} ]] && return 2 247 | done 248 | 249 | return 0 250 | } 251 | 252 | USE_IWCONFIG=0 253 | 254 | is_interface() { 255 | [[ -z "$1" ]] && return 1 256 | [[ -d "/sys/class/net/${1}" ]] 257 | } 258 | 259 | is_wifi_interface() { 260 | which iw > /dev/null 2>&1 && iw dev $1 info > /dev/null 2>&1 && return 0 261 | if which iwconfig > /dev/null 2>&1 && iwconfig $1 > /dev/null 2>&1; then 262 | USE_IWCONFIG=1 263 | return 0 264 | fi 265 | return 1 266 | } 267 | 268 | is_bridge_interface() { 269 | [[ -z "$1" ]] && return 1 270 | [[ -d "/sys/class/net/${1}/bridge" ]] 271 | } 272 | 273 | get_phy_device() { 274 | local x 275 | for x in /sys/class/ieee80211/*; do 276 | [[ ! -e "$x" ]] && continue 277 | if [[ "${x##*/}" = "$1" ]]; then 278 | echo $1 279 | return 0 280 | elif [[ -e "$x/device/net/$1" ]]; then 281 | echo ${x##*/} 282 | return 0 283 | elif [[ -e "$x/device/net:$1" ]]; then 284 | echo ${x##*/} 285 | return 0 286 | fi 287 | done 288 | echo "Failed to get phy interface" >&2 289 | return 1 290 | } 291 | 292 | get_adapter_info() { 293 | local PHY 294 | PHY=$(get_phy_device "$1") 295 | [[ $? -ne 0 ]] && return 1 296 | iw phy $PHY info 297 | } 298 | 299 | get_adapter_kernel_module() { 300 | local MODULE 301 | MODULE=$(readlink -f "/sys/class/net/$1/device/driver/module") 302 | echo ${MODULE##*/} 303 | } 304 | 305 | can_be_sta_and_ap() { 306 | # iwconfig does not provide this information, assume false 307 | [[ $USE_IWCONFIG -eq 1 ]] && return 1 308 | if [[ "$(get_adapter_kernel_module "$1")" == "brcmfmac" ]]; then 309 | echo "WARN: brmfmac driver doesn't work properly with virtual interfaces and" >&2 310 | echo " it can cause kernel panic. For this reason we disallow virtual" >&2 311 | echo " interfaces for your adapter." >&2 312 | echo " For more info: https://github.com/oblique/create_ap/issues/203" >&2 313 | return 1 314 | fi 315 | get_adapter_info "$1" | grep -E '{.* managed.* AP.*}' > /dev/null 2>&1 && return 0 316 | get_adapter_info "$1" | grep -E '{.* AP.* managed.*}' > /dev/null 2>&1 && return 0 317 | return 1 318 | } 319 | 320 | can_be_ap() { 321 | # iwconfig does not provide this information, assume true 322 | [[ $USE_IWCONFIG -eq 1 ]] && return 0 323 | get_adapter_info "$1" | grep -E '\* AP$' > /dev/null 2>&1 && return 0 324 | return 1 325 | } 326 | 327 | can_transmit_to_channel() { 328 | local IFACE CHANNEL_NUM CHANNEL_INFO 329 | IFACE=$1 330 | CHANNEL_NUM=$2 331 | 332 | if [[ $USE_IWCONFIG -eq 0 ]]; then 333 | if [[ $FREQ_BAND == 2.4 ]]; then 334 | CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " 24[0-9][0-9] MHz \[${CHANNEL_NUM}\]") 335 | else 336 | CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " \(49[0-9][0-9]\|5[0-9]\{3\}\) MHz \[${CHANNEL_NUM}\]") 337 | fi 338 | [[ -z "${CHANNEL_INFO}" ]] && return 1 339 | [[ "${CHANNEL_INFO}" == *no\ IR* ]] && return 1 340 | [[ "${CHANNEL_INFO}" == *disabled* ]] && return 1 341 | return 0 342 | else 343 | CHANNEL_NUM=$(printf '%02d' ${CHANNEL_NUM}) 344 | CHANNEL_INFO=$(iwlist ${IFACE} channel | grep -E "Channel[[:blank:]]${CHANNEL_NUM}[[:blank:]]?:") 345 | [[ -z "${CHANNEL_INFO}" ]] && return 1 346 | return 0 347 | fi 348 | } 349 | 350 | # taken from iw/util.c 351 | ieee80211_frequency_to_channel() { 352 | local FREQ=$1 353 | if [[ $FREQ -eq 2484 ]]; then 354 | echo 14 355 | elif [[ $FREQ -lt 2484 ]]; then 356 | echo $(( ($FREQ - 2407) / 5 )) 357 | elif [[ $FREQ -ge 4910 && $FREQ -le 4980 ]]; then 358 | echo $(( ($FREQ - 4000) / 5 )) 359 | elif [[ $FREQ -le 45000 ]]; then 360 | echo $(( ($FREQ - 5000) / 5 )) 361 | elif [[ $FREQ -ge 58320 && $FREQ -le 64800 ]]; then 362 | echo $(( ($FREQ - 56160) / 2160 )) 363 | else 364 | echo 0 365 | fi 366 | } 367 | 368 | is_5ghz_frequency() { 369 | [[ $1 =~ ^(49[0-9]{2})|(5[0-9]{3})$ ]] 370 | } 371 | 372 | is_wifi_connected() { 373 | if [[ $USE_IWCONFIG -eq 0 ]]; then 374 | iw dev "$1" link 2>&1 | grep -E '^Connected to' > /dev/null 2>&1 && return 0 375 | else 376 | iwconfig "$1" 2>&1 | grep -E 'Access Point: [0-9a-fA-F]{2}:' > /dev/null 2>&1 && return 0 377 | fi 378 | return 1 379 | } 380 | 381 | is_macaddr() { 382 | echo "$1" | grep -E "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$" > /dev/null 2>&1 383 | } 384 | 385 | is_unicast_macaddr() { 386 | local x 387 | is_macaddr "$1" || return 1 388 | x=$(echo "$1" | cut -d: -f1) 389 | x=$(printf '%d' "0x${x}") 390 | [[ $(expr $x % 2) -eq 0 ]] 391 | } 392 | 393 | get_macaddr() { 394 | is_interface "$1" || return 395 | cat "/sys/class/net/${1}/address" 396 | } 397 | 398 | get_mtu() { 399 | is_interface "$1" || return 400 | cat "/sys/class/net/${1}/mtu" 401 | } 402 | 403 | alloc_new_iface() { 404 | local prefix=$1 405 | local i=0 406 | 407 | mutex_lock 408 | while :; do 409 | if ! is_interface $prefix$i && [[ ! -f $COMMON_CONFDIR/ifaces/$prefix$i ]]; then 410 | mkdir -p $COMMON_CONFDIR/ifaces 411 | touch $COMMON_CONFDIR/ifaces/$prefix$i 412 | echo $prefix$i 413 | mutex_unlock 414 | return 415 | fi 416 | i=$((i + 1)) 417 | done 418 | mutex_unlock 419 | } 420 | 421 | dealloc_iface() { 422 | rm -f $COMMON_CONFDIR/ifaces/$1 423 | } 424 | 425 | get_all_macaddrs() { 426 | cat /sys/class/net/*/address 427 | } 428 | 429 | get_new_macaddr() { 430 | local OLDMAC NEWMAC LAST_BYTE i 431 | OLDMAC=$(get_macaddr "$1") 432 | LAST_BYTE=$(printf %d 0x${OLDMAC##*:}) 433 | mutex_lock 434 | for i in {1..255}; do 435 | NEWMAC="${OLDMAC%:*}:$(printf %02x $(( ($LAST_BYTE + $i) % 256 )))" 436 | (get_all_macaddrs | grep "$NEWMAC" > /dev/null 2>&1) || break 437 | done 438 | mutex_unlock 439 | echo $NEWMAC 440 | } 441 | 442 | get_ipv6addr() { 443 | ip -6 addr show "$1" scope global | sed -e 's/ /\n/g' | sed -ne '/inet6/{n;p}' | tail -n1 444 | } 445 | 446 | get_new_ipv6addr() { 447 | local OLDIP NEWIP LAST_CHUNK i 448 | OLDIP=$(get_ipv6addr "$1") 449 | OLDIP="${OLDIP%/*}" 450 | mutex_lock 451 | for i in {1..65535}; do 452 | if [[ "$OLDIP" == ::* ]]; then 453 | NEWIP="${OLDIP}::$(printf %04x $i)" 454 | else 455 | LAST_CHUNK=$(printf %d 0x${OLDIP##*:}) 456 | NEWIP="${OLDIP%:*}:$(printf %02x $(( ($LAST_CHUNK + $i) % 65536 )))" 457 | ip -6 addr show "$1" scope global | sed -e 's/ /\n/g' | sed -ne '/inet6/{n;p}' | grep "$NEWIP" > /dev/null 2>&1 || break 458 | fi 459 | done 460 | mutex_unlock 461 | echo $NEWIP 462 | } 463 | 464 | # start haveged when needed 465 | haveged_watchdog() { 466 | local show_warn=1 467 | while :; do 468 | mutex_lock 469 | if [[ $(cat /proc/sys/kernel/random/entropy_avail) -lt 1000 ]]; then 470 | if ! which haveged > /dev/null 2>&1; then 471 | if [[ $show_warn -eq 1 ]]; then 472 | echo "WARN: Low entropy detected. We recommend you to install \`haveged'" 473 | show_warn=0 474 | fi 475 | elif ! pidof haveged > /dev/null 2>&1; then 476 | echo "Low entropy detected, starting haveged" 477 | # boost low-entropy 478 | haveged -w 1024 -p $COMMON_CONFDIR/haveged.pid 479 | fi 480 | fi 481 | mutex_unlock 482 | sleep 2 483 | done 484 | } 485 | 486 | NETWORKMANAGER_CONF=/etc/NetworkManager/NetworkManager.conf 487 | NM_OLDER_VERSION=1 488 | 489 | networkmanager_exists() { 490 | local NM_VER 491 | which nmcli > /dev/null 2>&1 || return 1 492 | NM_VER=$(nmcli -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+') 493 | version_cmp $NM_VER 0.9.9 494 | if [[ $? -eq 1 ]]; then 495 | NM_OLDER_VERSION=1 496 | else 497 | NM_OLDER_VERSION=0 498 | fi 499 | return 0 500 | } 501 | 502 | networkmanager_is_running() { 503 | local NMCLI_OUT 504 | networkmanager_exists || return 1 505 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 506 | NMCLI_OUT=$(nmcli -t -f RUNNING nm 2>&1 | grep -E '^running$') 507 | else 508 | NMCLI_OUT=$(nmcli -t -f RUNNING g 2>&1 | grep -E '^running$') 509 | fi 510 | [[ -n "$NMCLI_OUT" ]] 511 | } 512 | 513 | networkmanager_knows_iface() { 514 | # check if the interface $1 is known to NetworkManager 515 | # an interface may exist but may not be known to NetworkManager if it is in a different network namespace than NetworkManager 516 | nmcli -t -f DEVICE d 2>&1 | grep -Fxq "$1" 517 | } 518 | 519 | networkmanager_iface_is_unmanaged() { 520 | is_interface "$1" || return 2 521 | networkmanager_knows_iface "$1" || return 0 522 | (nmcli -t -f DEVICE,STATE d 2>&1 | grep -E "^$1:unmanaged$" > /dev/null 2>&1) || return 1 523 | } 524 | 525 | ADDED_UNMANAGED= 526 | 527 | networkmanager_add_unmanaged() { 528 | local MAC UNMANAGED WAS_EMPTY x 529 | networkmanager_exists || return 1 530 | 531 | [[ -d ${NETWORKMANAGER_CONF%/*} ]] || mkdir -p ${NETWORKMANAGER_CONF%/*} 532 | [[ -f ${NETWORKMANAGER_CONF} ]] || touch ${NETWORKMANAGER_CONF} 533 | 534 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 535 | if [[ -z "$2" ]]; then 536 | MAC=$(get_macaddr "$1") 537 | else 538 | MAC="$2" 539 | fi 540 | [[ -z "$MAC" ]] && return 1 541 | fi 542 | 543 | mutex_lock 544 | UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf) 545 | 546 | WAS_EMPTY=0 547 | [[ -z "$UNMANAGED" ]] && WAS_EMPTY=1 548 | UNMANAGED=$(echo "$UNMANAGED" | sed 's/unmanaged-devices=//' | tr ';,' ' ') 549 | 550 | # if it exists, do nothing 551 | for x in $UNMANAGED; do 552 | if [[ $x == "mac:${MAC}" ]] || 553 | [[ $NM_OLDER_VERSION -eq 0 && $x == "interface-name:${1}" ]]; then 554 | mutex_unlock 555 | return 2 556 | fi 557 | done 558 | 559 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 560 | UNMANAGED="${UNMANAGED} mac:${MAC}" 561 | else 562 | UNMANAGED="${UNMANAGED} interface-name:${1}" 563 | fi 564 | 565 | UNMANAGED=$(echo $UNMANAGED | sed -e 's/^ //') 566 | UNMANAGED="${UNMANAGED// /;}" 567 | UNMANAGED="unmanaged-devices=${UNMANAGED}" 568 | 569 | if ! grep -E '^\[keyfile\]' ${NETWORKMANAGER_CONF} > /dev/null 2>&1; then 570 | echo -e "\n\n[keyfile]\n${UNMANAGED}" >> ${NETWORKMANAGER_CONF} 571 | elif [[ $WAS_EMPTY -eq 1 ]]; then 572 | sed -e "s/^\(\[keyfile\].*\)$/\1\n${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 573 | else 574 | sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 575 | fi 576 | 577 | ADDED_UNMANAGED="${ADDED_UNMANAGED} ${1} " 578 | mutex_unlock 579 | 580 | local nm_pid=$(pidof NetworkManager) 581 | [[ -n "$nm_pid" ]] && kill -HUP $nm_pid 582 | 583 | return 0 584 | } 585 | 586 | networkmanager_rm_unmanaged() { 587 | local MAC UNMANAGED 588 | networkmanager_exists || return 1 589 | [[ ! -f ${NETWORKMANAGER_CONF} ]] && return 1 590 | 591 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 592 | if [[ -z "$2" ]]; then 593 | MAC=$(get_macaddr "$1") 594 | else 595 | MAC="$2" 596 | fi 597 | [[ -z "$MAC" ]] && return 1 598 | fi 599 | 600 | mutex_lock 601 | UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf | sed 's/unmanaged-devices=//' | tr ';,' ' ') 602 | 603 | if [[ -z "$UNMANAGED" ]]; then 604 | mutex_unlock 605 | return 1 606 | fi 607 | 608 | [[ -n "$MAC" ]] && UNMANAGED=$(echo $UNMANAGED | sed -e "s/mac:${MAC}\( \|$\)//g") 609 | UNMANAGED=$(echo $UNMANAGED | sed -e "s/interface-name:${1}\( \|$\)//g") 610 | UNMANAGED=$(echo $UNMANAGED | sed -e 's/ $//') 611 | 612 | if [[ -z "$UNMANAGED" ]]; then 613 | sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF} 614 | else 615 | UNMANAGED="${UNMANAGED// /;}" 616 | UNMANAGED="unmanaged-devices=${UNMANAGED}" 617 | sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 618 | fi 619 | 620 | ADDED_UNMANAGED="${ADDED_UNMANAGED/ ${1} /}" 621 | mutex_unlock 622 | 623 | local nm_pid=$(pidof NetworkManager) 624 | [[ -n "$nm_pid" ]] && kill -HUP $nm_pid 625 | 626 | return 0 627 | } 628 | 629 | networkmanager_fix_unmanaged() { 630 | [[ -f ${NETWORKMANAGER_CONF} ]] || return 631 | 632 | mutex_lock 633 | sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF} 634 | mutex_unlock 635 | 636 | local nm_pid=$(pidof NetworkManager) 637 | [[ -n "$nm_pid" ]] && kill -HUP $nm_pid 638 | } 639 | 640 | networkmanager_rm_unmanaged_if_needed() { 641 | [[ $ADDED_UNMANAGED =~ .*\ ${1}\ .* ]] && networkmanager_rm_unmanaged $1 $2 642 | } 643 | 644 | networkmanager_wait_until_unmanaged() { 645 | local RES 646 | networkmanager_is_running || return 1 647 | while :; do 648 | networkmanager_iface_is_unmanaged "$1" 649 | RES=$? 650 | [[ $RES -eq 0 ]] && break 651 | [[ $RES -eq 2 ]] && die "Interface '${1}' does not exist. 652 | It's probably renamed by a udev rule." 653 | sleep 1 654 | done 655 | sleep 2 656 | return 0 657 | } 658 | 659 | 660 | CHANNEL=default 661 | GATEWAY=192.168.12.1 662 | WPA_VERSION=1+2 663 | ETC_HOSTS=0 664 | ADDN_HOSTS= 665 | DHCP_DNS=gateway 666 | DHCP_DNS6=gateway 667 | NO_DNS=0 668 | NO_DNSMASQ=0 669 | DNS_PORT= 670 | HIDDEN=0 671 | MAC_FILTER=0 672 | MAC_FILTER_ACCEPT=/etc/hostapd/hostapd.accept 673 | ISOLATE_CLIENTS=0 674 | SHARE_METHOD=nat 675 | IEEE80211N=0 676 | IEEE80211AC=0 677 | HT_CAPAB='[HT40+]' 678 | VHT_CAPAB= 679 | DRIVER=nl80211 680 | NO_VIRT=0 681 | COUNTRY= 682 | FREQ_BAND=2.4 683 | BEACON_INTERVAL=100 684 | DTIM_PERIOD=2 685 | NEW_MACADDR= 686 | DAEMONIZE=0 687 | DAEMON_PIDFILE= 688 | DAEMON_LOGFILE=/dev/null 689 | NO_HAVEGED=0 690 | USE_PSK=0 691 | IPV6=0 692 | WPS=0 693 | 694 | HOSTAPD_DEBUG_ARGS= 695 | REDIRECT_TO_LOCALHOST=0 696 | 697 | CONFIG_OPTS=(CHANNEL GATEWAY WPA_VERSION ETC_HOSTS DHCP_DNS DHCP_DNS6 NO_DNS NO_DNSMASQ HIDDEN MAC_FILTER MAC_FILTER_ACCEPT ISOLATE_CLIENTS 698 | SHARE_METHOD IEEE80211N IEEE80211AC HT_CAPAB VHT_CAPAB DRIVER NO_VIRT COUNTRY FREQ_BAND 699 | NEW_MACADDR DAEMONIZE DAEMON_PIDFILE DAEMON_LOGFILE NO_HAVEGED WIFI_IFACE INTERNET_IFACE 700 | SSID PASSPHRASE USE_PSK BEACON_INTERVAL DTIM_PERIOD IPV6 ADDN_HOSTS WPS) 701 | 702 | FIX_UNMANAGED=0 703 | LIST_RUNNING=0 704 | STOP_ID= 705 | LIST_CLIENTS_ID= 706 | 707 | STORE_CONFIG= 708 | LOAD_CONFIG= 709 | 710 | CONFDIR= 711 | WIFI_IFACE= 712 | VWIFI_IFACE= 713 | INTERNET_IFACE= 714 | BRIDGE_IFACE= 715 | OLD_MACADDR= 716 | IP_ADDRS= 717 | ROUTE_ADDRS= 718 | 719 | HAVEGED_WATCHDOG_PID= 720 | 721 | _cleanup() { 722 | local PID x 723 | 724 | trap "" SIGINT SIGUSR1 SIGUSR2 EXIT 725 | mutex_lock 726 | disown -a 727 | 728 | # kill haveged_watchdog 729 | [[ -n "$HAVEGED_WATCHDOG_PID" ]] && kill $HAVEGED_WATCHDOG_PID 730 | 731 | # kill processes 732 | for x in $CONFDIR/*.pid; do 733 | # even if the $CONFDIR is empty, the for loop will assign 734 | # a value in $x. so we need to check if the value is a file 735 | [[ -f $x ]] && kill -9 $(cat $x) 736 | done 737 | 738 | rm -rf $CONFDIR 739 | 740 | local found=0 741 | for x in $(list_running_conf); do 742 | if [[ -f $x/nat_internet_iface && $(cat $x/nat_internet_iface) == $INTERNET_IFACE ]]; then 743 | found=1 744 | break 745 | fi 746 | done 747 | 748 | if [[ $found -eq 0 ]]; then 749 | cp -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding \ 750 | /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding 751 | rm -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding 752 | 753 | cp -f $COMMON_CONFDIR/ipv6_forwarding \ 754 | /proc/sys/net/ipv6/conf/all/forwarding 755 | rm -f $COMMON_CONFDIR/ipv6_forwarding 756 | 757 | cp -f $COMMON_CONFDIR/${INTERNET_IFACE}_ipv6_forwarding \ 758 | /proc/sys/net/ipv6/conf/$INTERNET_IFACE/forwarding 759 | rm -f $COMMON_CONFDIR/${INTERNET_IFACE}_ipv6_forwarding 760 | fi 761 | 762 | # if we are the last create_ap instance then set back the common values 763 | if ! has_running_instance; then 764 | # kill common processes 765 | for x in $COMMON_CONFDIR/*.pid; do 766 | [[ -f $x ]] && kill -9 $(cat $x) 767 | done 768 | 769 | # set old ip_forward 770 | if [[ -f $COMMON_CONFDIR/ip_forward ]]; then 771 | cp -f $COMMON_CONFDIR/ip_forward /proc/sys/net/ipv4 772 | rm -f $COMMON_CONFDIR/ip_forward 773 | fi 774 | 775 | # set old bridge-nf-call-iptables 776 | if [[ -f $COMMON_CONFDIR/bridge-nf-call-iptables ]]; then 777 | if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 778 | cp -f $COMMON_CONFDIR/bridge-nf-call-iptables /proc/sys/net/bridge 779 | fi 780 | rm -f $COMMON_CONFDIR/bridge-nf-call-iptables 781 | fi 782 | if [[ -f $COMMON_CONFDIR/bridge-nf-call-ip6tables ]]; then 783 | if [[ -e /proc/sys/net/bridge/bridge-nf-call-ip6tables ]]; then 784 | cp -f $COMMON_CONFDIR/bridge-nf-call-ip6tables /proc/sys/net/bridge 785 | fi 786 | rm -f $COMMON_CONFDIR/bridge-nf-call-ip6tables 787 | fi 788 | 789 | rm -rf $COMMON_CONFDIR 790 | fi 791 | 792 | if [[ "$SHARE_METHOD" != "none" ]]; then 793 | if [[ "$SHARE_METHOD" == "nat" ]]; then 794 | iptables -w -t nat -D POSTROUTING -s ${GATEWAY%.*}.0/24 ! -o ${WIFI_IFACE} -j MASQUERADE 795 | iptables -w -D FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT 796 | iptables -w -D FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT 797 | # FIXME: restore ip6tables? 798 | elif [[ "$SHARE_METHOD" == "bridge" ]]; then 799 | if ! is_bridge_interface $INTERNET_IFACE; then 800 | ip link set dev $BRIDGE_IFACE down 801 | ip link set dev $INTERNET_IFACE down 802 | ip link set dev $INTERNET_IFACE promisc off 803 | ip link set dev $INTERNET_IFACE nomaster 804 | ip link delete $BRIDGE_IFACE type bridge 805 | ip addr flush $INTERNET_IFACE 806 | ip link set dev $INTERNET_IFACE up 807 | dealloc_iface $BRIDGE_IFACE 808 | 809 | for x in "${IP_ADDRS[@]}"; do 810 | x="${x/inet/}" 811 | x="${x/secondary/}" 812 | x="${x/dynamic/}" 813 | x=$(echo $x | sed 's/\([0-9]\)sec/\1/g') 814 | x="${x/${INTERNET_IFACE}/}" 815 | ip addr add $x dev $INTERNET_IFACE 816 | done 817 | 818 | ip route flush dev $INTERNET_IFACE 819 | 820 | for x in "${ROUTE_ADDRS[@]}"; do 821 | [[ -z "$x" ]] && continue 822 | [[ "$x" == default* ]] && continue 823 | ip route add $x dev $INTERNET_IFACE 824 | done 825 | 826 | for x in "${ROUTE_ADDRS[@]}"; do 827 | [[ -z "$x" ]] && continue 828 | [[ "$x" != default* ]] && continue 829 | ip route add $x dev $INTERNET_IFACE 830 | done 831 | 832 | networkmanager_rm_unmanaged_if_needed $INTERNET_IFACE 833 | fi 834 | fi 835 | fi 836 | 837 | if [[ "$SHARE_METHOD" != "bridge" ]]; then 838 | if [[ $NO_DNS -eq 0 ]]; then 839 | iptables -w -D INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT 840 | iptables -w -D INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT 841 | iptables -w -t nat -D PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ 842 | -p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT 843 | iptables -w -t nat -D PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ 844 | -p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT 845 | if [[ "$IPV6" -ne 0 ]]; then 846 | ip6tables -w -D INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT 847 | ip6tables -w -D INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT 848 | ip6tables -w -t nat -D PREROUTING -s ${GATEWAY6}/$PREFIXLEN6 -d ${GATEWAY6} \ 849 | -p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT 850 | ip6tables -w -t nat -D PREROUTING -s ${GATEWAY6}/$PREFIXLEN6 -d ${GATEWAY6} \ 851 | -p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT 852 | fi 853 | fi 854 | iptables -w -D INPUT -p udp -m udp --dport 67 -j ACCEPT 855 | if [[ $IPV6 -ne 0 ]]; then 856 | ip6tables -w -D INPUT -p udp -m udp --dport 67 -j ACCEPT 857 | # Restore original subnet to internet-facing interface 858 | ip -6 addr del "$INTERNET6"/128 dev ${INTERNET_IFACE} 859 | ip -6 addr add "$INTERNET6"/"$PREFIXLEN6" dev ${INTERNET_IFACE} 860 | fi 861 | fi 862 | 863 | if [[ $NO_VIRT -eq 0 ]]; then 864 | if [[ -n "$VWIFI_IFACE" ]]; then 865 | ip link set down dev ${VWIFI_IFACE} 866 | ip addr flush ${VWIFI_IFACE} 867 | networkmanager_rm_unmanaged_if_needed ${VWIFI_IFACE} ${OLD_MACADDR} 868 | iw dev ${VWIFI_IFACE} del 869 | dealloc_iface $VWIFI_IFACE 870 | fi 871 | else 872 | ip link set down dev ${WIFI_IFACE} 873 | ip addr flush ${WIFI_IFACE} 874 | if [[ -n "$NEW_MACADDR" ]]; then 875 | ip link set dev ${WIFI_IFACE} address ${OLD_MACADDR} 876 | fi 877 | networkmanager_rm_unmanaged_if_needed ${WIFI_IFACE} ${OLD_MACADDR} 878 | fi 879 | 880 | mutex_unlock 881 | cleanup_lock 882 | 883 | if [[ $RUNNING_AS_DAEMON -eq 1 && -n "$DAEMON_PIDFILE" && -f "$DAEMON_PIDFILE" ]]; then 884 | rm $DAEMON_PIDFILE 885 | fi 886 | } 887 | 888 | cleanup() { 889 | echo 890 | echo -n "Doing cleanup.. " 891 | _cleanup > /dev/null 2>&1 892 | echo "done" 893 | } 894 | 895 | die() { 896 | [[ -n "$1" ]] && echo -e "\nERROR: $1\n" >&2 897 | # send die signal to the main process 898 | [[ $BASHPID -ne $$ ]] && kill -USR2 $$ 899 | # we don't need to call cleanup because it's traped on EXIT 900 | exit 1 901 | } 902 | 903 | clean_exit() { 904 | # send clean_exit signal to the main process 905 | [[ $BASHPID -ne $$ ]] && kill -USR1 $$ 906 | # we don't need to call cleanup because it's traped on EXIT 907 | exit 0 908 | } 909 | 910 | list_running_conf() { 911 | local x 912 | mutex_lock 913 | for x in /tmp/create_ap.*; do 914 | if [[ -f $x/pid && -f $x/wifi_iface && -d /proc/$(cat $x/pid) ]]; then 915 | echo $x 916 | fi 917 | done 918 | mutex_unlock 919 | } 920 | 921 | list_running() { 922 | local IFACE wifi_iface x 923 | mutex_lock 924 | for x in $(list_running_conf); do 925 | IFACE=${x#*.} 926 | IFACE=${IFACE%%.*} 927 | wifi_iface=$(cat $x/wifi_iface) 928 | 929 | if [[ $IFACE == $wifi_iface ]]; then 930 | echo $(cat $x/pid) $IFACE 931 | else 932 | echo $(cat $x/pid) $IFACE '('$(cat $x/wifi_iface)')' 933 | fi 934 | done 935 | mutex_unlock 936 | } 937 | 938 | get_wifi_iface_from_pid() { 939 | list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E "^${1} " | cut -d' ' -f2 940 | } 941 | 942 | get_pid_from_wifi_iface() { 943 | list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E " ${1}$" | cut -d' ' -f1 944 | } 945 | 946 | get_confdir_from_pid() { 947 | local IFACE x 948 | mutex_lock 949 | for x in $(list_running_conf); do 950 | if [[ $(cat $x/pid) == "$1" ]]; then 951 | echo $x 952 | break 953 | fi 954 | done 955 | mutex_unlock 956 | } 957 | 958 | print_client() { 959 | local line ipaddr hostname 960 | local mac="$1" 961 | 962 | if [[ -f $CONFDIR/dnsmasq.leases ]]; then 963 | line=$(grep " $mac " $CONFDIR/dnsmasq.leases | tail -n 1) 964 | ipaddr=$(echo $line | cut -d' ' -f3) 965 | hostname=$(echo "$line" | cut -d' ' -f4) 966 | fi 967 | 968 | [[ -z "$ipaddr" ]] && ipaddr="*" 969 | [[ -z "$hostname" ]] && hostname="*" 970 | 971 | printf "%-20s %-18s %s\n" "$mac" "$ipaddr" "$hostname" 972 | } 973 | 974 | list_clients() { 975 | local wifi_iface pid 976 | 977 | # If PID is given, get the associated wifi iface 978 | if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then 979 | pid="$1" 980 | wifi_iface=$(get_wifi_iface_from_pid "$pid") 981 | [[ -z "$wifi_iface" ]] && die "'$pid' is not the pid of a running $PROGNAME instance." 982 | fi 983 | 984 | [[ -z "$wifi_iface" ]] && wifi_iface="$1" 985 | is_wifi_interface "$wifi_iface" || die "'$wifi_iface' is not a WiFi interface." 986 | 987 | [[ -z "$pid" ]] && pid=$(get_pid_from_wifi_iface "$wifi_iface") 988 | [[ -z "$pid" ]] && die "'$wifi_iface' is not used from $PROGNAME instance.\n\ 989 | Maybe you need to pass the virtual interface instead.\n\ 990 | Use --list-running to find it out." 991 | [[ -z "$CONFDIR" ]] && CONFDIR=$(get_confdir_from_pid "$pid") 992 | 993 | if [[ $USE_IWCONFIG -eq 0 ]]; then 994 | local awk_cmd='($1 ~ /Station$/) {print $2}' 995 | local client_list=$(iw dev "$wifi_iface" station dump | awk "$awk_cmd") 996 | 997 | if [[ -z "$client_list" ]]; then 998 | echo "No clients connected" 999 | return 1000 | fi 1001 | 1002 | printf "%-20s %-18s %s\n" "MAC" "IP" "Hostname" 1003 | 1004 | local mac 1005 | for mac in $client_list; do 1006 | print_client $mac 1007 | done 1008 | else 1009 | die "This option is not supported for the current driver." 1010 | fi 1011 | } 1012 | 1013 | wps_trigger() { 1014 | local wifi_iface pid 1015 | 1016 | # If PID is given, get the associated wifi iface 1017 | if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then 1018 | pid="$1" 1019 | wifi_iface=$(get_wifi_iface_from_pid "$pid") 1020 | [[ -z "$wifi_iface" ]] && die "'$pid' is not the pid of a running $PROGNAME instance." 1021 | fi 1022 | 1023 | [[ -z "$wifi_iface" ]] && wifi_iface="$1" 1024 | is_wifi_interface "$wifi_iface" || die "'$wifi_iface' is not a WiFi interface." 1025 | 1026 | [[ -z "$pid" ]] && pid=$(get_pid_from_wifi_iface "$wifi_iface") 1027 | [[ -z "$pid" ]] && die "'$wifi_iface' is not used from $PROGNAME instance.\n\ 1028 | Maybe you need to pass the virtual interface instead.\n\ 1029 | Use --list-running to find it out." 1030 | [[ -z "$CONFDIR" ]] && CONFDIR=$(get_confdir_from_pid "$pid") 1031 | 1032 | HOSTAPD_CLI=$(which hostapd_cli) 1033 | if ! $HOSTAPD_CLI -p $CONFDIR/hostapd_ctrl get_config | grep -q wps_state=configured; then 1034 | die "WPS is not configured. You need to run $PROGNAME with --wps, or add WPS=1 to config file." 1035 | fi 1036 | 1037 | if [[ "$2" = "PBC" ]]; then 1038 | echo "Triggering WPS pushbutton; your device will have 120 seconds to connect..." 1039 | $HOSTAPD_CLI -p $CONFDIR/hostapd_ctrl wps_pbc 1040 | else 1041 | echo "Enrolling WPS PIN '$2'..." 1042 | $HOSTAPD_CLI -p $CONFDIR/hostapd_ctrl wps_pin any "$2" 1043 | fi 1044 | $HOSTAPD_CLI -p $CONFDIR/hostapd_ctrl wps_get_status 1045 | } 1046 | 1047 | has_running_instance() { 1048 | local PID x 1049 | 1050 | mutex_lock 1051 | for x in /tmp/create_ap.*; do 1052 | if [[ -f $x/pid ]]; then 1053 | PID=$(cat $x/pid) 1054 | if [[ -d /proc/$PID ]]; then 1055 | mutex_unlock 1056 | return 0 1057 | fi 1058 | fi 1059 | done 1060 | mutex_lock 1061 | 1062 | return 1 1063 | } 1064 | 1065 | is_running_pid() { 1066 | list_running | grep -E "^${1} " > /dev/null 2>&1 1067 | } 1068 | 1069 | send_stop() { 1070 | local x 1071 | 1072 | mutex_lock 1073 | # send stop signal to specific pid 1074 | if is_running_pid $1; then 1075 | kill -USR1 $1 1076 | mutex_unlock 1077 | return 1078 | fi 1079 | 1080 | # send stop signal to specific interface 1081 | for x in $(list_running | grep -E " \(?${1}( |\)?\$)" | cut -f1 -d' '); do 1082 | kill -USR1 $x 1083 | done 1084 | mutex_unlock 1085 | } 1086 | 1087 | # Storing configs 1088 | write_config() { 1089 | local i=1 1090 | 1091 | if ! eval 'echo -n > "$STORE_CONFIG"' > /dev/null 2>&1; then 1092 | echo "ERROR: Unable to create config file $STORE_CONFIG" >&2 1093 | exit 1 1094 | fi 1095 | 1096 | WIFI_IFACE=$1 1097 | if [[ "$SHARE_METHOD" == "none" ]]; then 1098 | SSID="$2" 1099 | PASSPHRASE="$3" 1100 | else 1101 | INTERNET_IFACE="$2" 1102 | SSID="$3" 1103 | PASSPHRASE="$4" 1104 | fi 1105 | 1106 | for config_opt in "${CONFIG_OPTS[@]}"; do 1107 | eval echo $config_opt=\$$config_opt 1108 | done >> "$STORE_CONFIG" 1109 | 1110 | echo -e "Config options written to '$STORE_CONFIG'" 1111 | exit 0 1112 | } 1113 | 1114 | is_config_opt() { 1115 | local elem opt="$1" 1116 | 1117 | for elem in "${CONFIG_OPTS[@]}"; do 1118 | if [[ "$elem" == "$opt" ]]; then 1119 | return 0 1120 | fi 1121 | done 1122 | return 1 1123 | } 1124 | 1125 | # Load options from config file 1126 | read_config() { 1127 | local opt_name opt_val line 1128 | 1129 | while read line; do 1130 | # Skip comments and blank lines 1131 | [[ -z "${line}" || "${line:0:1}" == "#" ]] && continue 1132 | 1133 | # Read switches and their values 1134 | opt_name="${line%%=*}" 1135 | opt_val="${line#*=}" 1136 | if is_config_opt "$opt_name" ; then 1137 | eval $opt_name="\$opt_val" 1138 | else 1139 | echo "WARN: Unrecognized configuration entry $opt_name" >&2 1140 | fi 1141 | done < "$LOAD_CONFIG" 1142 | } 1143 | 1144 | 1145 | ARGS=( "$@" ) 1146 | 1147 | # Preprocessing for --config before option-parsing starts 1148 | for ((i=0; i<$#; i++)); do 1149 | if [[ "${ARGS[i]}" = "--config" ]]; then 1150 | if [[ -f "${ARGS[i+1]}" ]]; then 1151 | LOAD_CONFIG="${ARGS[i+1]}" 1152 | read_config 1153 | else 1154 | echo "ERROR: No config file found at given location" >&2 1155 | exit 1 1156 | fi 1157 | break 1158 | fi 1159 | done 1160 | 1161 | 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:","dhcp-dns6:","daemon","pidfile:","logfile:","stop:","list","list-running","list-clients:","version","psk","no-haveged","no-dns","no-dnsmasq","ipv6","mkconfig:","config:","wps","wps-pbc:","wps-pin:" -n "$PROGNAME" -- "$@") 1162 | [[ $? -ne 0 ]] && exit 1 1163 | eval set -- "$GETOPT_ARGS" 1164 | 1165 | while :; do 1166 | case "$1" in 1167 | -h|--help) 1168 | usage 1169 | exit 0 1170 | ;; 1171 | --version) 1172 | echo $VERSION 1173 | exit 0 1174 | ;; 1175 | --hidden) 1176 | shift 1177 | HIDDEN=1 1178 | ;; 1179 | --mac-filter) 1180 | shift 1181 | MAC_FILTER=1 1182 | ;; 1183 | --mac-filter-accept) 1184 | shift 1185 | MAC_FILTER_ACCEPT="$1" 1186 | shift 1187 | ;; 1188 | --isolate-clients) 1189 | shift 1190 | ISOLATE_CLIENTS=1 1191 | ;; 1192 | -c) 1193 | shift 1194 | CHANNEL="$1" 1195 | shift 1196 | ;; 1197 | -w) 1198 | shift 1199 | WPA_VERSION="$1" 1200 | [[ "$WPA_VERSION" == "2+1" ]] && WPA_VERSION=1+2 1201 | shift 1202 | ;; 1203 | -g) 1204 | shift 1205 | GATEWAY="$1" 1206 | shift 1207 | ;; 1208 | --ipv6) 1209 | shift 1210 | IPV6=1 1211 | ;; 1212 | -d) 1213 | shift 1214 | ETC_HOSTS=1 1215 | ;; 1216 | -e) 1217 | shift 1218 | ADDN_HOSTS="$1" 1219 | shift 1220 | ;; 1221 | -n) 1222 | shift 1223 | SHARE_METHOD=none 1224 | ;; 1225 | -m) 1226 | shift 1227 | SHARE_METHOD="$1" 1228 | shift 1229 | ;; 1230 | --ieee80211n) 1231 | shift 1232 | IEEE80211N=1 1233 | ;; 1234 | --ieee80211ac) 1235 | shift 1236 | IEEE80211AC=1 1237 | ;; 1238 | --ht_capab) 1239 | shift 1240 | HT_CAPAB="$1" 1241 | shift 1242 | ;; 1243 | --vht_capab) 1244 | shift 1245 | VHT_CAPAB="$1" 1246 | shift 1247 | ;; 1248 | --driver) 1249 | shift 1250 | DRIVER="$1" 1251 | shift 1252 | ;; 1253 | --no-virt) 1254 | shift 1255 | NO_VIRT=1 1256 | ;; 1257 | --fix-unmanaged) 1258 | shift 1259 | FIX_UNMANAGED=1 1260 | ;; 1261 | --country) 1262 | shift 1263 | COUNTRY="$1" 1264 | shift 1265 | ;; 1266 | --freq-band) 1267 | shift 1268 | FREQ_BAND="$1" 1269 | shift 1270 | ;; 1271 | --mac) 1272 | shift 1273 | NEW_MACADDR="$1" 1274 | shift 1275 | ;; 1276 | --dhcp-dns) 1277 | shift 1278 | DHCP_DNS="$1" 1279 | shift 1280 | ;; 1281 | --dhcp-dns6) 1282 | shift 1283 | DHCP_DNS6="$1" 1284 | shift 1285 | ;; 1286 | --daemon) 1287 | shift 1288 | DAEMONIZE=1 1289 | ;; 1290 | --pidfile) 1291 | shift 1292 | DAEMON_PIDFILE="$1" 1293 | shift 1294 | ;; 1295 | --logfile) 1296 | shift 1297 | DAEMON_LOGFILE="$1" 1298 | shift 1299 | ;; 1300 | --stop) 1301 | shift 1302 | STOP_ID="$1" 1303 | shift 1304 | ;; 1305 | --list) 1306 | shift 1307 | LIST_RUNNING=1 1308 | echo -e "WARN: --list is deprecated, use --list-running instead.\n" >&2 1309 | ;; 1310 | --list-running) 1311 | shift 1312 | LIST_RUNNING=1 1313 | ;; 1314 | --list-clients) 1315 | shift 1316 | LIST_CLIENTS_ID="$1" 1317 | shift 1318 | ;; 1319 | --wps-pbc) 1320 | shift 1321 | WPS_TRIGGER_ID="$1" 1322 | WPS_TRIGGER_PIN="PBC" 1323 | shift; 1324 | ;; 1325 | --wps-pin) 1326 | shift 1327 | WPS_TRIGGER_ID="${1/,*/}" 1328 | WPS_TRIGGER_PIN="${1/*,/}" 1329 | shift; 1330 | ;; 1331 | --no-haveged) 1332 | shift 1333 | NO_HAVEGED=1 1334 | ;; 1335 | --wps) 1336 | shift 1337 | WPS=1 1338 | ;; 1339 | --psk) 1340 | shift 1341 | USE_PSK=1 1342 | ;; 1343 | --no-dns) 1344 | shift 1345 | NO_DNS=1 1346 | ;; 1347 | --no-dnsmasq) 1348 | shift 1349 | NO_DNSMASQ=1 1350 | ;; 1351 | --redirect-to-localhost) 1352 | shift 1353 | REDIRECT_TO_LOCALHOST=1 1354 | ;; 1355 | --hostapd-debug) 1356 | shift 1357 | if [ "x$1" = "x1" ]; then 1358 | HOSTAPD_DEBUG_ARGS="-d" 1359 | elif [ "x$1" = "x2" ]; then 1360 | HOSTAPD_DEBUG_ARGS="-dd" 1361 | else 1362 | printf "Error: argument for --hostapd-debug expected 1 or 2, got %s\n" "$1" 1363 | exit 1 1364 | fi 1365 | shift 1366 | ;; 1367 | --mkconfig) 1368 | shift 1369 | STORE_CONFIG="$1" 1370 | shift 1371 | ;; 1372 | --config) 1373 | shift 1374 | shift 1375 | ;; 1376 | --) 1377 | shift 1378 | break 1379 | ;; 1380 | esac 1381 | done 1382 | 1383 | # Load positional args from config file, if needed 1384 | if [[ -n "$LOAD_CONFIG" && $# -eq 0 ]]; then 1385 | i=0 1386 | # set arguments in order 1387 | for x in WIFI_IFACE INTERNET_IFACE SSID PASSPHRASE; do 1388 | if eval "[[ -n \"\$${x}\" ]]"; then 1389 | eval "set -- \"\${@:1:$i}\" \"\$${x}\"" 1390 | ((i++)) 1391 | fi 1392 | # we unset the variable to avoid any problems later 1393 | eval "unset $x" 1394 | done 1395 | fi 1396 | 1397 | # Check if required number of positional args are present 1398 | if [[ $# -lt 1 && $FIX_UNMANAGED -eq 0 && -z "$STOP_ID" && 1399 | $LIST_RUNNING -eq 0 && -z "$LIST_CLIENTS_ID" && -z "$WPS_TRIGGER_ID" ]]; then 1400 | usage >&2 1401 | exit 1 1402 | fi 1403 | 1404 | # Set NO_DNS, if dnsmasq is disabled 1405 | if [[ $NO_DNSMASQ -eq 1 ]]; then 1406 | NO_DNS=1 1407 | fi 1408 | 1409 | trap "cleanup_lock" EXIT 1410 | 1411 | if ! init_lock; then 1412 | echo "ERROR: Failed to initialize lock" >&2 1413 | exit 1 1414 | fi 1415 | 1416 | # if the user press ctrl+c or we get USR1 signal 1417 | # then run clean_exit() 1418 | trap "clean_exit" SIGINT SIGUSR1 1419 | # if we get USR2 signal then run die(). 1420 | trap "die" SIGUSR2 1421 | 1422 | [[ -n "$STORE_CONFIG" ]] && write_config "$@" 1423 | 1424 | if [[ $LIST_RUNNING -eq 1 ]]; then 1425 | echo -e "List of running $PROGNAME instances:\n" 1426 | list_running 1427 | exit 0 1428 | fi 1429 | 1430 | if [[ -n "$LIST_CLIENTS_ID" ]]; then 1431 | list_clients "$LIST_CLIENTS_ID" 1432 | exit 0 1433 | fi 1434 | 1435 | if [[ $(id -u) -ne 0 ]]; then 1436 | echo "You must run it as root." >&2 1437 | exit 1 1438 | fi 1439 | 1440 | if [[ -n "$WPS_TRIGGER_ID" ]]; then 1441 | wps_trigger "$WPS_TRIGGER_ID" "$WPS_TRIGGER_PIN" 1442 | exit 0 1443 | fi 1444 | 1445 | if [[ -n "$STOP_ID" ]]; then 1446 | echo "Trying to kill $PROGNAME instance associated with $STOP_ID..." 1447 | send_stop "$STOP_ID" 1448 | exit 0 1449 | fi 1450 | 1451 | if [[ $FIX_UNMANAGED -eq 1 ]]; then 1452 | echo "Trying to fix unmanaged status in NetworkManager..." 1453 | networkmanager_fix_unmanaged 1454 | exit 0 1455 | fi 1456 | 1457 | if [[ $DAEMONIZE -eq 1 && $RUNNING_AS_DAEMON -eq 0 ]]; then 1458 | # Assume we're running underneath a service manager if PIDFILE is set 1459 | # and don't clobber it's output with a useless message 1460 | if [ -z "$DAEMON_PIDFILE" ]; then 1461 | echo "Running as Daemon..." 1462 | fi 1463 | # run a detached create_ap 1464 | RUNNING_AS_DAEMON=1 setsid "$0" "${ARGS[@]}" >>$DAEMON_LOGFILE 2>&1 & 1465 | exit 0 1466 | elif [[ $RUNNING_AS_DAEMON -eq 1 && -n "$DAEMON_PIDFILE" ]]; then 1467 | echo $$ >$DAEMON_PIDFILE 1468 | fi 1469 | 1470 | if [[ $FREQ_BAND != 2.4 && $FREQ_BAND != 5 ]]; then 1471 | echo "ERROR: Invalid frequency band" >&2 1472 | exit 1 1473 | fi 1474 | 1475 | if [[ $CHANNEL == default ]]; then 1476 | if [[ $FREQ_BAND == 2.4 ]]; then 1477 | CHANNEL=1 1478 | else 1479 | CHANNEL=36 1480 | fi 1481 | fi 1482 | 1483 | if [[ $FREQ_BAND != 5 && $CHANNEL -gt 14 ]]; then 1484 | echo "Channel number is greater than 14, assuming 5GHz frequency band" 1485 | FREQ_BAND=5 1486 | fi 1487 | 1488 | WIFI_IFACE=$1 1489 | 1490 | if ! is_wifi_interface ${WIFI_IFACE}; then 1491 | echo "ERROR: '${WIFI_IFACE}' is not a WiFi interface" >&2 1492 | exit 1 1493 | fi 1494 | 1495 | if ! can_be_ap ${WIFI_IFACE}; then 1496 | echo "ERROR: Your adapter does not support AP (master) mode" >&2 1497 | exit 1 1498 | fi 1499 | 1500 | if ! can_be_sta_and_ap ${WIFI_IFACE}; then 1501 | if is_wifi_connected ${WIFI_IFACE}; then 1502 | echo "ERROR: Your adapter can not be a station (i.e. be connected) and an AP at the same time" >&2 1503 | exit 1 1504 | elif [[ $NO_VIRT -eq 0 ]]; then 1505 | echo "WARN: Your adapter does not fully support AP virtual interface, enabling --no-virt" >&2 1506 | NO_VIRT=1 1507 | fi 1508 | fi 1509 | 1510 | HOSTAPD=$(which hostapd) 1511 | 1512 | if [[ ! -x "$HOSTAPD" ]]; then 1513 | echo "ERROR: hostapd not found." >&2 1514 | exit 1 1515 | fi 1516 | 1517 | if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^(8192[cd][ue]|8723a[sue])$ ]]; then 1518 | if ! strings "$HOSTAPD" | grep -m1 rtl871xdrv > /dev/null 2>&1; then 1519 | echo "ERROR: You need to patch your hostapd with rtl871xdrv patches." >&2 1520 | exit 1 1521 | fi 1522 | 1523 | if [[ $DRIVER != "rtl871xdrv" ]]; then 1524 | echo "WARN: Your adapter needs rtl871xdrv, enabling --driver=rtl871xdrv" >&2 1525 | DRIVER=rtl871xdrv 1526 | fi 1527 | fi 1528 | 1529 | if [[ "$SHARE_METHOD" != "nat" && "$SHARE_METHOD" != "bridge" && "$SHARE_METHOD" != "none" ]]; then 1530 | echo "ERROR: Wrong Internet sharing method" >&2 1531 | echo 1532 | usage >&2 1533 | exit 1 1534 | fi 1535 | 1536 | if [[ -n "$NEW_MACADDR" ]]; then 1537 | if ! is_macaddr "$NEW_MACADDR"; then 1538 | echo "ERROR: '${NEW_MACADDR}' is not a valid MAC address" >&2 1539 | exit 1 1540 | fi 1541 | 1542 | if ! is_unicast_macaddr "$NEW_MACADDR"; then 1543 | echo "ERROR: The first byte of MAC address (${NEW_MACADDR}) must be even" >&2 1544 | exit 1 1545 | fi 1546 | 1547 | if [[ $(get_all_macaddrs | grep -c ${NEW_MACADDR}) -ne 0 ]]; then 1548 | echo "WARN: MAC address '${NEW_MACADDR}' already exists. Because of this, you may encounter some problems" >&2 1549 | fi 1550 | fi 1551 | 1552 | if [[ "$SHARE_METHOD" != "none" ]]; then 1553 | MIN_REQUIRED_ARGS=2 1554 | else 1555 | MIN_REQUIRED_ARGS=1 1556 | fi 1557 | 1558 | if [[ $# -gt $MIN_REQUIRED_ARGS ]]; then 1559 | if [[ "$SHARE_METHOD" != "none" ]]; then 1560 | if [[ $# -ne 3 && $# -ne 4 ]]; then 1561 | usage >&2 1562 | exit 1 1563 | fi 1564 | INTERNET_IFACE="$2" 1565 | SSID="$3" 1566 | PASSPHRASE="$4" 1567 | else 1568 | if [[ $# -ne 2 && $# -ne 3 ]]; then 1569 | usage >&2 1570 | exit 1 1571 | fi 1572 | SSID="$2" 1573 | PASSPHRASE="$3" 1574 | fi 1575 | else 1576 | if [[ "$SHARE_METHOD" != "none" ]]; then 1577 | if [[ $# -ne 2 ]]; then 1578 | usage >&2 1579 | exit 1 1580 | fi 1581 | INTERNET_IFACE="$2" 1582 | fi 1583 | if tty -s; then 1584 | while :; do 1585 | read -p "SSID: " SSID 1586 | if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then 1587 | echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2 1588 | continue 1589 | fi 1590 | break 1591 | done 1592 | while :; do 1593 | if [[ $USE_PSK -eq 0 ]]; then 1594 | read -p "Passphrase: " -s PASSPHRASE 1595 | echo 1596 | if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then 1597 | echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2 1598 | continue 1599 | fi 1600 | read -p "Retype passphrase: " -s PASSPHRASE2 1601 | echo 1602 | if [[ "$PASSPHRASE" != "$PASSPHRASE2" ]]; then 1603 | echo "Passphrases do not match." 1604 | else 1605 | break 1606 | fi 1607 | else 1608 | read -p "PSK: " PASSPHRASE 1609 | echo 1610 | if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then 1611 | echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2 1612 | continue 1613 | fi 1614 | fi 1615 | done 1616 | else 1617 | read SSID 1618 | read PASSPHRASE 1619 | fi 1620 | fi 1621 | 1622 | if [[ "$SHARE_METHOD" != "none" ]] && ! is_interface $INTERNET_IFACE; then 1623 | echo "ERROR: '${INTERNET_IFACE}' is not an interface" >&2 1624 | exit 1 1625 | fi 1626 | 1627 | if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then 1628 | echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2 1629 | exit 1 1630 | fi 1631 | 1632 | if [[ $USE_PSK -eq 0 ]]; then 1633 | if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then 1634 | echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2 1635 | exit 1 1636 | fi 1637 | elif [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then 1638 | echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2 1639 | exit 1 1640 | fi 1641 | 1642 | if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^rtl[0-9].*$ ]]; then 1643 | if [[ -n "$PASSPHRASE" ]]; then 1644 | echo "WARN: Realtek drivers usually have problems with WPA1, enabling -w 2" >&2 1645 | WPA_VERSION=2 1646 | fi 1647 | echo "WARN: If AP doesn't work, please read: howto/realtek.md" >&2 1648 | fi 1649 | 1650 | if [[ $NO_VIRT -eq 1 && "$WIFI_IFACE" == "$INTERNET_IFACE" ]]; then 1651 | echo -n "ERROR: You can not share your connection from the same" >&2 1652 | echo " interface if you are using --no-virt option." >&2 1653 | exit 1 1654 | fi 1655 | 1656 | mutex_lock 1657 | trap "cleanup" EXIT 1658 | CONFDIR=$(mktemp -d /tmp/create_ap.${WIFI_IFACE}.conf.XXXXXXXX) 1659 | echo "Config dir: $CONFDIR" 1660 | echo "PID: $$" 1661 | echo $$ > $CONFDIR/pid 1662 | 1663 | # to make --list-running work from any user, we must give read 1664 | # permissions to $CONFDIR and $CONFDIR/pid 1665 | chmod 755 $CONFDIR 1666 | chmod 444 $CONFDIR/pid 1667 | 1668 | COMMON_CONFDIR=/tmp/create_ap.common.conf 1669 | mkdir -p $COMMON_CONFDIR 1670 | 1671 | if [[ "$SHARE_METHOD" == "nat" ]]; then 1672 | echo $INTERNET_IFACE > $CONFDIR/nat_internet_iface 1673 | cp_n /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding \ 1674 | $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding 1675 | cp_n /proc/sys/net/ipv6/conf/all/forwarding \ 1676 | $COMMON_CONFDIR/ipv6_forwarding 1677 | cp_n /proc/sys/net/ipv6/conf/${INTERNET_IFACE}/forwarding \ 1678 | $COMMON_CONFDIR/${INTERNET_IFACE}_ipv6_forwarding 1679 | fi 1680 | cp_n /proc/sys/net/ipv4/ip_forward $COMMON_CONFDIR 1681 | if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 1682 | cp_n /proc/sys/net/bridge/bridge-nf-call-iptables $COMMON_CONFDIR 1683 | fi 1684 | if [[ -e /proc/sys/net/bridge/bridge-nf-call-ip6tables ]]; then 1685 | cp_n /proc/sys/net/bridge/bridge-nf-call-ip6tables $COMMON_CONFDIR 1686 | fi 1687 | mutex_unlock 1688 | 1689 | if [[ "$SHARE_METHOD" == "bridge" ]]; then 1690 | if is_bridge_interface $INTERNET_IFACE; then 1691 | BRIDGE_IFACE=$INTERNET_IFACE 1692 | else 1693 | BRIDGE_IFACE=$(alloc_new_iface br) 1694 | fi 1695 | elif [[ $IPV6 -ne 0 ]]; then 1696 | echo "Looking up IPv6 address of internet interface..." 1697 | NETADDR6=$(get_ipv6addr $INTERNET_IFACE) 1698 | if [[ -n "$NETADDR6" ]]; then 1699 | INTERNET6="${NETADDR6%/*}" 1700 | PREFIXLEN6="${NETADDR6#*/}" 1701 | GATEWAY6=$(get_new_ipv6addr $INTERNET_IFACE) 1702 | echo "Internet interface is $INTERNET6/$PREFIXLEN6. Will route $GATEWAY6/$PREFIXLEN6 to wireless clients." 1703 | else 1704 | echo "No IPv6 address found. Disabling IPv6." 1705 | IPV6=0 1706 | fi 1707 | fi 1708 | 1709 | if [[ $USE_IWCONFIG -eq 0 ]]; then 1710 | iw dev ${WIFI_IFACE} set power_save off 1711 | fi 1712 | 1713 | if [[ $NO_VIRT -eq 0 ]]; then 1714 | VWIFI_IFACE=$(alloc_new_iface ap) 1715 | 1716 | # in NetworkManager 0.9.9 and above we can set the interface as unmanaged without 1717 | # the need of MAC address, so we set it before we create the virtual interface. 1718 | if networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]]; then 1719 | echo -n "Network Manager found, set ${VWIFI_IFACE} as unmanaged device... " 1720 | networkmanager_add_unmanaged ${VWIFI_IFACE} 1721 | # do not call networkmanager_wait_until_unmanaged because interface does not 1722 | # exist yet 1723 | echo "DONE" 1724 | fi 1725 | 1726 | if is_wifi_connected ${WIFI_IFACE}; then 1727 | WIFI_IFACE_FREQ=$(iw dev ${WIFI_IFACE} link | grep -i freq | awk '{print $2}') 1728 | WIFI_IFACE_CHANNEL=$(ieee80211_frequency_to_channel ${WIFI_IFACE_FREQ}) 1729 | echo -n "${WIFI_IFACE} is already associated with channel ${WIFI_IFACE_CHANNEL} (${WIFI_IFACE_FREQ} MHz)" 1730 | if is_5ghz_frequency $WIFI_IFACE_FREQ; then 1731 | FREQ_BAND=5 1732 | else 1733 | FREQ_BAND=2.4 1734 | fi 1735 | if [[ $WIFI_IFACE_CHANNEL -ne $CHANNEL ]]; then 1736 | echo ", fallback to channel ${WIFI_IFACE_CHANNEL}" 1737 | CHANNEL=$WIFI_IFACE_CHANNEL 1738 | else 1739 | echo 1740 | fi 1741 | fi 1742 | 1743 | VIRTDIEMSG="Maybe your WiFi adapter does not fully support virtual interfaces. 1744 | Try again with --no-virt." 1745 | echo -n "Creating a virtual WiFi interface... " 1746 | 1747 | if iw dev ${WIFI_IFACE} interface add ${VWIFI_IFACE} type __ap; then 1748 | # now we can call networkmanager_wait_until_unmanaged 1749 | networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]] && networkmanager_wait_until_unmanaged ${VWIFI_IFACE} 1750 | echo "${VWIFI_IFACE} created." 1751 | else 1752 | VWIFI_IFACE= 1753 | die "$VIRTDIEMSG" 1754 | fi 1755 | OLD_MACADDR=$(get_macaddr ${VWIFI_IFACE}) 1756 | if [[ -z "$NEW_MACADDR" && $(get_all_macaddrs | grep -c ${OLD_MACADDR}) -ne 1 ]]; then 1757 | NEW_MACADDR=$(get_new_macaddr ${VWIFI_IFACE}) 1758 | fi 1759 | WIFI_IFACE=${VWIFI_IFACE} 1760 | else 1761 | OLD_MACADDR=$(get_macaddr ${WIFI_IFACE}) 1762 | fi 1763 | 1764 | mutex_lock 1765 | echo $WIFI_IFACE > $CONFDIR/wifi_iface 1766 | chmod 444 $CONFDIR/wifi_iface 1767 | mutex_unlock 1768 | 1769 | if [[ -n "$COUNTRY" && $USE_IWCONFIG -eq 0 ]]; then 1770 | iw reg set "$COUNTRY" 1771 | fi 1772 | 1773 | can_transmit_to_channel ${WIFI_IFACE} ${CHANNEL} || die "Your adapter can not transmit to channel ${CHANNEL}, frequency band ${FREQ_BAND}GHz." 1774 | 1775 | if networkmanager_exists && ! networkmanager_iface_is_unmanaged ${WIFI_IFACE}; then 1776 | echo -n "Network Manager found, set ${WIFI_IFACE} as unmanaged device... " 1777 | networkmanager_add_unmanaged ${WIFI_IFACE} 1778 | 1779 | if networkmanager_is_running; then 1780 | networkmanager_wait_until_unmanaged ${WIFI_IFACE} 1781 | fi 1782 | 1783 | echo "DONE" 1784 | fi 1785 | 1786 | [[ $HIDDEN -eq 1 ]] && echo "Access Point's SSID is hidden!" 1787 | 1788 | [[ $MAC_FILTER -eq 1 ]] && echo "MAC address filtering is enabled!" 1789 | 1790 | [[ $ISOLATE_CLIENTS -eq 1 ]] && echo "Access Point's clients will be isolated!" 1791 | 1792 | # hostapd config 1793 | cat << EOF > $CONFDIR/hostapd.conf 1794 | ssid=${SSID} 1795 | interface=${WIFI_IFACE} 1796 | driver=${DRIVER} 1797 | channel=${CHANNEL} 1798 | ctrl_interface=$CONFDIR/hostapd_ctrl 1799 | ctrl_interface_group=0 1800 | ignore_broadcast_ssid=$HIDDEN 1801 | ap_isolate=$ISOLATE_CLIENTS 1802 | beacon_int=${BEACON_INTERVAL} 1803 | dtim_period=${DTIM_PERIOD} 1804 | EOF 1805 | 1806 | if [[ $WPS -eq 1 ]]; then 1807 | echo "Enabling WPS push button (PBC) and PIN modes!" 1808 | # WPS configuration: AP configured, do not allow external WPS Registrars, 1809 | # device type is "Network Infrastructure / AP" 1810 | cat << EOF >> $CONFDIR/hostapd.conf 1811 | wps_state=2 1812 | ap_setup_locked=1 1813 | device_type=6-0050F204-1 1814 | config_methods=push_button label 1815 | EOF 1816 | # Show SSID as device_name unless hidden 1817 | # (other potential descriptive options are manufacturer, model_name, 1818 | # model_number, serial_number, os_version) 1819 | if [[ $HIDDEN -ne 1 ]]; then 1820 | echo "device_name=${SSID}" >> $CONFDIR/hostapd.conf 1821 | fi 1822 | fi 1823 | 1824 | if [[ -n "$COUNTRY" ]]; then 1825 | cat << EOF >> $CONFDIR/hostapd.conf 1826 | country_code=${COUNTRY} 1827 | ieee80211d=1 1828 | EOF 1829 | fi 1830 | 1831 | if [[ $FREQ_BAND == 2.4 ]]; then 1832 | echo "hw_mode=g" >> $CONFDIR/hostapd.conf 1833 | else 1834 | echo "hw_mode=a" >> $CONFDIR/hostapd.conf 1835 | fi 1836 | 1837 | if [[ $MAC_FILTER -eq 1 ]]; then 1838 | cat << EOF >> $CONFDIR/hostapd.conf 1839 | macaddr_acl=${MAC_FILTER} 1840 | accept_mac_file=${MAC_FILTER_ACCEPT} 1841 | EOF 1842 | fi 1843 | 1844 | if [[ $IEEE80211N -eq 1 ]]; then 1845 | cat << EOF >> $CONFDIR/hostapd.conf 1846 | ieee80211n=1 1847 | ht_capab=${HT_CAPAB} 1848 | EOF 1849 | fi 1850 | 1851 | if [[ $IEEE80211AC -eq 1 ]]; then 1852 | echo "ieee80211ac=1" >> $CONFDIR/hostapd.conf 1853 | fi 1854 | 1855 | if [[ -n "$VHT_CAPAB" ]]; then 1856 | echo "vht_capab=${VHT_CAPAB}" >> $CONFDIR/hostapd.conf 1857 | fi 1858 | 1859 | if [[ $IEEE80211N -eq 1 ]] || [[ $IEEE80211AC -eq 1 ]]; then 1860 | echo "wmm_enabled=1" >> $CONFDIR/hostapd.conf 1861 | fi 1862 | 1863 | if [[ -n "$PASSPHRASE" ]]; then 1864 | [[ "$WPA_VERSION" == "1+2" ]] && WPA_VERSION=3 1865 | if [[ $USE_PSK -eq 0 ]]; then 1866 | WPA_KEY_TYPE=passphrase 1867 | else 1868 | WPA_KEY_TYPE=psk 1869 | fi 1870 | cat << EOF >> $CONFDIR/hostapd.conf 1871 | wpa=${WPA_VERSION} 1872 | wpa_${WPA_KEY_TYPE}=${PASSPHRASE} 1873 | wpa_key_mgmt=WPA-PSK 1874 | wpa_pairwise=TKIP CCMP 1875 | rsn_pairwise=CCMP 1876 | EOF 1877 | fi 1878 | 1879 | if [[ "$SHARE_METHOD" == "bridge" ]]; then 1880 | echo "bridge=${BRIDGE_IFACE}" >> $CONFDIR/hostapd.conf 1881 | elif [[ $NO_DNSMASQ -eq 0 ]]; then 1882 | # dnsmasq config (dhcp + dns) 1883 | DNSMASQ_VER=$(dnsmasq -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+') 1884 | version_cmp $DNSMASQ_VER 2.63 1885 | if [[ $? -eq 1 ]]; then 1886 | DNSMASQ_BIND=bind-interfaces 1887 | else 1888 | DNSMASQ_BIND=bind-dynamic 1889 | fi 1890 | DHCP_DNS="${DHCP_DNS/gateway/$GATEWAY}" 1891 | DHCP_DNS6="${DHCP_DNS6/gateway/[$GATEWAY6]}" 1892 | cat << EOF > $CONFDIR/dnsmasq.conf 1893 | interface=${WIFI_IFACE} 1894 | ${DNSMASQ_BIND} 1895 | dhcp-range=${GATEWAY%.*}.1,${GATEWAY%.*}.254,255.255.255.0,24h 1896 | dhcp-option-force=option:router,${GATEWAY} 1897 | dhcp-option-force=option:dns-server,${DHCP_DNS} 1898 | EOF 1899 | if [[ $IPV6 -ne 0 ]]; then 1900 | if [[ "$NO_DNS" -ne 0 ]]; then 1901 | RA_MODE="ra-names" 1902 | else 1903 | RA_MODE="ra-stateless" 1904 | fi 1905 | [[ -n "$DHCP_DNS6" ]] && echo "dhcp-option-force=option6:dns-server,${DHCP_DNS6}" >> $CONFDIR/dnsmasq.conf 1906 | echo "dhcp-range=::,constructor:${WIFI_IFACE}, $RA_MODE, slaac, 24h" >> $CONFDIR/dnsmasq.conf 1907 | fi 1908 | MTU=$(get_mtu $INTERNET_IFACE) 1909 | [[ -n "$MTU" ]] && echo "dhcp-option-force=option:mtu,${MTU}" >> $CONFDIR/dnsmasq.conf 1910 | [[ $ETC_HOSTS -eq 0 ]] && echo no-hosts >> $CONFDIR/dnsmasq.conf 1911 | [[ -n "$ADDN_HOSTS" ]] && echo "addn-hosts=${ADDN_HOSTS}" >> $CONFDIR/dnsmasq.conf 1912 | if [[ "$SHARE_METHOD" == "none" && "$REDIRECT_TO_LOCALHOST" == "1" ]]; then 1913 | cat << EOF >> $CONFDIR/dnsmasq.conf 1914 | address=/#/$GATEWAY 1915 | EOF 1916 | fi 1917 | fi 1918 | 1919 | # initialize WiFi interface 1920 | if [[ $NO_VIRT -eq 0 && -n "$NEW_MACADDR" ]]; then 1921 | ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die "$VIRTDIEMSG" 1922 | fi 1923 | 1924 | ip link set down dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1925 | ip addr flush ${WIFI_IFACE} || die "$VIRTDIEMSG" 1926 | 1927 | if [[ $NO_VIRT -eq 1 && -n "$NEW_MACADDR" ]]; then 1928 | ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die 1929 | fi 1930 | 1931 | if [[ "$SHARE_METHOD" != "bridge" ]]; then 1932 | ip link set up dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1933 | ip addr add ${GATEWAY}/24 broadcast ${GATEWAY%.*}.255 dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1934 | if [[ $IPV6 -ne 0 ]]; then 1935 | # Normally, the Internet-facing device has been assigned a /64 subnet (saved in INTERNET6/PREFIXLEN6). 1936 | # We want to keep only /128 for the Internet-facing device, but route the rest of the subnet to 1937 | # the AP, so that it can be used by clients via DHCPv6 or SLAAC. 1938 | 1939 | ip -6 addr del "$INTERNET6"/"$PREFIXLEN6" dev ${INTERNET_IFACE} || die "$VIRTDIEMSG" 1940 | ip -6 addr add "$INTERNET6"/128 dev ${INTERNET_IFACE} || die "$VIRTDIEMSG" 1941 | 1942 | ip -6 addr add "$GATEWAY6"/"$PREFIXLEN6" dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1943 | fi 1944 | fi 1945 | 1946 | # enable Internet sharing 1947 | if [[ "$SHARE_METHOD" != "none" ]]; then 1948 | echo "Sharing Internet using method: $SHARE_METHOD" 1949 | if [[ "$SHARE_METHOD" == "nat" ]]; then 1950 | iptables -w -t nat -I POSTROUTING -s ${GATEWAY%.*}.0/24 ! -o ${WIFI_IFACE} -j MASQUERADE || die 1951 | iptables -w -I FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT || die 1952 | iptables -w -I FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT || die 1953 | echo 1 > /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding || die 1954 | echo 1 > /proc/sys/net/ipv4/ip_forward || die 1955 | if [[ $IPV6 -ne 0 ]]; then 1956 | # This enables forwarding of *all* IPv6 packets, which is handy for running an IPv6 1957 | # server on a device inside the network without NAT... 1958 | ip6tables -P FORWARD ACCEPT || die 1959 | # ... but if you want something that prevents random incoming connections, try: 1960 | #ip6tables -P FORWARD DENY || die 1961 | #ip6tables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT || die 1962 | #ip6tables -I FORWARD -i ${WIFI_IFACE} -j ACCEPT || die 1963 | echo 1 > /proc/sys/net/ipv6/conf/$INTERNET_IFACE/forwarding || die 1964 | echo 1 > /proc/sys/net/ipv6/conf/all/forwarding || die 1965 | fi 1966 | # to enable clients to establish PPTP connections we must 1967 | # load nf_nat_pptp module 1968 | modprobe nf_nat_pptp > /dev/null 2>&1 1969 | elif [[ "$SHARE_METHOD" == "bridge" ]]; then 1970 | # disable iptables rules for bridged interfaces 1971 | if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 1972 | echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables 1973 | fi 1974 | 1975 | # to initialize the bridge interface correctly we need to do the following: 1976 | # 1977 | # 1) save the IPs and route table of INTERNET_IFACE 1978 | # 2) if NetworkManager is running set INTERNET_IFACE as unmanaged 1979 | # 3) create BRIDGE_IFACE and attach INTERNET_IFACE to it 1980 | # 4) set the previously saved IPs and route table to BRIDGE_IFACE 1981 | # 1982 | # we need the above because BRIDGE_IFACE is the master interface from now on 1983 | # and it must know where is connected, otherwise connection is lost. 1984 | if ! is_bridge_interface $INTERNET_IFACE; then 1985 | echo -n "Create a bridge interface... " 1986 | OLD_IFS="$IFS" 1987 | IFS=$'\n' 1988 | 1989 | IP_ADDRS=( $(ip addr show $INTERNET_IFACE | grep -A 1 -E 'inet[[:blank:]]' | paste - -) ) 1990 | ROUTE_ADDRS=( $(ip route show dev $INTERNET_IFACE) ) 1991 | 1992 | IFS="$OLD_IFS" 1993 | 1994 | if networkmanager_is_running; then 1995 | networkmanager_add_unmanaged $INTERNET_IFACE 1996 | networkmanager_wait_until_unmanaged $INTERNET_IFACE 1997 | fi 1998 | 1999 | # create bridge interface 2000 | ip link add name $BRIDGE_IFACE type bridge || die 2001 | ip link set dev $BRIDGE_IFACE up || die 2002 | # set 0ms forward delay 2003 | echo -n 0 > /sys/class/net/$BRIDGE_IFACE/bridge/forward_delay 2004 | 2005 | # attach internet interface to bridge interface 2006 | ip link set dev $INTERNET_IFACE promisc on || die 2007 | ip link set dev $INTERNET_IFACE up || die 2008 | ip link set dev $INTERNET_IFACE master $BRIDGE_IFACE || die 2009 | 2010 | ip addr flush $INTERNET_IFACE 2011 | for x in "${IP_ADDRS[@]}"; do 2012 | x="${x/inet/}" 2013 | x="${x/secondary/}" 2014 | x="${x/dynamic/}" 2015 | x=$(echo $x | sed 's/\([0-9]\)sec/\1/g') 2016 | x="${x/${INTERNET_IFACE}/}" 2017 | ip addr add $x dev $BRIDGE_IFACE || die 2018 | done 2019 | 2020 | # remove any existing entries that were added from 'ip addr add' 2021 | ip route flush dev $INTERNET_IFACE 2022 | ip route flush dev $BRIDGE_IFACE 2023 | 2024 | # we must first add the entries that specify the subnets and then the 2025 | # gateway entry, otherwise 'ip addr add' will return an error 2026 | for x in "${ROUTE_ADDRS[@]}"; do 2027 | [[ "$x" == default* ]] && continue 2028 | ip route add $x dev $BRIDGE_IFACE || die 2029 | done 2030 | 2031 | for x in "${ROUTE_ADDRS[@]}"; do 2032 | [[ "$x" != default* ]] && continue 2033 | ip route add $x dev $BRIDGE_IFACE || die 2034 | done 2035 | 2036 | echo "$BRIDGE_IFACE created." 2037 | fi 2038 | fi 2039 | else 2040 | echo "No Internet sharing" 2041 | fi 2042 | 2043 | # start dhcp + dns (optional) 2044 | if [[ "$SHARE_METHOD" != "bridge" ]]; then 2045 | if [[ $NO_DNS -eq 0 ]]; then 2046 | DNS_PORT=5353 2047 | iptables -w -I INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT || die 2048 | iptables -w -I INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT || die 2049 | iptables -w -t nat -I PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ 2050 | -p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die 2051 | iptables -w -t nat -I PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \ 2052 | -p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die 2053 | if [[ $IPV6 -ne 0 ]]; then 2054 | ip6tables -w -I INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT || die 2055 | ip6tables -w -I INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT || die 2056 | ip6tables -w -t nat -I PREROUTING -s ${GATEWAY6}/$PREFIXLEN6 -d ${GATEWAY6} \ 2057 | -p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die 2058 | ip6tables -w -t nat -I PREROUTING -s ${GATEWAY6}/$PREFIXLEN6 -d ${GATEWAY6} \ 2059 | -p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die 2060 | fi 2061 | else 2062 | DNS_PORT=0 2063 | fi 2064 | 2065 | if [[ $NO_DNSMASQ -eq 0 ]]; then 2066 | iptables -w -I INPUT -p udp -m udp --dport 67 -j ACCEPT || die 2067 | if [[ $IPV6 -ne 0 ]]; then 2068 | ip6tables -w -I INPUT -p udp -m udp --dport 67 -j ACCEPT || die 2069 | fi 2070 | 2071 | if which complain > /dev/null 2>&1; then 2072 | # openSUSE's apparmor does not allow dnsmasq to read files. 2073 | # remove restriction. 2074 | complain dnsmasq 2075 | fi 2076 | 2077 | umask 0033 2078 | dnsmasq -C $CONFDIR/dnsmasq.conf -x $CONFDIR/dnsmasq.pid -l $CONFDIR/dnsmasq.leases -p $DNS_PORT || die 2079 | umask $SCRIPT_UMASK 2080 | fi 2081 | fi 2082 | 2083 | # start access point 2084 | echo "hostapd command-line interface: hostapd_cli -p $CONFDIR/hostapd_ctrl" 2085 | 2086 | if [[ $NO_HAVEGED -eq 0 ]]; then 2087 | haveged_watchdog & 2088 | HAVEGED_WATCHDOG_PID=$! 2089 | fi 2090 | 2091 | # start hostapd (use stdbuf when available for no delayed output in programs that redirect stdout) 2092 | STDBUF_PATH=`which stdbuf` 2093 | if [ $? -eq 0 ]; then 2094 | STDBUF_PATH=$STDBUF_PATH" -oL" 2095 | fi 2096 | $STDBUF_PATH $HOSTAPD $HOSTAPD_DEBUG_ARGS $CONFDIR/hostapd.conf & 2097 | HOSTAPD_PID=$! 2098 | echo $HOSTAPD_PID > $CONFDIR/hostapd.pid 2099 | 2100 | if ! wait $HOSTAPD_PID; then 2101 | echo -e "\nError: Failed to run hostapd, maybe a program is interfering." >&2 2102 | if networkmanager_is_running; then 2103 | echo "If an error like 'n80211: Could not configure driver mode' was thrown" >&2 2104 | echo "try running the following before starting create_ap:" >&2 2105 | if [[ $NM_OLDER_VERSION -eq 1 ]]; then 2106 | echo " nmcli nm wifi off" >&2 2107 | else 2108 | echo " nmcli r wifi off" >&2 2109 | fi 2110 | echo " rfkill unblock wlan" >&2 2111 | fi 2112 | die 2113 | fi 2114 | 2115 | clean_exit 2116 | 2117 | # Local Variables: 2118 | # tab-width: 4 2119 | # indent-tabs-mode: nil 2120 | # End: 2121 | 2122 | # vim: et sts=4 sw=4 2123 | --------------------------------------------------------------------------------