create_ap (51910B)
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.2 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] <wifi-interface> [<interface-with-internet>] [<access-point-name> [<passphrase>]]" 31 echo 32 echo "Options:" 33 echo " -h, --help Show this help" 34 echo " --version Print version number" 35 echo " -c <channel> Channel number (default: 1)" 36 echo " -w <WPA version> 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 <interface-with-internet> argument)" 39 echo " -m <method> 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 " --redirect-to-localhost If -n is set, redirect every web request to localhost (useful for public information networks)" 46 echo " --hostapd-debug <level> With level between 1 and 2, passes arguments -d or -dd to hostapd for debugging." 47 echo " --isolate-clients Disable communication between clients" 48 echo " --ieee80211n Enable IEEE 802.11n (HT)" 49 echo " --ht_capab <HT> HT capabilities (default: [HT40+])" 50 echo " --country <code> Set two-letter country code for regularity (example: US)" 51 echo " --freq-band <GHz> Set frequency band. Valid inputs: 2.4, 5 (default: 2.4)" 52 echo " --driver Choose your WiFi adapter driver (default: nl80211)" 53 echo " --no-virt Do not create virtual interface" 54 echo " --no-haveged Do not run 'haveged' automatically when needed" 55 echo " --fix-unmanaged If NetworkManager shows your interface as unmanaged after you" 56 echo " close create_ap, then use this option to switch your interface" 57 echo " back to managed" 58 echo " --mac <MAC> Set MAC address" 59 echo " --dhcp-dns <IP1[,IP2]> Set DNS returned by DHCP" 60 echo " --daemon Run create_ap in the background" 61 echo " --stop <id> Send stop command to an already running create_ap. For an <id>" 62 echo " you can put the PID of create_ap or the WiFi interface. You can" 63 echo " get them with --list-running" 64 echo " --list-running Show the create_ap processes that are already running" 65 echo " --list-clients <id> List the clients connected to create_ap instance associated with <id>." 66 echo " For an <id> you can put the PID of create_ap or the WiFi interface." 67 echo " If virtual WiFi interface was created, then use that one." 68 echo " You can get them with --list-running" 69 echo " --mkconfig <conf_file> Store configs in conf_file" 70 echo " --config <conf_file> Load configs from conf_file" 71 echo 72 echo "Non-Bridging Options:" 73 echo " --no-dns Disable dnsmasq DNS server" 74 echo " -g <gateway> IPv4 Gateway for the Access Point (default: 192.168.12.1)" 75 echo " -d DNS server will take into account /etc/hosts" 76 echo 77 echo "Useful informations:" 78 echo " * If you're not using the --no-virt option, then you can create an AP with the same" 79 echo " interface you are getting your Internet connection." 80 echo " * You can pass your SSID and password through pipe or through arguments (see examples)." 81 echo " * On bridge method if the <interface-with-internet> is not a bridge interface, then" 82 echo " a bridge interface is created automatically." 83 echo 84 echo "Examples:" 85 echo " "$PROGNAME" wlan0 eth0 MyAccessPoint MyPassPhrase" 86 echo " echo -e 'MyAccessPoint\nMyPassPhrase' | "$PROGNAME" wlan0 eth0" 87 echo " "$PROGNAME" wlan0 eth0 MyAccessPoint" 88 echo " echo 'MyAccessPoint' | "$PROGNAME" wlan0 eth0" 89 echo " "$PROGNAME" wlan0 wlan0 MyAccessPoint MyPassPhrase" 90 echo " "$PROGNAME" -n wlan0 MyAccessPoint MyPassPhrase" 91 echo " "$PROGNAME" -m bridge wlan0 eth0 MyAccessPoint MyPassPhrase" 92 echo " "$PROGNAME" -m bridge wlan0 br0 MyAccessPoint MyPassPhrase" 93 echo " "$PROGNAME" --driver rtl871xdrv wlan0 eth0 MyAccessPoint MyPassPhrase" 94 echo " "$PROGNAME" --daemon wlan0 eth0 MyAccessPoint MyPassPhrase" 95 echo " "$PROGNAME" --stop wlan0" 96 } 97 98 # on success it echos a non-zero unused FD 99 # on error it echos 0 100 get_avail_fd() { 101 local x 102 for x in $(seq 1 $(ulimit -n)); do 103 if [[ ! -a "/proc/$BASHPID/fd/$x" ]]; then 104 echo $x 105 return 106 fi 107 done 108 echo 0 109 } 110 111 # lock file for the mutex counter 112 COUNTER_LOCK_FILE=/tmp/create_ap.$$.lock 113 114 cleanup_lock() { 115 rm -f $COUNTER_LOCK_FILE 116 } 117 118 init_lock() { 119 local LOCK_FILE=/tmp/create_ap.all.lock 120 121 # we initialize only once 122 [[ $LOCK_FD -ne 0 ]] && return 0 123 124 LOCK_FD=$(get_avail_fd) 125 [[ $LOCK_FD -eq 0 ]] && return 1 126 127 # open/create lock file with write access for all users 128 # otherwise normal users will not be able to use it. 129 # to avoid race conditions on creation, we need to 130 # use umask to set the permissions. 131 umask 0555 132 eval "exec $LOCK_FD>$LOCK_FILE" > /dev/null 2>&1 || return 1 133 umask $SCRIPT_UMASK 134 135 # there is a case where lock file was created from a normal 136 # user. change the owner to root as soon as we can. 137 [[ $(id -u) -eq 0 ]] && chown 0:0 $LOCK_FILE 138 139 # create mutex counter lock file 140 echo 0 > $COUNTER_LOCK_FILE 141 142 return $? 143 } 144 145 # recursive mutex lock for all create_ap processes 146 mutex_lock() { 147 local counter_mutex_fd 148 local counter 149 150 # lock local mutex and read counter 151 counter_mutex_fd=$(get_avail_fd) 152 if [[ $counter_mutex_fd -ne 0 ]]; then 153 eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE" 154 flock $counter_mutex_fd 155 read -u $counter_mutex_fd counter 156 else 157 echo "Failed to lock mutex counter" >&2 158 return 1 159 fi 160 161 # lock global mutex and increase counter 162 [[ $counter -eq 0 ]] && flock $LOCK_FD 163 counter=$(( $counter + 1 )) 164 165 # write counter and unlock local mutex 166 echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd 167 eval "exec ${counter_mutex_fd}<&-" 168 return 0 169 } 170 171 # recursive mutex unlock for all create_ap processes 172 mutex_unlock() { 173 local counter_mutex_fd 174 local counter 175 176 # lock local mutex and read counter 177 counter_mutex_fd=$(get_avail_fd) 178 if [[ $counter_mutex_fd -ne 0 ]]; then 179 eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE" 180 flock $counter_mutex_fd 181 read -u $counter_mutex_fd counter 182 else 183 echo "Failed to lock mutex counter" >&2 184 return 1 185 fi 186 187 # decrease counter and unlock global mutex 188 if [[ $counter -gt 0 ]]; then 189 counter=$(( $counter - 1 )) 190 [[ $counter -eq 0 ]] && flock -u $LOCK_FD 191 fi 192 193 # write counter and unlock local mutex 194 echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd 195 eval "exec ${counter_mutex_fd}<&-" 196 return 0 197 } 198 199 # it takes 2 arguments 200 # returns: 201 # 0 if v1 (1st argument) and v2 (2nd argument) are the same 202 # 1 if v1 is less than v2 203 # 2 if v1 is greater than v2 204 version_cmp() { 205 local V1 V2 VN x 206 [[ ! $1 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!" 207 [[ ! $2 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!" 208 209 V1=( $(echo $1 | tr '.' ' ') ) 210 V2=( $(echo $2 | tr '.' ' ') ) 211 VN=${#V1[@]} 212 [[ $VN -lt ${#V2[@]} ]] && VN=${#V2[@]} 213 214 for ((x = 0; x < $VN; x++)); do 215 [[ ${V1[x]} -lt ${V2[x]} ]] && return 1 216 [[ ${V1[x]} -gt ${V2[x]} ]] && return 2 217 done 218 219 return 0 220 } 221 222 USE_IWCONFIG=0 223 224 is_interface() { 225 [[ -z "$1" ]] && return 1 226 [[ -d "/sys/class/net/${1}" ]] 227 } 228 229 is_wifi_interface() { 230 which iw > /dev/null 2>&1 && iw dev $1 info > /dev/null 2>&1 && return 0 231 if which iwconfig > /dev/null 2>&1 && iwconfig $1 > /dev/null 2>&1; then 232 USE_IWCONFIG=1 233 return 0 234 fi 235 return 1 236 } 237 238 is_bridge_interface() { 239 [[ -z "$1" ]] && return 1 240 [[ -d "/sys/class/net/${1}/bridge" ]] 241 } 242 243 get_phy_device() { 244 local x 245 for x in /sys/class/ieee80211/*; do 246 [[ ! -e "$x" ]] && continue 247 if [[ "${x##*/}" = "$1" ]]; then 248 echo $1 249 return 0 250 elif [[ -e "$x/device/net/$1" ]]; then 251 echo ${x##*/} 252 return 0 253 elif [[ -e "$x/device/net:$1" ]]; then 254 echo ${x##*/} 255 return 0 256 fi 257 done 258 echo "Failed to get phy interface" >&2 259 return 1 260 } 261 262 get_adapter_info() { 263 local PHY 264 PHY=$(get_phy_device "$1") 265 [[ $? -ne 0 ]] && return 1 266 iw phy $PHY info 267 } 268 269 get_adapter_kernel_module() { 270 local MODULE 271 MODULE=$(readlink -f "/sys/class/net/$1/device/driver/module") 272 echo ${MODULE##*/} 273 } 274 275 can_be_sta_and_ap() { 276 # iwconfig does not provide this information, assume false 277 [[ $USE_IWCONFIG -eq 1 ]] && return 1 278 get_adapter_info "$1" | grep -E '{.* managed.* AP.*}' > /dev/null 2>&1 && return 0 279 get_adapter_info "$1" | grep -E '{.* AP.* managed.*}' > /dev/null 2>&1 && return 0 280 return 1 281 } 282 283 can_be_ap() { 284 # iwconfig does not provide this information, assume true 285 [[ $USE_IWCONFIG -eq 1 ]] && return 0 286 get_adapter_info "$1" | grep -E '\* AP$' > /dev/null 2>&1 && return 0 287 return 1 288 } 289 290 can_transmit_to_channel() { 291 local IFACE CHANNEL_NUM CHANNEL_INFO 292 IFACE=$1 293 CHANNEL_NUM=$2 294 295 if [[ $USE_IWCONFIG -eq 0 ]]; then 296 if [[ $FREQ_BAND == 2.4 ]]; then 297 CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " 24[0-9][0-9] MHz \[${CHANNEL_NUM}\]") 298 else 299 CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " \(49[0-9][0-9]\|5[0-9]\{3\}\) MHz \[${CHANNEL_NUM}\]") 300 fi 301 [[ -z "${CHANNEL_INFO}" ]] && return 1 302 [[ "${CHANNEL_INFO}" == *no\ IR* ]] && return 1 303 [[ "${CHANNEL_INFO}" == *disabled* ]] && return 1 304 return 0 305 else 306 CHANNEL_NUM=$(printf '%02d' ${CHANNEL_NUM}) 307 CHANNEL_INFO=$(iwlist ${IFACE} channel | grep -E "Channel[[:blank:]]${CHANNEL_NUM}[[:blank:]]?:") 308 [[ -z "${CHANNEL_INFO}" ]] && return 1 309 return 0 310 fi 311 } 312 313 # taken from iw/util.c 314 ieee80211_frequency_to_channel() { 315 local FREQ=$1 316 if [[ $FREQ -eq 2484 ]]; then 317 echo 14 318 elif [[ $FREQ -lt 2484 ]]; then 319 echo $(( ($FREQ - 2407) / 5 )) 320 elif [[ $FREQ -ge 4910 && $FREQ -le 4980 ]]; then 321 echo $(( ($FREQ - 4000) / 5 )) 322 elif [[ $FREQ -le 45000 ]]; then 323 echo $(( ($FREQ - 5000) / 5 )) 324 elif [[ $FREQ -ge 58320 && $FREQ -le 64800 ]]; then 325 echo $(( ($FREQ - 56160) / 2160 )) 326 else 327 echo 0 328 fi 329 } 330 331 is_5ghz_frequency() { 332 [[ $1 =~ ^(49[0-9]{2})|(5[0-9]{3})$ ]] 333 } 334 335 is_wifi_connected() { 336 if [[ $USE_IWCONFIG -eq 0 ]]; then 337 iw dev "$1" link 2>&1 | grep -E '^Connected to' > /dev/null 2>&1 && return 0 338 else 339 iwconfig "$1" 2>&1 | grep -E 'Access Point: [0-9a-fA-F]{2}:' > /dev/null 2>&1 && return 0 340 fi 341 return 1 342 } 343 344 is_macaddr() { 345 echo "$1" | grep -E "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$" > /dev/null 2>&1 346 } 347 348 is_unicast_macaddr() { 349 local x 350 is_macaddr "$1" || return 1 351 x=$(echo "$1" | cut -d: -f1) 352 x=$(printf '%d' "0x${x}") 353 [[ $(expr $x % 2) -eq 0 ]] 354 } 355 356 get_macaddr() { 357 is_interface "$1" || return 358 cat "/sys/class/net/${1}/address" 359 } 360 361 get_mtu() { 362 is_interface "$1" || return 363 cat "/sys/class/net/${1}/mtu" 364 } 365 366 alloc_new_iface() { 367 local prefix=$1 368 local i=0 369 370 mutex_lock 371 while :; do 372 if ! is_interface $prefix$i && [[ ! -f $COMMON_CONFDIR/ifaces/$prefix$i ]]; then 373 mkdir -p $COMMON_CONFDIR/ifaces 374 touch $COMMON_CONFDIR/ifaces/$prefix$i 375 echo $prefix$i 376 mutex_unlock 377 return 378 fi 379 i=$((i + 1)) 380 done 381 mutex_unlock 382 } 383 384 dealloc_iface() { 385 rm -f $COMMON_CONFDIR/ifaces/$1 386 } 387 388 get_all_macaddrs() { 389 cat /sys/class/net/*/address 390 } 391 392 get_new_macaddr() { 393 local OLDMAC NEWMAC LAST_BYTE i 394 OLDMAC=$(get_macaddr "$1") 395 LAST_BYTE=$(printf %d 0x${OLDMAC##*:}) 396 mutex_lock 397 for i in {1..255}; do 398 NEWMAC="${OLDMAC%:*}:$(printf %02x $(( ($LAST_BYTE + $i) % 256 )))" 399 (get_all_macaddrs | grep "$NEWMAC" > /dev/null 2>&1) || break 400 done 401 mutex_unlock 402 echo $NEWMAC 403 } 404 405 # start haveged when needed 406 haveged_watchdog() { 407 local show_warn=1 408 while :; do 409 mutex_lock 410 if [[ $(cat /proc/sys/kernel/random/entropy_avail) -lt 1000 ]]; then 411 if ! which haveged > /dev/null 2>&1; then 412 if [[ $show_warn -eq 1 ]]; then 413 echo "WARN: Low entropy detected. We recommend you to install \`haveged'" 414 show_warn=0 415 fi 416 elif ! pidof haveged > /dev/null 2>&1; then 417 echo "Low entropy detected, starting haveged" 418 # boost low-entropy 419 haveged -w 1024 -p $COMMON_CONFDIR/haveged.pid 420 fi 421 fi 422 mutex_unlock 423 sleep 2 424 done 425 } 426 427 NETWORKMANAGER_CONF=/etc/NetworkManager/NetworkManager.conf 428 NM_OLDER_VERSION=1 429 430 networkmanager_exists() { 431 local NM_VER 432 which nmcli > /dev/null 2>&1 || return 1 433 NM_VER=$(nmcli -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+') 434 version_cmp $NM_VER 0.9.9 435 if [[ $? -eq 1 ]]; then 436 NM_OLDER_VERSION=1 437 else 438 NM_OLDER_VERSION=0 439 fi 440 return 0 441 } 442 443 networkmanager_is_running() { 444 local NMCLI_OUT 445 networkmanager_exists || return 1 446 if [[ $NM_OLDER_VERSION -eq 1 ]]; then 447 NMCLI_OUT=$(nmcli -t -f RUNNING nm) 448 else 449 NMCLI_OUT=$(nmcli -t -f RUNNING g) 450 fi 451 [[ "$NMCLI_OUT" == "running" ]] 452 } 453 454 networkmanager_iface_is_unmanaged() { 455 is_interface "$1" || return 2 456 (nmcli -t -f DEVICE,STATE d | grep -E "^$1:unmanaged$" > /dev/null 2>&1) || return 1 457 } 458 459 ADDED_UNMANAGED= 460 461 networkmanager_add_unmanaged() { 462 local MAC UNMANAGED WAS_EMPTY x 463 networkmanager_exists || return 1 464 465 [[ -d ${NETWORKMANAGER_CONF%/*} ]] || mkdir -p ${NETWORKMANAGER_CONF%/*} 466 [[ -f ${NETWORKMANAGER_CONF} ]] || touch ${NETWORKMANAGER_CONF} 467 468 if [[ $NM_OLDER_VERSION -eq 1 ]]; then 469 if [[ -z "$2" ]]; then 470 MAC=$(get_macaddr "$1") 471 else 472 MAC="$2" 473 fi 474 [[ -z "$MAC" ]] && return 1 475 fi 476 477 mutex_lock 478 UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf) 479 480 WAS_EMPTY=0 481 [[ -z "$UNMANAGED" ]] && WAS_EMPTY=1 482 UNMANAGED=$(echo "$UNMANAGED" | sed 's/unmanaged-devices=//' | tr ';,' ' ') 483 484 # if it exists, do nothing 485 for x in $UNMANAGED; do 486 if [[ $x == "mac:${MAC}" ]] || 487 [[ $NM_OLDER_VERSION -eq 0 && $x == "interface-name:${1}" ]]; then 488 mutex_unlock 489 return 2 490 fi 491 done 492 493 if [[ $NM_OLDER_VERSION -eq 1 ]]; then 494 UNMANAGED="${UNMANAGED} mac:${MAC}" 495 else 496 UNMANAGED="${UNMANAGED} interface-name:${1}" 497 fi 498 499 UNMANAGED=$(echo $UNMANAGED | sed -e 's/^ //') 500 UNMANAGED="${UNMANAGED// /;}" 501 UNMANAGED="unmanaged-devices=${UNMANAGED}" 502 503 if ! grep -E '^\[keyfile\]' ${NETWORKMANAGER_CONF} > /dev/null 2>&1; then 504 echo -e "\n\n[keyfile]\n${UNMANAGED}" >> ${NETWORKMANAGER_CONF} 505 elif [[ $WAS_EMPTY -eq 1 ]]; then 506 sed -e "s/^\(\[keyfile\].*\)$/\1\n${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 507 else 508 sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 509 fi 510 511 ADDED_UNMANAGED="${ADDED_UNMANAGED} ${1} " 512 mutex_unlock 513 514 return 0 515 } 516 517 networkmanager_rm_unmanaged() { 518 local MAC UNMANAGED 519 networkmanager_exists || return 1 520 [[ ! -f ${NETWORKMANAGER_CONF} ]] && return 1 521 522 if [[ $NM_OLDER_VERSION -eq 1 ]]; then 523 if [[ -z "$2" ]]; then 524 MAC=$(get_macaddr "$1") 525 else 526 MAC="$2" 527 fi 528 [[ -z "$MAC" ]] && return 1 529 fi 530 531 mutex_lock 532 UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf | sed 's/unmanaged-devices=//' | tr ';,' ' ') 533 534 if [[ -z "$UNMANAGED" ]]; then 535 mutex_unlock 536 return 1 537 fi 538 539 [[ -n "$MAC" ]] && UNMANAGED=$(echo $UNMANAGED | sed -e "s/mac:${MAC}\( \|$\)//g") 540 UNMANAGED=$(echo $UNMANAGED | sed -e "s/interface-name:${1}\( \|$\)//g") 541 UNMANAGED=$(echo $UNMANAGED | sed -e 's/ $//') 542 543 if [[ -z "$UNMANAGED" ]]; then 544 sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF} 545 else 546 UNMANAGED="${UNMANAGED// /;}" 547 UNMANAGED="unmanaged-devices=${UNMANAGED}" 548 sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF} 549 fi 550 551 ADDED_UNMANAGED="${ADDED_UNMANAGED/ ${1} /}" 552 mutex_unlock 553 554 return 0 555 } 556 557 networkmanager_fix_unmanaged() { 558 [[ -f ${NETWORKMANAGER_CONF} ]] || return 559 mutex_lock 560 sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF} 561 mutex_unlock 562 } 563 564 networkmanager_rm_unmanaged_if_needed() { 565 [[ $ADDED_UNMANAGED =~ .*\ ${1}\ .* ]] && networkmanager_rm_unmanaged $1 $2 566 } 567 568 networkmanager_wait_until_unmanaged() { 569 local RES 570 networkmanager_is_running || return 1 571 while :; do 572 networkmanager_iface_is_unmanaged "$1" 573 RES=$? 574 [[ $RES -eq 0 ]] && break 575 [[ $RES -eq 2 ]] && die "Interface '${1}' does not exists. 576 It's probably renamed by a udev rule." 577 sleep 1 578 done 579 sleep 2 580 return 0 581 } 582 583 584 CHANNEL=default 585 GATEWAY=192.168.12.1 586 WPA_VERSION=1+2 587 ETC_HOSTS=0 588 DHCP_DNS=gateway 589 NO_DNS=0 590 HIDDEN=0 591 ISOLATE_CLIENTS=0 592 SHARE_METHOD=nat 593 IEEE80211N=0 594 HT_CAPAB='[HT40+]' 595 DRIVER=nl80211 596 NO_VIRT=0 597 COUNTRY= 598 FREQ_BAND=2.4 599 NEW_MACADDR= 600 DAEMONIZE=0 601 NO_HAVEGED=0 602 USE_PSK=0 603 604 HOSTAPD_DEBUG_ARGS= 605 REDIRECT_TO_LOCALHOST=0 606 607 CONFIG_OPTS=(CHANNEL GATEWAY WPA_VERSION ETC_HOSTS DHCP_DNS NO_DNS HIDDEN ISOLATE_CLIENTS SHARE_METHOD 608 IEEE80211N HT_CAPAB DRIVER NO_VIRT COUNTRY FREQ_BAND 609 NEW_MACADDR DAEMONIZE NO_HAVEGED WIFI_IFACE INTERNET_IFACE 610 SSID PASSPHRASE USE_PSK) 611 612 FIX_UNMANAGED=0 613 LIST_RUNNING=0 614 STOP_ID= 615 LIST_CLIENTS_ID= 616 617 STORE_CONFIG= 618 LOAD_CONFIG= 619 620 CONFDIR= 621 WIFI_IFACE= 622 VWIFI_IFACE= 623 INTERNET_IFACE= 624 BRIDGE_IFACE= 625 OLD_MACADDR= 626 IP_ADDRS= 627 ROUTE_ADDRS= 628 629 HAVEGED_WATCHDOG_PID= 630 631 _cleanup() { 632 local PID x 633 634 trap "" SIGINT SIGUSR1 SIGUSR2 EXIT 635 mutex_lock 636 disown -a 637 638 # kill haveged_watchdog 639 [[ -n "$HAVEGED_WATCHDOG_PID" ]] && kill $HAVEGED_WATCHDOG_PID 640 641 # kill processes 642 for x in $CONFDIR/*.pid; do 643 # even if the $CONFDIR is empty, the for loop will assign 644 # a value in $x. so we need to check if the value is a file 645 [[ -f $x ]] && kill -9 $(cat $x) 646 done 647 648 rm -rf $CONFDIR 649 650 local found=0 651 for x in $(list_running_conf); do 652 if [[ -f $x/nat_internet_iface && $(cat $x/nat_internet_iface) == $INTERNET_IFACE ]]; then 653 found=1 654 break 655 fi 656 done 657 658 if [[ $found -eq 0 ]]; then 659 cp -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding \ 660 /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding 661 rm -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding 662 fi 663 664 # if we are the last create_ap instance then set back the common values 665 if ! has_running_instance; then 666 # kill common processes 667 for x in $COMMON_CONFDIR/*.pid; do 668 [[ -f $x ]] && kill -9 $(cat $x) 669 done 670 671 # set old ip_forward 672 if [[ -f $COMMON_CONFDIR/ip_forward ]]; then 673 cp -f $COMMON_CONFDIR/ip_forward /proc/sys/net/ipv4 674 rm -f $COMMON_CONFDIR/ip_forward 675 fi 676 677 # set old bridge-nf-call-iptables 678 if [[ -f $COMMON_CONFDIR/bridge-nf-call-iptables ]]; then 679 if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 680 cp -f $COMMON_CONFDIR/bridge-nf-call-iptables /proc/sys/net/bridge 681 fi 682 rm -f $COMMON_CONFDIR/bridge-nf-call-iptables 683 fi 684 685 rm -rf $COMMON_CONFDIR 686 fi 687 688 if [[ "$SHARE_METHOD" != "none" ]]; then 689 if [[ "$SHARE_METHOD" == "nat" ]]; then 690 iptables -t nat -D POSTROUTING -o ${INTERNET_IFACE} -s ${GATEWAY%.*}.0/24 -j MASQUERADE 691 iptables -D FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT 692 iptables -D FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT 693 elif [[ "$SHARE_METHOD" == "bridge" ]]; then 694 if ! is_bridge_interface $INTERNET_IFACE; then 695 ip link set dev $BRIDGE_IFACE down 696 ip link set dev $INTERNET_IFACE down 697 ip link set dev $INTERNET_IFACE promisc off 698 ip link set dev $INTERNET_IFACE nomaster 699 ip link delete $BRIDGE_IFACE type bridge 700 ip addr flush $INTERNET_IFACE 701 ip link set dev $INTERNET_IFACE up 702 dealloc_iface $BRIDGE_IFACE 703 704 for x in "${IP_ADDRS[@]}"; do 705 x="${x/inet/}" 706 x="${x/secondary/}" 707 x="${x/dynamic/}" 708 x=$(echo $x | sed 's/\([0-9]\)sec/\1/g') 709 x="${x/${INTERNET_IFACE}/}" 710 ip addr add $x dev $INTERNET_IFACE 711 done 712 713 ip route flush dev $INTERNET_IFACE 714 715 for x in "${ROUTE_ADDRS[@]}"; do 716 [[ -z "$x" ]] && continue 717 [[ "$x" == default* ]] && continue 718 ip route add $x dev $INTERNET_IFACE 719 done 720 721 for x in "${ROUTE_ADDRS[@]}"; do 722 [[ -z "$x" ]] && continue 723 [[ "$x" != default* ]] && continue 724 ip route add $x dev $INTERNET_IFACE 725 done 726 727 networkmanager_rm_unmanaged_if_needed $INTERNET_IFACE 728 fi 729 fi 730 fi 731 732 if [[ "$SHARE_METHOD" != "bridge" ]]; then 733 iptables -D INPUT -p tcp -m tcp --dport 53 -j ACCEPT 734 iptables -D INPUT -p udp -m udp --dport 53 -j ACCEPT 735 iptables -D INPUT -p udp -m udp --dport 67 -j ACCEPT 736 fi 737 738 if [[ $NO_VIRT -eq 0 ]]; then 739 if [[ -n "$VWIFI_IFACE" ]]; then 740 ip link set down dev ${VWIFI_IFACE} 741 ip addr flush ${VWIFI_IFACE} 742 networkmanager_rm_unmanaged_if_needed ${VWIFI_IFACE} ${OLD_MACADDR} 743 iw dev ${VWIFI_IFACE} del 744 dealloc_iface $VWIFI_IFACE 745 fi 746 else 747 ip link set down dev ${WIFI_IFACE} 748 ip addr flush ${WIFI_IFACE} 749 if [[ -n "$NEW_MACADDR" ]]; then 750 ip link set dev ${WIFI_IFACE} address ${OLD_MACADDR} 751 fi 752 networkmanager_rm_unmanaged_if_needed ${WIFI_IFACE} ${OLD_MACADDR} 753 fi 754 755 mutex_unlock 756 cleanup_lock 757 } 758 759 cleanup() { 760 echo 761 echo -n "Doing cleanup.. " 762 _cleanup > /dev/null 2>&1 763 echo "done" 764 } 765 766 die() { 767 [[ -n "$1" ]] && echo -e "\nERROR: $1\n" >&2 768 # send die signal to the main process 769 [[ $BASHPID -ne $$ ]] && kill -USR2 $$ 770 # we don't need to call cleanup because it's traped on EXIT 771 exit 1 772 } 773 774 clean_exit() { 775 # send clean_exit signal to the main process 776 [[ $BASHPID -ne $$ ]] && kill -USR1 $$ 777 # we don't need to call cleanup because it's traped on EXIT 778 exit 0 779 } 780 781 list_running_conf() { 782 local x 783 mutex_lock 784 for x in /tmp/create_ap.*; do 785 if [[ -f $x/pid && -f $x/wifi_iface && -d /proc/$(cat $x/pid) ]]; then 786 echo $x 787 fi 788 done 789 mutex_unlock 790 } 791 792 list_running() { 793 local IFACE wifi_iface x 794 mutex_lock 795 for x in $(list_running_conf); do 796 IFACE=${x#*.} 797 IFACE=${IFACE%%.*} 798 wifi_iface=$(cat $x/wifi_iface) 799 800 if [[ $IFACE == $wifi_iface ]]; then 801 echo $(cat $x/pid) $IFACE 802 else 803 echo $(cat $x/pid) $IFACE '('$(cat $x/wifi_iface)')' 804 fi 805 done 806 mutex_unlock 807 } 808 809 get_wifi_iface_from_pid() { 810 list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E "^${1} " | cut -d' ' -f2 811 } 812 813 get_pid_from_wifi_iface() { 814 list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E " ${1}$" | cut -d' ' -f1 815 } 816 817 get_confdir_from_pid() { 818 local IFACE x 819 mutex_lock 820 for x in $(list_running_conf); do 821 if [[ $(cat $x/pid) == "$1" ]]; then 822 echo $x 823 break 824 fi 825 done 826 mutex_unlock 827 } 828 829 print_client() { 830 local line ipaddr hostname 831 local mac="$1" 832 833 if [[ -f $CONFDIR/dnsmasq.leases ]]; then 834 line=$(grep " $mac " $CONFDIR/dnsmasq.leases | tail -n 1) 835 ipaddr=$(echo $line | cut -d' ' -f3) 836 hostname=$(echo $line | cut -d' ' -f4) 837 fi 838 839 [[ -z "$ipaddr" ]] && ipaddr="*" 840 [[ -z "$hostname" ]] && hostname="*" 841 842 printf "%-20s %-18s %s\n" "$mac" "$ipaddr" "$hostname" 843 } 844 845 list_clients() { 846 local wifi_iface pid 847 848 # If PID is given, get the associated wifi iface 849 if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then 850 pid="$1" 851 wifi_iface=$(get_wifi_iface_from_pid "$pid") 852 [[ -z "$wifi_iface" ]] && die "'$pid' is not the pid of a running $PROGNAME instance." 853 fi 854 855 [[ -z "$wifi_iface" ]] && wifi_iface="$1" 856 is_wifi_interface "$wifi_iface" || die "'$wifi_iface' is not a WiFi interface." 857 858 [[ -z "$pid" ]] && pid=$(get_pid_from_wifi_iface "$wifi_iface") 859 [[ -z "$pid" ]] && die "'$wifi_iface' is not used from $PROGNAME instance.\n\ 860 Maybe you need to pass the virtual interface instead.\n\ 861 Use --list-running to find it out." 862 [[ -z "$CONFDIR" ]] && CONFDIR=$(get_confdir_from_pid "$pid") 863 864 if [[ $USE_IWCONFIG -eq 0 ]]; then 865 local awk_cmd='($1 ~ /Station$/) {print $2}' 866 local client_list=$(iw dev "$wifi_iface" station dump | awk "$awk_cmd") 867 868 if [[ -z "$client_list" ]]; then 869 echo "No clients connected" 870 return 871 fi 872 873 printf "%-20s %-18s %s\n" "MAC" "IP" "Hostname" 874 875 local mac 876 for mac in $client_list; do 877 print_client $mac 878 done 879 else 880 die "This option is not supported for the current driver." 881 fi 882 } 883 884 has_running_instance() { 885 local PID x 886 887 mutex_lock 888 for x in /tmp/create_ap.*; do 889 if [[ -f $x/pid ]]; then 890 PID=$(cat $x/pid) 891 if [[ -d /proc/$PID ]]; then 892 mutex_unlock 893 return 0 894 fi 895 fi 896 done 897 mutex_lock 898 899 return 1 900 } 901 902 is_running_pid() { 903 list_running | grep -E "^${1} " > /dev/null 2>&1 904 } 905 906 send_stop() { 907 local x 908 909 mutex_lock 910 # send stop signal to specific pid 911 if is_running_pid $1; then 912 kill -USR1 $1 913 mutex_unlock 914 return 915 fi 916 917 # send stop signal to specific interface 918 for x in $(list_running | grep -E " \(?${1}( |\)?\$)" | cut -f1 -d' '); do 919 kill -USR1 $x 920 done 921 mutex_unlock 922 } 923 924 # Storing configs 925 write_config() { 926 local i=1 927 928 if ! eval 'echo -n > "$STORE_CONFIG"' > /dev/null 2>&1; then 929 echo "ERROR: Unable to create config file $STORE_CONFIG" >&2 930 exit 1 931 fi 932 933 WIFI_IFACE=$1 934 if [[ "$SHARE_METHOD" == "none" ]]; then 935 SSID="$2" 936 PASSPHRASE="$3" 937 else 938 INTERNET_IFACE="$2" 939 SSID="$3" 940 PASSPHRASE="$4" 941 fi 942 943 for config_opt in "${CONFIG_OPTS[@]}"; do 944 eval echo $config_opt=\$$config_opt 945 done >> "$STORE_CONFIG" 946 947 echo -e "Config options written to '$STORE_CONFIG'" 948 exit 0 949 } 950 951 is_config_opt() { 952 local elem opt="$1" 953 954 for elem in "${CONFIG_OPTS[@]}"; do 955 if [[ "$elem" == "$opt" ]]; then 956 return 0 957 fi 958 done 959 return 1 960 } 961 962 # Load options from config file 963 read_config() { 964 local opt_name opt_val line 965 966 while read line; do 967 # Read switches and their values 968 opt_name="${line%%=*}" 969 opt_val="${line#*=}" 970 if is_config_opt "$opt_name" ; then 971 eval $opt_name="\$opt_val" 972 else 973 echo "WARN: Unrecognized configuration entry $opt_name" >&2 974 fi 975 done < "$LOAD_CONFIG" 976 } 977 978 979 ARGS=( "$@" ) 980 981 # Preprocessing for --config before option-parsing starts 982 for ((i=0; i<$#; i++)); do 983 if [[ "${ARGS[i]}" = "--config" ]]; then 984 if [[ -f "${ARGS[i+1]}" ]]; then 985 LOAD_CONFIG="${ARGS[i+1]}" 986 read_config 987 else 988 echo "ERROR: No config file found at given location" >&2 989 exit 1 990 fi 991 break 992 fi 993 done 994 995 GETOPT_ARGS=$(getopt -o hc:w:g:dnm: -l "help","hidden","hostapd-debug:","redirect-to-localhost","isolate-clients","ieee80211n","ht_capab:","driver:","no-virt","fix-unmanaged","country:","freq-band:","mac:","dhcp-dns:","daemon","stop:","list","list-running","list-clients:","version","psk","no-haveged","no-dns","mkconfig:","config:" -n "$PROGNAME" -- "$@") 996 [[ $? -ne 0 ]] && exit 1 997 eval set -- "$GETOPT_ARGS" 998 999 while :; do 1000 case "$1" in 1001 -h|--help) 1002 usage 1003 exit 0 1004 ;; 1005 --version) 1006 echo $VERSION 1007 exit 0 1008 ;; 1009 --hidden) 1010 shift 1011 HIDDEN=1 1012 ;; 1013 --isolate-clients) 1014 shift 1015 ISOLATE_CLIENTS=1 1016 ;; 1017 -c) 1018 shift 1019 CHANNEL="$1" 1020 shift 1021 ;; 1022 -w) 1023 shift 1024 WPA_VERSION="$1" 1025 [[ "$WPA_VERSION" == "2+1" ]] && WPA_VERSION=1+2 1026 shift 1027 ;; 1028 -g) 1029 shift 1030 GATEWAY="$1" 1031 shift 1032 ;; 1033 -d) 1034 shift 1035 ETC_HOSTS=1 1036 ;; 1037 -n) 1038 shift 1039 SHARE_METHOD=none 1040 ;; 1041 -m) 1042 shift 1043 SHARE_METHOD="$1" 1044 shift 1045 ;; 1046 --ieee80211n) 1047 shift 1048 IEEE80211N=1 1049 ;; 1050 --ht_capab) 1051 shift 1052 HT_CAPAB="$1" 1053 shift 1054 ;; 1055 --driver) 1056 shift 1057 DRIVER="$1" 1058 shift 1059 ;; 1060 --no-virt) 1061 shift 1062 NO_VIRT=1 1063 ;; 1064 --fix-unmanaged) 1065 shift 1066 FIX_UNMANAGED=1 1067 ;; 1068 --country) 1069 shift 1070 COUNTRY="$1" 1071 shift 1072 ;; 1073 --freq-band) 1074 shift 1075 FREQ_BAND="$1" 1076 shift 1077 ;; 1078 --mac) 1079 shift 1080 NEW_MACADDR="$1" 1081 shift 1082 ;; 1083 --dhcp-dns) 1084 shift 1085 DHCP_DNS="$1" 1086 shift 1087 ;; 1088 --daemon) 1089 shift 1090 DAEMONIZE=1 1091 ;; 1092 --stop) 1093 shift 1094 STOP_ID="$1" 1095 shift 1096 ;; 1097 --list) 1098 shift 1099 LIST_RUNNING=1 1100 echo -e "WARN: --list is deprecated, use --list-running instead.\n" >&2 1101 ;; 1102 --list-running) 1103 shift 1104 LIST_RUNNING=1 1105 ;; 1106 --list-clients) 1107 shift 1108 LIST_CLIENTS_ID="$1" 1109 shift 1110 ;; 1111 --no-haveged) 1112 shift 1113 NO_HAVEGED=1 1114 ;; 1115 --psk) 1116 shift 1117 USE_PSK=1 1118 ;; 1119 --no-dns) 1120 shift 1121 NO_DNS=1 1122 ;; 1123 --redirect-to-localhost) 1124 shift 1125 REDIRECT_TO_LOCALHOST=1 1126 ;; 1127 --hostapd-debug) 1128 shift 1129 if [ "x$1" = "x1" ]; then 1130 HOSTAPD_DEBUG_ARGS="-d" 1131 elif [ "x$1" = "x2" ]; then 1132 HOSTAPD_DEBUG_ARGS="-dd" 1133 else 1134 printf "Error: argument for --hostapd-debug expected 1 or 2, got %s\n" "$1" 1135 exit 1 1136 fi 1137 shift 1138 ;; 1139 --mkconfig) 1140 shift 1141 STORE_CONFIG="$1" 1142 shift 1143 ;; 1144 --config) 1145 shift 1146 shift 1147 ;; 1148 --) 1149 shift 1150 break 1151 ;; 1152 esac 1153 done 1154 1155 # Load positional args from config file, if needed 1156 if [[ -n "$LOAD_CONFIG" && $# -eq 0 ]]; then 1157 i=0 1158 # set arguments in order 1159 for x in WIFI_IFACE INTERNET_IFACE SSID PASSPHRASE; do 1160 if eval "[[ -n \"\$${x}\" ]]"; then 1161 eval "set -- \"\${@:1:$i}\" \"\$${x}\"" 1162 ((i++)) 1163 fi 1164 # we unset the variable to avoid any problems later 1165 eval "unset $x" 1166 done 1167 fi 1168 1169 # Check if required number of positional args are present 1170 if [[ $# -lt 1 && $FIX_UNMANAGED -eq 0 && -z "$STOP_ID" && 1171 $LIST_RUNNING -eq 0 && -z "$LIST_CLIENTS_ID" ]]; then 1172 usage >&2 1173 exit 1 1174 fi 1175 1176 trap "cleanup_lock" EXIT 1177 1178 if ! init_lock; then 1179 echo "ERROR: Failed to initialize lock" >&2 1180 exit 1 1181 fi 1182 1183 # if the user press ctrl+c or we get USR1 signal 1184 # then run clean_exit() 1185 trap "clean_exit" SIGINT SIGUSR1 1186 # if we get USR2 signal then run die(). 1187 trap "die" SIGUSR2 1188 1189 [[ -n "$STORE_CONFIG" ]] && write_config "$@" 1190 1191 if [[ $LIST_RUNNING -eq 1 ]]; then 1192 echo -e "List of running $PROGNAME instances:\n" 1193 list_running 1194 exit 0 1195 fi 1196 1197 if [[ -n "$LIST_CLIENTS_ID" ]]; then 1198 list_clients "$LIST_CLIENTS_ID" 1199 exit 0 1200 fi 1201 1202 if [[ $(id -u) -ne 0 ]]; then 1203 echo "You must run it as root." >&2 1204 exit 1 1205 fi 1206 1207 if [[ -n "$STOP_ID" ]]; then 1208 echo "Trying to kill $PROGNAME instance associated with $STOP_ID..." 1209 send_stop "$STOP_ID" 1210 exit 0 1211 fi 1212 1213 if [[ $FIX_UNMANAGED -eq 1 ]]; then 1214 echo "Trying to fix unmanaged status in NetworkManager..." 1215 networkmanager_fix_unmanaged 1216 exit 0 1217 fi 1218 1219 if [[ $DAEMONIZE -eq 1 && $RUNNING_AS_DAEMON -eq 0 ]]; then 1220 echo "Running as Daemon..." 1221 # run a detached create_ap 1222 RUNNING_AS_DAEMON=1 setsid "$0" "${ARGS[@]}" & 1223 exit 0 1224 fi 1225 1226 if [[ $FREQ_BAND != 2.4 && $FREQ_BAND != 5 ]]; then 1227 echo "ERROR: Invalid frequency band" >&2 1228 exit 1 1229 fi 1230 1231 if [[ $CHANNEL == default ]]; then 1232 if [[ $FREQ_BAND == 2.4 ]]; then 1233 CHANNEL=1 1234 else 1235 CHANNEL=36 1236 fi 1237 fi 1238 1239 if [[ $FREQ_BAND != 5 && $CHANNEL -gt 14 ]]; then 1240 echo "Channel number is greater than 14, assuming 5GHz frequency band" 1241 FREQ_BAND=5 1242 fi 1243 1244 WIFI_IFACE=$1 1245 1246 if ! is_wifi_interface ${WIFI_IFACE}; then 1247 echo "ERROR: '${WIFI_IFACE}' is not a WiFi interface" >&2 1248 exit 1 1249 fi 1250 1251 if ! can_be_ap ${WIFI_IFACE}; then 1252 echo "ERROR: Your adapter does not support AP (master) mode" >&2 1253 exit 1 1254 fi 1255 1256 if ! can_be_sta_and_ap ${WIFI_IFACE}; then 1257 if is_wifi_connected ${WIFI_IFACE}; then 1258 echo "ERROR: Your adapter can not be a station (i.e. be connected) and an AP at the same time" >&2 1259 exit 1 1260 elif [[ $NO_VIRT -eq 0 ]]; then 1261 echo "WARN: Your adapter does not fully support AP virtual interface, enabling --no-virt" >&2 1262 NO_VIRT=1 1263 fi 1264 fi 1265 1266 if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^(8192[cd][ue]|8723a[sue])$ ]]; then 1267 if ! strings $(which hostapd) | grep -m1 rtl871xdrv > /dev/null 2>&1; then 1268 echo "ERROR: You need to patch your hostapd with rtl871xdrv patches." >&2 1269 exit 1 1270 fi 1271 1272 if [[ $DRIVER != "rtl871xdrv" ]]; then 1273 echo "WARN: Your adapter needs rtl871xdrv, enabling --driver=rtl871xdrv" >&2 1274 DRIVER=rtl871xdrv 1275 fi 1276 fi 1277 1278 if [[ "$SHARE_METHOD" != "nat" && "$SHARE_METHOD" != "bridge" && "$SHARE_METHOD" != "none" ]]; then 1279 echo "ERROR: Wrong Internet sharing method" >&2 1280 echo 1281 usage >&2 1282 exit 1 1283 fi 1284 1285 if [[ -n "$NEW_MACADDR" ]]; then 1286 if ! is_macaddr "$NEW_MACADDR"; then 1287 echo "ERROR: '${NEW_MACADDR}' is not a valid MAC address" >&2 1288 exit 1 1289 fi 1290 1291 if ! is_unicast_macaddr "$NEW_MACADDR"; then 1292 echo "ERROR: The first byte of MAC address (${NEW_MACADDR}) must be even" >&2 1293 exit 1 1294 fi 1295 1296 if [[ $(get_all_macaddrs | grep -c ${NEW_MACADDR}) -ne 0 ]]; then 1297 echo "WARN: MAC address '${NEW_MACADDR}' already exists. Because of this, you may encounter some problems" >&2 1298 fi 1299 fi 1300 1301 if [[ "$SHARE_METHOD" != "none" ]]; then 1302 MIN_REQUIRED_ARGS=2 1303 else 1304 MIN_REQUIRED_ARGS=1 1305 fi 1306 1307 if [[ $# -gt $MIN_REQUIRED_ARGS ]]; then 1308 if [[ "$SHARE_METHOD" != "none" ]]; then 1309 if [[ $# -ne 3 && $# -ne 4 ]]; then 1310 usage >&2 1311 exit 1 1312 fi 1313 INTERNET_IFACE="$2" 1314 SSID="$3" 1315 PASSPHRASE="$4" 1316 else 1317 if [[ $# -ne 2 && $# -ne 3 ]]; then 1318 usage >&2 1319 exit 1 1320 fi 1321 SSID="$2" 1322 PASSPHRASE="$3" 1323 fi 1324 else 1325 if [[ "$SHARE_METHOD" != "none" ]]; then 1326 if [[ $# -ne 2 ]]; then 1327 usage >&2 1328 exit 1 1329 fi 1330 INTERNET_IFACE="$2" 1331 fi 1332 if tty -s; then 1333 while :; do 1334 read -p "SSID: " SSID 1335 if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then 1336 echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2 1337 continue 1338 fi 1339 break 1340 done 1341 while :; do 1342 if [[ $USE_PSK -eq 0 ]]; then 1343 read -p "Passphrase: " -s PASSPHRASE 1344 echo 1345 if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then 1346 echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2 1347 continue 1348 fi 1349 read -p "Retype passphrase: " -s PASSPHRASE2 1350 echo 1351 if [[ "$PASSPHRASE" != "$PASSPHRASE2" ]]; then 1352 echo "Passphrases do not match." 1353 else 1354 break 1355 fi 1356 else 1357 read -p "PSK: " PASSPHRASE 1358 echo 1359 if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then 1360 echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2 1361 continue 1362 fi 1363 fi 1364 done 1365 else 1366 read SSID 1367 read PASSPHRASE 1368 fi 1369 fi 1370 1371 if [[ "$SHARE_METHOD" != "none" ]] && ! is_interface $INTERNET_IFACE; then 1372 echo "ERROR: '${INTERNET_IFACE}' is not an interface" >&2 1373 exit 1 1374 fi 1375 1376 if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then 1377 echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2 1378 exit 1 1379 fi 1380 1381 if [[ $USE_PSK -eq 0 ]]; then 1382 if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then 1383 echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2 1384 exit 1 1385 fi 1386 elif [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then 1387 echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2 1388 exit 1 1389 fi 1390 1391 if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^rtl[0-9].*$ ]]; then 1392 if [[ -n "$PASSPHRASE" ]]; then 1393 echo "WARN: Realtek drivers usually have problems with WPA1, enabling -w 2" >&2 1394 WPA_VERSION=2 1395 fi 1396 echo "WARN: If AP doesn't work, please read: howto/realtek.md" >&2 1397 fi 1398 1399 if [[ $NO_VIRT -eq 1 && "$WIFI_IFACE" == "$INTERNET_IFACE" ]]; then 1400 echo -n "ERROR: You can not share your connection from the same" >&2 1401 echo " interface if you are using --no-virt option." >&2 1402 exit 1 1403 fi 1404 1405 mutex_lock 1406 trap "cleanup" EXIT 1407 CONFDIR=$(mktemp -d /tmp/create_ap.${WIFI_IFACE}.conf.XXXXXXXX) 1408 echo "Config dir: $CONFDIR" 1409 echo "PID: $$" 1410 echo $$ > $CONFDIR/pid 1411 1412 # to make --list-running work from any user, we must give read 1413 # permissions to $CONFDIR and $CONFDIR/pid 1414 chmod 755 $CONFDIR 1415 chmod 444 $CONFDIR/pid 1416 1417 COMMON_CONFDIR=/tmp/create_ap.common.conf 1418 mkdir -p $COMMON_CONFDIR 1419 1420 if [[ "$SHARE_METHOD" == "nat" ]]; then 1421 echo $INTERNET_IFACE > $CONFDIR/nat_internet_iface 1422 cp -n /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding \ 1423 $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding 1424 fi 1425 cp -n /proc/sys/net/ipv4/ip_forward $COMMON_CONFDIR 1426 if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 1427 cp -n /proc/sys/net/bridge/bridge-nf-call-iptables $COMMON_CONFDIR 1428 fi 1429 mutex_unlock 1430 1431 if [[ "$SHARE_METHOD" == "bridge" ]]; then 1432 if is_bridge_interface $INTERNET_IFACE; then 1433 BRIDGE_IFACE=$INTERNET_IFACE 1434 else 1435 BRIDGE_IFACE=$(alloc_new_iface br) 1436 fi 1437 fi 1438 1439 if [[ $USE_IWCONFIG -eq 0 ]]; then 1440 iw dev ${WIFI_IFACE} set power_save off 1441 fi 1442 1443 if [[ $NO_VIRT -eq 0 ]]; then 1444 VWIFI_IFACE=$(alloc_new_iface ap) 1445 1446 # in NetworkManager 0.9.9 and above we can set the interface as unmanaged without 1447 # the need of MAC address, so we set it before we create the virtual interface. 1448 if networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]]; then 1449 echo -n "Network Manager found, set ${VWIFI_IFACE} as unmanaged device... " 1450 networkmanager_add_unmanaged ${VWIFI_IFACE} 1451 # do not call networkmanager_wait_until_unmanaged because interface does not 1452 # exist yet 1453 echo "DONE" 1454 fi 1455 1456 if is_wifi_connected ${WIFI_IFACE}; then 1457 WIFI_IFACE_FREQ=$(iw dev ${WIFI_IFACE} link | grep -i freq | awk '{print $2}') 1458 WIFI_IFACE_CHANNEL=$(ieee80211_frequency_to_channel ${WIFI_IFACE_FREQ}) 1459 echo -n "${WIFI_IFACE} is already associated with channel ${WIFI_IFACE_CHANNEL} (${WIFI_IFACE_FREQ} MHz)" 1460 if is_5ghz_frequency $WIFI_IFACE_FREQ; then 1461 FREQ_BAND=5 1462 else 1463 FREQ_BAND=2.4 1464 fi 1465 if [[ $WIFI_IFACE_CHANNEL -ne $CHANNEL ]]; then 1466 echo ", fallback to channel ${WIFI_IFACE_CHANNEL}" 1467 CHANNEL=$WIFI_IFACE_CHANNEL 1468 else 1469 echo 1470 fi 1471 fi 1472 1473 VIRTDIEMSG="Maybe your WiFi adapter does not fully support virtual interfaces. 1474 Try again with --no-virt." 1475 echo -n "Creating a virtual WiFi interface... " 1476 1477 if iw dev ${WIFI_IFACE} interface add ${VWIFI_IFACE} type managed; then 1478 # now we can call networkmanager_wait_until_unmanaged 1479 networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]] && networkmanager_wait_until_unmanaged ${VWIFI_IFACE} 1480 echo "${VWIFI_IFACE} created." 1481 else 1482 VWIFI_IFACE= 1483 die "$VIRTDIEMSG" 1484 fi 1485 OLD_MACADDR=$(get_macaddr ${VWIFI_IFACE}) 1486 if [[ -z "$NEW_MACADDR" && $(get_all_macaddrs | grep -c ${OLD_MACADDR}) -ne 1 ]]; then 1487 NEW_MACADDR=$(get_new_macaddr ${VWIFI_IFACE}) 1488 fi 1489 WIFI_IFACE=${VWIFI_IFACE} 1490 else 1491 OLD_MACADDR=$(get_macaddr ${WIFI_IFACE}) 1492 fi 1493 1494 mutex_lock 1495 echo $WIFI_IFACE > $CONFDIR/wifi_iface 1496 chmod 444 $CONFDIR/wifi_iface 1497 mutex_unlock 1498 1499 can_transmit_to_channel ${WIFI_IFACE} ${CHANNEL} || die "Your adapter can not transmit to channel ${CHANNEL}, frequency band ${FREQ_BAND}GHz." 1500 1501 if networkmanager_is_running && ! networkmanager_iface_is_unmanaged ${WIFI_IFACE}; then 1502 echo -n "Network Manager found, set ${WIFI_IFACE} as unmanaged device... " 1503 networkmanager_add_unmanaged ${WIFI_IFACE} 1504 networkmanager_wait_until_unmanaged ${WIFI_IFACE} 1505 echo "DONE" 1506 fi 1507 1508 [[ $HIDDEN -eq 1 ]] && echo "Access Point's SSID is hidden!" 1509 1510 [[ $ISOLATE_CLIENTS -eq 1 ]] && echo "Access Point's clients will be isolated!" 1511 1512 # hostapd config 1513 cat << EOF > $CONFDIR/hostapd.conf 1514 beacon_int=100 1515 ssid=${SSID} 1516 interface=${WIFI_IFACE} 1517 driver=${DRIVER} 1518 channel=${CHANNEL} 1519 ctrl_interface=$CONFDIR/hostapd_ctrl 1520 ctrl_interface_group=0 1521 ignore_broadcast_ssid=$HIDDEN 1522 ap_isolate=$ISOLATE_CLIENTS 1523 EOF 1524 1525 if [[ -n $COUNTRY ]]; then 1526 [[ $USE_IWCONFIG -eq 0 ]] && iw reg set $COUNTRY 1527 echo "country_code=${COUNTRY}" >> $CONFDIR/hostapd.conf 1528 fi 1529 1530 if [[ $FREQ_BAND == 2.4 ]]; then 1531 echo "hw_mode=g" >> $CONFDIR/hostapd.conf 1532 else 1533 echo "hw_mode=a" >> $CONFDIR/hostapd.conf 1534 fi 1535 1536 if [[ $IEEE80211N -eq 1 ]]; then 1537 cat << EOF >> $CONFDIR/hostapd.conf 1538 ieee80211n=1 1539 wmm_enabled=1 1540 ht_capab=${HT_CAPAB} 1541 EOF 1542 fi 1543 1544 if [[ -n "$PASSPHRASE" ]]; then 1545 [[ "$WPA_VERSION" == "1+2" ]] && WPA_VERSION=3 1546 if [[ $USE_PSK -eq 0 ]]; then 1547 WPA_KEY_TYPE=passphrase 1548 else 1549 WPA_KEY_TYPE=psk 1550 fi 1551 cat << EOF >> $CONFDIR/hostapd.conf 1552 wpa=${WPA_VERSION} 1553 wpa_${WPA_KEY_TYPE}=${PASSPHRASE} 1554 wpa_key_mgmt=WPA-PSK 1555 wpa_pairwise=TKIP CCMP 1556 rsn_pairwise=CCMP 1557 EOF 1558 fi 1559 1560 if [[ "$SHARE_METHOD" == "bridge" ]]; then 1561 echo "bridge=${BRIDGE_IFACE}" >> $CONFDIR/hostapd.conf 1562 else 1563 # dnsmasq config (dhcp + dns) 1564 DNSMASQ_VER=$(dnsmasq -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+') 1565 version_cmp $DNSMASQ_VER 2.63 1566 if [[ $? -eq 1 ]]; then 1567 DNSMASQ_BIND=bind-interfaces 1568 else 1569 DNSMASQ_BIND=bind-dynamic 1570 fi 1571 if [[ "$DHCP_DNS" == "gateway" ]]; then 1572 DHCP_DNS="$GATEWAY" 1573 fi 1574 cat << EOF > $CONFDIR/dnsmasq.conf 1575 listen-address=${GATEWAY} 1576 ${DNSMASQ_BIND} 1577 dhcp-range=${GATEWAY%.*}.1,${GATEWAY%.*}.254,255.255.255.0,24h 1578 dhcp-option-force=option:router,${GATEWAY} 1579 dhcp-option-force=option:dns-server,${DHCP_DNS} 1580 EOF 1581 MTU=$(get_mtu $INTERNET_IFACE) 1582 [[ -n "$MTU" ]] && echo "dhcp-option-force=option:mtu,${MTU}" >> $CONFDIR/dnsmasq.conf 1583 [[ $ETC_HOSTS -eq 0 ]] && echo no-hosts >> $CONFDIR/dnsmasq.conf 1584 if [[ "$SHARE_METHOD" == "none" && "$REDIRECT_TO_LOCALHOST" == "1" ]]; then 1585 cat << EOF >> $CONFDIR/dnsmasq.conf 1586 address=/#/$GATEWAY 1587 EOF 1588 fi 1589 fi 1590 1591 # initialize WiFi interface 1592 if [[ $NO_VIRT -eq 0 && -n "$NEW_MACADDR" ]]; then 1593 ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die "$VIRTDIEMSG" 1594 fi 1595 1596 ip link set down dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1597 ip addr flush ${WIFI_IFACE} || die "$VIRTDIEMSG" 1598 1599 if [[ $NO_VIRT -eq 1 && -n "$NEW_MACADDR" ]]; then 1600 ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die 1601 fi 1602 1603 if [[ "$SHARE_METHOD" != "bridge" ]]; then 1604 ip link set up dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1605 ip addr add ${GATEWAY}/24 broadcast ${GATEWAY%.*}.255 dev ${WIFI_IFACE} || die "$VIRTDIEMSG" 1606 fi 1607 1608 # enable Internet sharing 1609 if [[ "$SHARE_METHOD" != "none" ]]; then 1610 echo "Sharing Internet using method: $SHARE_METHOD" 1611 if [[ "$SHARE_METHOD" == "nat" ]]; then 1612 iptables -t nat -I POSTROUTING -o ${INTERNET_IFACE} -s ${GATEWAY%.*}.0/24 -j MASQUERADE || die 1613 iptables -I FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT || die 1614 iptables -I FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT || die 1615 echo 1 > /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding || die 1616 echo 1 > /proc/sys/net/ipv4/ip_forward || die 1617 # to enable clients to establish PPTP connections we must 1618 # load nf_nat_pptp module 1619 modprobe nf_nat_pptp > /dev/null 2>&1 1620 elif [[ "$SHARE_METHOD" == "bridge" ]]; then 1621 # disable iptables rules for bridged interfaces 1622 if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then 1623 echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables 1624 fi 1625 1626 # to initialize the bridge interface correctly we need to do the following: 1627 # 1628 # 1) save the IPs and route table of INTERNET_IFACE 1629 # 2) if NetworkManager is running set INTERNET_IFACE as unmanaged 1630 # 3) create BRIDGE_IFACE and attach INTERNET_IFACE to it 1631 # 4) set the previously saved IPs and route table to BRIDGE_IFACE 1632 # 1633 # we need the above because BRIDGE_IFACE is the master interface from now on 1634 # and it must know where is connected, otherwise connection is lost. 1635 if ! is_bridge_interface $INTERNET_IFACE; then 1636 echo -n "Create a bridge interface... " 1637 OLD_IFS="$IFS" 1638 IFS=$'\n' 1639 1640 IP_ADDRS=( $(ip addr show $INTERNET_IFACE | grep -A 1 -E 'inet[[:blank:]]' | paste - -) ) 1641 ROUTE_ADDRS=( $(ip route show dev $INTERNET_IFACE) ) 1642 1643 IFS="$OLD_IFS" 1644 1645 if networkmanager_is_running; then 1646 networkmanager_add_unmanaged $INTERNET_IFACE 1647 networkmanager_wait_until_unmanaged $INTERNET_IFACE 1648 fi 1649 1650 # create bridge interface 1651 ip link add name $BRIDGE_IFACE type bridge || die 1652 ip link set dev $BRIDGE_IFACE up || die 1653 # set 0ms forward delay 1654 echo 0 > /sys/class/net/$BRIDGE_IFACE/bridge/forward_delay 1655 1656 # attach internet interface to bridge interface 1657 ip link set dev $INTERNET_IFACE promisc on || die 1658 ip link set dev $INTERNET_IFACE up || die 1659 ip link set dev $INTERNET_IFACE master $BRIDGE_IFACE || die 1660 1661 ip addr flush $INTERNET_IFACE 1662 for x in "${IP_ADDRS[@]}"; do 1663 x="${x/inet/}" 1664 x="${x/secondary/}" 1665 x="${x/dynamic/}" 1666 x=$(echo $x | sed 's/\([0-9]\)sec/\1/g') 1667 x="${x/${INTERNET_IFACE}/}" 1668 ip addr add $x dev $BRIDGE_IFACE || die 1669 done 1670 1671 # remove any existing entries that were added from 'ip addr add' 1672 ip route flush dev $INTERNET_IFACE 1673 ip route flush dev $BRIDGE_IFACE 1674 1675 # we must first add the entries that specify the subnets and then the 1676 # gateway entry, otherwise 'ip addr add' will return an error 1677 for x in "${ROUTE_ADDRS[@]}"; do 1678 [[ "$x" == default* ]] && continue 1679 ip route add $x dev $BRIDGE_IFACE || die 1680 done 1681 1682 for x in "${ROUTE_ADDRS[@]}"; do 1683 [[ "$x" != default* ]] && continue 1684 ip route add $x dev $BRIDGE_IFACE || die 1685 done 1686 1687 echo "$BRIDGE_IFACE created." 1688 fi 1689 fi 1690 else 1691 echo "No Internet sharing" 1692 fi 1693 1694 # start dhcp + dns (optional) 1695 if [[ "$SHARE_METHOD" != "bridge" ]]; then 1696 if [[ $NO_DNS -eq 0 ]]; then 1697 DNS_PORT=53 1698 iptables -I INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT || die 1699 iptables -I INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT || die 1700 else 1701 DNS_PORT=0 1702 fi 1703 iptables -I INPUT -p udp -m udp --dport 67 -j ACCEPT || die 1704 1705 if which complain > /dev/null 2>&1; then 1706 # openSUSE's apparmor does not allow dnsmasq to read files. 1707 # remove restriction. 1708 complain dnsmasq 1709 fi 1710 1711 umask 0033 1712 dnsmasq -C $CONFDIR/dnsmasq.conf -x $CONFDIR/dnsmasq.pid -l $CONFDIR/dnsmasq.leases -p $DNS_PORT || die 1713 umask $SCRIPT_UMASK 1714 fi 1715 1716 # start access point 1717 echo "hostapd command-line interface: hostapd_cli -p $CONFDIR/hostapd_ctrl" 1718 1719 if [[ $NO_HAVEGED -eq 0 ]]; then 1720 haveged_watchdog & 1721 HAVEGED_WATCHDOG_PID=$! 1722 fi 1723 1724 # start hostapd (use stdbuf for no delayed output in porgrams that redirect stdout) 1725 stdbuf -oL hostapd $HOSTAPD_DEBUG_ARGS $CONFDIR/hostapd.conf & 1726 HOSTAPD_PID=$! 1727 echo $HOSTAPD_PID > $CONFDIR/hostapd.pid 1728 1729 if ! wait $HOSTAPD_PID; then 1730 echo -e "\nError: Failed to run hostapd, maybe a program is interfering." >&2 1731 if networkmanager_is_running; then 1732 echo "If an error like 'n80211: Could not configure driver mode' was thrown" >&2 1733 echo "try running the following before starting create_ap:" >&2 1734 if [[ $NM_OLDER_VERSION -eq 1 ]]; then 1735 echo " nmcli nm wifi off" >&2 1736 else 1737 echo " nmcli r wifi off" >&2 1738 fi 1739 echo " rfkill unblock wlan" >&2 1740 fi 1741 die 1742 fi 1743 1744 clean_exit 1745 1746 # Local Variables: 1747 # tab-width: 4 1748 # indent-tabs-mode: nil 1749 # End: 1750 1751 # vim: et sts=4 sw=4