Set two-letter country code for regularity (example: US)"
+ echo " --freq-band Set frequency band. Valid inputs: 2.4, 5 (default: 2.4)"
+ echo " --driver Choose your WiFi adapter driver (default: nl80211)"
+ echo " --no-virt Do not create virtual interface"
+ echo " --no-haveged Do not run 'haveged' automatically when needed"
+ echo " --fix-unmanaged If NetworkManager shows your interface as unmanaged after you"
+ echo " close create_ap, then use this option to switch your interface"
+ echo " back to managed"
+ echo " --mac Set MAC address"
+ echo " --dhcp-dns Set DNS returned by DHCP"
+ echo " --daemon Run create_ap in the background"
+ echo " --pidfile Save daemon PID to file"
+ echo " --logfile Save daemon messages to file"
+ echo " --stop Send stop command to an already running create_ap. For an "
+ echo " you can put the PID of create_ap or the WiFi interface. You can"
+ echo " get them with --list-running"
+ echo " --list-running Show the create_ap processes that are already running"
+ echo " --list-clients List the clients connected to create_ap instance associated with ."
+ echo " For an you can put the PID of create_ap or the WiFi interface."
+ echo " If virtual WiFi interface was created, then use that one."
+ echo " You can get them with --list-running"
+ echo " --mkconfig Store configs in conf_file"
+ echo " --config Load configs from conf_file"
+ echo
+ echo "Non-Bridging Options:"
+ echo " --no-dns Disable dnsmasq DNS server"
+ echo " --no-dnsmasq Disable dnsmasq server completely"
+ echo " -g IPv4 Gateway for the Access Point (default: 192.168.12.1)"
+ echo " -d DNS server will take into account /etc/hosts"
+ echo " -e DNS server will take into account additional hosts file"
+ echo
+ echo "Useful informations:"
+ echo " * If you're not using the --no-virt option, then you can create an AP with the same"
+ echo " interface you are getting your Internet connection."
+ echo " * You can pass your SSID and password through pipe or through arguments (see examples)."
+ echo " * On bridge method if the is not a bridge interface, then"
+ echo " a bridge interface is created automatically."
+ echo
+ echo "Examples:"
+ echo " "$PROGNAME" wlan0 eth0 MyAccessPoint MyPassPhrase"
+ echo " echo -e 'MyAccessPoint\nMyPassPhrase' | "$PROGNAME" wlan0 eth0"
+ echo " "$PROGNAME" wlan0 eth0 MyAccessPoint"
+ echo " echo 'MyAccessPoint' | "$PROGNAME" wlan0 eth0"
+ echo " "$PROGNAME" wlan0 wlan0 MyAccessPoint MyPassPhrase"
+ echo " "$PROGNAME" -n wlan0 MyAccessPoint MyPassPhrase"
+ echo " "$PROGNAME" -m bridge wlan0 eth0 MyAccessPoint MyPassPhrase"
+ echo " "$PROGNAME" -m bridge wlan0 br0 MyAccessPoint MyPassPhrase"
+ echo " "$PROGNAME" --driver rtl871xdrv wlan0 eth0 MyAccessPoint MyPassPhrase"
+ echo " "$PROGNAME" --daemon wlan0 eth0 MyAccessPoint MyPassPhrase"
+ echo " "$PROGNAME" --stop wlan0"
+}
+
+# Busybox polyfills
+if cp --help 2>&1 | grep -q -- --no-clobber; then
+ cp_n() {
+ cp -n "$@"
+ }
+else
+ cp_n() {
+ yes n | cp -i "$@"
+ }
+fi
+
+# on success it echos a non-zero unused FD
+# on error it echos 0
+get_avail_fd() {
+ local x
+ for x in $(seq 1 $(ulimit -n)); do
+ if [[ ! -a "/proc/$BASHPID/fd/$x" ]]; then
+ echo $x
+ return
+ fi
+ done
+ echo 0
+}
+
+# lock file for the mutex counter
+COUNTER_LOCK_FILE=/tmp/create_ap.$$.lock
+
+cleanup_lock() {
+ rm -f $COUNTER_LOCK_FILE
+}
+
+init_lock() {
+ local LOCK_FILE=/tmp/create_ap.all.lock
+
+ # we initialize only once
+ [[ $LOCK_FD -ne 0 ]] && return 0
+
+ LOCK_FD=$(get_avail_fd)
+ [[ $LOCK_FD -eq 0 ]] && return 1
+
+ # open/create lock file with write access for all users
+ # otherwise normal users will not be able to use it.
+ # to avoid race conditions on creation, we need to
+ # use umask to set the permissions.
+ umask 0555
+ eval "exec $LOCK_FD>$LOCK_FILE" > /dev/null 2>&1 || return 1
+ umask $SCRIPT_UMASK
+
+ # there is a case where lock file was created from a normal
+ # user. change the owner to root as soon as we can.
+ [[ $(id -u) -eq 0 ]] && chown 0:0 $LOCK_FILE
+
+ # create mutex counter lock file
+ echo 0 > $COUNTER_LOCK_FILE
+
+ return $?
+}
+
+# recursive mutex lock for all create_ap processes
+mutex_lock() {
+ local counter_mutex_fd
+ local counter
+
+ # lock local mutex and read counter
+ counter_mutex_fd=$(get_avail_fd)
+ if [[ $counter_mutex_fd -ne 0 ]]; then
+ eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE"
+ flock $counter_mutex_fd
+ read -u $counter_mutex_fd counter
+ else
+ echo "Failed to lock mutex counter" >&2
+ return 1
+ fi
+
+ # lock global mutex and increase counter
+ [[ $counter -eq 0 ]] && flock $LOCK_FD
+ counter=$(( $counter + 1 ))
+
+ # write counter and unlock local mutex
+ echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd
+ eval "exec ${counter_mutex_fd}<&-"
+ return 0
+}
+
+# recursive mutex unlock for all create_ap processes
+mutex_unlock() {
+ local counter_mutex_fd
+ local counter
+
+ # lock local mutex and read counter
+ counter_mutex_fd=$(get_avail_fd)
+ if [[ $counter_mutex_fd -ne 0 ]]; then
+ eval "exec $counter_mutex_fd<>$COUNTER_LOCK_FILE"
+ flock $counter_mutex_fd
+ read -u $counter_mutex_fd counter
+ else
+ echo "Failed to lock mutex counter" >&2
+ return 1
+ fi
+
+ # decrease counter and unlock global mutex
+ if [[ $counter -gt 0 ]]; then
+ counter=$(( $counter - 1 ))
+ [[ $counter -eq 0 ]] && flock -u $LOCK_FD
+ fi
+
+ # write counter and unlock local mutex
+ echo $counter > /proc/$BASHPID/fd/$counter_mutex_fd
+ eval "exec ${counter_mutex_fd}<&-"
+ return 0
+}
+
+# it takes 2 arguments
+# returns:
+# 0 if v1 (1st argument) and v2 (2nd argument) are the same
+# 1 if v1 is less than v2
+# 2 if v1 is greater than v2
+version_cmp() {
+ local V1 V2 VN x
+ [[ ! $1 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!"
+ [[ ! $2 =~ ^[0-9]+(\.[0-9]+)*$ ]] && die "Wrong version format!"
+
+ V1=( $(echo $1 | tr '.' ' ') )
+ V2=( $(echo $2 | tr '.' ' ') )
+ VN=${#V1[@]}
+ [[ $VN -lt ${#V2[@]} ]] && VN=${#V2[@]}
+
+ for ((x = 0; x < $VN; x++)); do
+ [[ ${V1[x]} -lt ${V2[x]} ]] && return 1
+ [[ ${V1[x]} -gt ${V2[x]} ]] && return 2
+ done
+
+ return 0
+}
+
+USE_IWCONFIG=0
+
+is_interface() {
+ [[ -z "$1" ]] && return 1
+ [[ -d "/sys/class/net/${1}" ]]
+}
+
+is_wifi_interface() {
+ which iw > /dev/null 2>&1 && iw dev $1 info > /dev/null 2>&1 && return 0
+ if which iwconfig > /dev/null 2>&1 && iwconfig $1 > /dev/null 2>&1; then
+ USE_IWCONFIG=1
+ return 0
+ fi
+ return 1
+}
+
+is_bridge_interface() {
+ [[ -z "$1" ]] && return 1
+ [[ -d "/sys/class/net/${1}/bridge" ]]
+}
+
+get_phy_device() {
+ local x
+ for x in /sys/class/ieee80211/*; do
+ [[ ! -e "$x" ]] && continue
+ if [[ "${x##*/}" = "$1" ]]; then
+ echo $1
+ return 0
+ elif [[ -e "$x/device/net/$1" ]]; then
+ echo ${x##*/}
+ return 0
+ elif [[ -e "$x/device/net:$1" ]]; then
+ echo ${x##*/}
+ return 0
+ else
+ if [[ -e "/sys/class/net/$1" ]]; then
+ phy=$(readlink -f /sys/class/net/$1/phy80211)
+ phy1=$(readlink -f $x)
+ if [[ $phy == $phy1 ]]; then
+ echo ${x##*/}
+ return 0
+ fi
+ fi
+ fi
+ done
+ echo "Failed to get phy interface" >&2
+ return 1
+}
+
+get_adapter_info() {
+ local PHY
+ PHY=$(get_phy_device "$1")
+ [[ $? -ne 0 ]] && return 1
+ iw phy $PHY info
+}
+
+get_adapter_kernel_module() {
+ local MODULE
+ MODULE=$(readlink -f "/sys/class/net/$1/device/driver/module")
+ echo ${MODULE##*/}
+}
+
+can_be_sta_and_ap() {
+ # iwconfig does not provide this information, assume false
+ [[ $USE_IWCONFIG -eq 1 ]] && return 1
+ if [[ "$(get_adapter_kernel_module "$1")" == "brcmfmac" ]]; then
+ echo "WARN: brmfmac driver doesn't work properly with virtual interfaces and" >&2
+ echo " it can cause kernel panic. For this reason we disallow virtual" >&2
+ echo " interfaces for your adapter." >&2
+ echo " For more info: https://github.com/oblique/create_ap/issues/203" >&2
+ return 1
+ fi
+ get_adapter_info "$1" | grep -E '{.* managed.* AP.*}' > /dev/null 2>&1 && return 0
+ get_adapter_info "$1" | grep -E '{.* AP.* managed.*}' > /dev/null 2>&1 && return 0
+ return 1
+}
+
+can_be_ap() {
+ # iwconfig does not provide this information, assume true
+ [[ $USE_IWCONFIG -eq 1 ]] && return 0
+ get_adapter_info "$1" | grep -E '\* AP$' > /dev/null 2>&1 && return 0
+ return 1
+}
+
+can_transmit_to_channel() {
+ local IFACE CHANNEL_NUM CHANNEL_INFO
+ IFACE=$1
+ CHANNEL_NUM=$2
+
+ if [[ $USE_IWCONFIG -eq 0 ]]; then
+ if [[ $FREQ_BAND == 2.4 ]]; then
+ CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " 24[0-9][0-9] MHz \[${CHANNEL_NUM}\]")
+ else
+ CHANNEL_INFO=$(get_adapter_info ${IFACE} | grep " \(49[0-9][0-9]\|5[0-9]\{3\}\) MHz \[${CHANNEL_NUM}\]")
+ fi
+ [[ -z "${CHANNEL_INFO}" ]] && return 1
+ [[ "${CHANNEL_INFO}" == *no\ IR* ]] && return 1
+ [[ "${CHANNEL_INFO}" == *disabled* ]] && return 1
+ return 0
+ else
+ CHANNEL_NUM=$(printf '%02d' ${CHANNEL_NUM})
+ CHANNEL_INFO=$(iwlist ${IFACE} channel | grep -E "Channel[[:blank:]]${CHANNEL_NUM}[[:blank:]]?:")
+ [[ -z "${CHANNEL_INFO}" ]] && return 1
+ return 0
+ fi
+}
+
+# taken from iw/util.c
+ieee80211_frequency_to_channel() {
+ local FREQ=$1
+ if [[ $FREQ -eq 2484 ]]; then
+ echo 14
+ elif [[ $FREQ -lt 2484 ]]; then
+ echo $(( ($FREQ - 2407) / 5 ))
+ elif [[ $FREQ -ge 4910 && $FREQ -le 4980 ]]; then
+ echo $(( ($FREQ - 4000) / 5 ))
+ elif [[ $FREQ -le 45000 ]]; then
+ echo $(( ($FREQ - 5000) / 5 ))
+ elif [[ $FREQ -ge 58320 && $FREQ -le 64800 ]]; then
+ echo $(( ($FREQ - 56160) / 2160 ))
+ else
+ echo 0
+ fi
+}
+
+is_5ghz_frequency() {
+ [[ $1 =~ ^(49[0-9]{2})|(5[0-9]{3})$ ]]
+}
+
+is_wifi_connected() {
+ if [[ $USE_IWCONFIG -eq 0 ]]; then
+ iw dev "$1" link 2>&1 | grep -E '^Connected to' > /dev/null 2>&1 && return 0
+ else
+ iwconfig "$1" 2>&1 | grep -E 'Access Point: [0-9a-fA-F]{2}:' > /dev/null 2>&1 && return 0
+ fi
+ return 1
+}
+
+is_macaddr() {
+ echo "$1" | grep -E "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$" > /dev/null 2>&1
+}
+
+is_unicast_macaddr() {
+ local x
+ is_macaddr "$1" || return 1
+ x=$(echo "$1" | cut -d: -f1)
+ x=$(printf '%d' "0x${x}")
+ [[ $(expr $x % 2) -eq 0 ]]
+}
+
+get_macaddr() {
+ is_interface "$1" || return
+ cat "/sys/class/net/${1}/address"
+}
+
+get_mtu() {
+ is_interface "$1" || return
+ cat "/sys/class/net/${1}/mtu"
+}
+
+alloc_new_iface() {
+ local prefix=$1
+ local i=0
+
+ mutex_lock
+ while :; do
+ if ! is_interface $prefix$i && [[ ! -f $COMMON_CONFDIR/ifaces/$prefix$i ]]; then
+ mkdir -p $COMMON_CONFDIR/ifaces
+ touch $COMMON_CONFDIR/ifaces/$prefix$i
+ echo $prefix$i
+ mutex_unlock
+ return
+ fi
+ i=$((i + 1))
+ done
+ mutex_unlock
+}
+
+dealloc_iface() {
+ rm -f $COMMON_CONFDIR/ifaces/$1
+}
+
+get_all_macaddrs() {
+ cat /sys/class/net/*/address
+}
+
+get_new_macaddr() {
+ local OLDMAC NEWMAC LAST_BYTE i
+ OLDMAC=$(get_macaddr "$1")
+ LAST_BYTE=$(printf %d 0x${OLDMAC##*:})
+ mutex_lock
+ for i in {1..255}; do
+ NEWMAC="${OLDMAC%:*}:$(printf %02x $(( ($LAST_BYTE + $i) % 256 )))"
+ (get_all_macaddrs | grep "$NEWMAC" > /dev/null 2>&1) || break
+ done
+ mutex_unlock
+ echo $NEWMAC
+}
+
+# start haveged when needed
+haveged_watchdog() {
+ local show_warn=1
+ while :; do
+ mutex_lock
+ if [[ $(cat /proc/sys/kernel/random/entropy_avail) -lt 1000 ]]; then
+ if ! which haveged > /dev/null 2>&1; then
+ if [[ $show_warn -eq 1 ]]; then
+ echo "WARN: Low entropy detected. We recommend you to install \`haveged'"
+ show_warn=0
+ fi
+ elif ! pidof haveged > /dev/null 2>&1; then
+ echo "Low entropy detected, starting haveged"
+ # boost low-entropy
+ haveged -w 1024 -p $COMMON_CONFDIR/haveged.pid
+ fi
+ fi
+ mutex_unlock
+ sleep 2
+ done
+}
+
+NETWORKMANAGER_CONF=/etc/NetworkManager/NetworkManager.conf
+NM_OLDER_VERSION=1
+
+networkmanager_exists() {
+ local NM_VER
+ which nmcli > /dev/null 2>&1 || return 1
+ NM_VER=$(nmcli -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+')
+ version_cmp $NM_VER 0.9.9
+ if [[ $? -eq 1 ]]; then
+ NM_OLDER_VERSION=1
+ else
+ NM_OLDER_VERSION=0
+ fi
+ return 0
+}
+
+networkmanager_is_running() {
+ local NMCLI_OUT
+ networkmanager_exists || return 1
+ if [[ $NM_OLDER_VERSION -eq 1 ]]; then
+ NMCLI_OUT=$(nmcli -t -f RUNNING nm 2>&1 | grep -E '^running$')
+ else
+ NMCLI_OUT=$(nmcli -t -f RUNNING g 2>&1 | grep -E '^running$')
+ fi
+ [[ -n "$NMCLI_OUT" ]]
+}
+
+networkmanager_knows_iface() {
+ # check if the interface $1 is known to NetworkManager
+ # an interface may exist but may not be known to NetworkManager if it is in a different network namespace than NetworkManager
+ nmcli -t -f DEVICE d 2>&1 | grep -Fxq "$1"
+}
+
+networkmanager_iface_is_unmanaged() {
+ is_interface "$1" || return 2
+ networkmanager_knows_iface "$1" || return 0
+ (nmcli -t -f DEVICE,STATE d 2>&1 | grep -E "^$1:unmanaged$" > /dev/null 2>&1) || return 1
+}
+
+ADDED_UNMANAGED=
+
+networkmanager_add_unmanaged() {
+ local MAC UNMANAGED WAS_EMPTY x
+ networkmanager_exists || return 1
+
+ [[ -d ${NETWORKMANAGER_CONF%/*} ]] || mkdir -p ${NETWORKMANAGER_CONF%/*}
+ [[ -f ${NETWORKMANAGER_CONF} ]] || touch ${NETWORKMANAGER_CONF}
+
+ if [[ $NM_OLDER_VERSION -eq 1 ]]; then
+ if [[ -z "$2" ]]; then
+ MAC=$(get_macaddr "$1")
+ else
+ MAC="$2"
+ fi
+ [[ -z "$MAC" ]] && return 1
+ fi
+
+ mutex_lock
+ UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf)
+
+ WAS_EMPTY=0
+ [[ -z "$UNMANAGED" ]] && WAS_EMPTY=1
+ UNMANAGED=$(echo "$UNMANAGED" | sed 's/unmanaged-devices=//' | tr ';,' ' ')
+
+ # if it exists, do nothing
+ for x in $UNMANAGED; do
+ if [[ $x == "mac:${MAC}" ]] ||
+ [[ $NM_OLDER_VERSION -eq 0 && $x == "interface-name:${1}" ]]; then
+ mutex_unlock
+ return 2
+ fi
+ done
+
+ if [[ $NM_OLDER_VERSION -eq 1 ]]; then
+ UNMANAGED="${UNMANAGED} mac:${MAC}"
+ else
+ UNMANAGED="${UNMANAGED} interface-name:${1}"
+ fi
+
+ UNMANAGED=$(echo $UNMANAGED | sed -e 's/^ //')
+ UNMANAGED="${UNMANAGED// /;}"
+ UNMANAGED="unmanaged-devices=${UNMANAGED}"
+
+ if ! grep -E '^\[keyfile\]' ${NETWORKMANAGER_CONF} > /dev/null 2>&1; then
+ echo -e "\n\n[keyfile]\n${UNMANAGED}" >> ${NETWORKMANAGER_CONF}
+ elif [[ $WAS_EMPTY -eq 1 ]]; then
+ sed -e "s/^\(\[keyfile\].*\)$/\1\n${UNMANAGED}/" -i ${NETWORKMANAGER_CONF}
+ else
+ sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF}
+ fi
+
+ ADDED_UNMANAGED="${ADDED_UNMANAGED} ${1} "
+ mutex_unlock
+
+ local nm_pid=$(pidof NetworkManager)
+ [[ -n "$nm_pid" ]] && kill -HUP $nm_pid
+
+ return 0
+}
+
+networkmanager_rm_unmanaged() {
+ local MAC UNMANAGED
+ networkmanager_exists || return 1
+ [[ ! -f ${NETWORKMANAGER_CONF} ]] && return 1
+
+ if [[ $NM_OLDER_VERSION -eq 1 ]]; then
+ if [[ -z "$2" ]]; then
+ MAC=$(get_macaddr "$1")
+ else
+ MAC="$2"
+ fi
+ [[ -z "$MAC" ]] && return 1
+ fi
+
+ mutex_lock
+ UNMANAGED=$(grep -m1 -Eo '^unmanaged-devices=[[:alnum:]:;,-]*' /etc/NetworkManager/NetworkManager.conf | sed 's/unmanaged-devices=//' | tr ';,' ' ')
+
+ if [[ -z "$UNMANAGED" ]]; then
+ mutex_unlock
+ return 1
+ fi
+
+ [[ -n "$MAC" ]] && UNMANAGED=$(echo $UNMANAGED | sed -e "s/mac:${MAC}\( \|$\)//g")
+ UNMANAGED=$(echo $UNMANAGED | sed -e "s/interface-name:${1}\( \|$\)//g")
+ UNMANAGED=$(echo $UNMANAGED | sed -e 's/ $//')
+
+ if [[ -z "$UNMANAGED" ]]; then
+ sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF}
+ else
+ UNMANAGED="${UNMANAGED// /;}"
+ UNMANAGED="unmanaged-devices=${UNMANAGED}"
+ sed -e "s/^unmanaged-devices=.*/${UNMANAGED}/" -i ${NETWORKMANAGER_CONF}
+ fi
+
+ ADDED_UNMANAGED="${ADDED_UNMANAGED/ ${1} /}"
+ mutex_unlock
+
+ local nm_pid=$(pidof NetworkManager)
+ [[ -n "$nm_pid" ]] && kill -HUP $nm_pid
+
+ return 0
+}
+
+networkmanager_fix_unmanaged() {
+ [[ -f ${NETWORKMANAGER_CONF} ]] || return
+
+ mutex_lock
+ sed -e "/^unmanaged-devices=.*/d" -i ${NETWORKMANAGER_CONF}
+ mutex_unlock
+
+ local nm_pid=$(pidof NetworkManager)
+ [[ -n "$nm_pid" ]] && kill -HUP $nm_pid
+}
+
+networkmanager_rm_unmanaged_if_needed() {
+ [[ $ADDED_UNMANAGED =~ .*\ ${1}\ .* ]] && networkmanager_rm_unmanaged $1 $2
+}
+
+networkmanager_wait_until_unmanaged() {
+ local RES
+ networkmanager_is_running || return 1
+ while :; do
+ networkmanager_iface_is_unmanaged "$1"
+ RES=$?
+ [[ $RES -eq 0 ]] && break
+ [[ $RES -eq 2 ]] && die "Interface '${1}' does not exist.
+ It's probably renamed by a udev rule."
+ sleep 1
+ done
+ sleep 2
+ return 0
+}
+
+
+CHANNEL=default
+GATEWAY=192.168.12.1
+WPA_VERSION=1+2
+ETC_HOSTS=0
+ADDN_HOSTS=
+DHCP_DNS=gateway
+NO_DNS=0
+NO_DNSMASQ=0
+DNS_PORT=
+HIDDEN=0
+MAC_FILTER=0
+MAC_FILTER_ACCEPT=/etc/hostapd/hostapd.accept
+ISOLATE_CLIENTS=0
+SHARE_METHOD=nat
+IEEE80211N=0
+IEEE80211AC=0
+HT_CAPAB='[HT40+]'
+VHT_CAPAB=
+DRIVER=nl80211
+NO_VIRT=0
+COUNTRY=
+FREQ_BAND=2.4
+NEW_MACADDR=
+DAEMONIZE=0
+DAEMON_PIDFILE=
+DAEMON_LOGFILE=/dev/null
+NO_HAVEGED=0
+USE_PSK=0
+
+HOSTAPD_DEBUG_ARGS=
+REDIRECT_TO_LOCALHOST=0
+
+CONFIG_OPTS=(CHANNEL GATEWAY WPA_VERSION ETC_HOSTS DHCP_DNS NO_DNS NO_DNSMASQ HIDDEN MAC_FILTER MAC_FILTER_ACCEPT ISOLATE_CLIENTS
+ SHARE_METHOD IEEE80211N IEEE80211AC HT_CAPAB VHT_CAPAB DRIVER NO_VIRT COUNTRY FREQ_BAND
+ NEW_MACADDR DAEMONIZE DAEMON_PIDFILE DAEMON_LOGFILE NO_HAVEGED WIFI_IFACE INTERNET_IFACE
+ SSID PASSPHRASE USE_PSK)
+
+FIX_UNMANAGED=0
+LIST_RUNNING=0
+STOP_ID=
+LIST_CLIENTS_ID=
+
+STORE_CONFIG=
+LOAD_CONFIG=
+
+CONFDIR=
+WIFI_IFACE=
+VWIFI_IFACE=
+INTERNET_IFACE=
+BRIDGE_IFACE=
+OLD_MACADDR=
+IP_ADDRS=
+ROUTE_ADDRS=
+
+HAVEGED_WATCHDOG_PID=
+
+_cleanup() {
+ local PID x
+
+ trap "" SIGINT SIGUSR1 SIGUSR2 EXIT
+ mutex_lock
+ disown -a
+
+ # kill haveged_watchdog
+ [[ -n "$HAVEGED_WATCHDOG_PID" ]] && kill $HAVEGED_WATCHDOG_PID
+
+ # kill processes
+ for x in $CONFDIR/*.pid; do
+ # even if the $CONFDIR is empty, the for loop will assign
+ # a value in $x. so we need to check if the value is a file
+ [[ -f $x ]] && kill -9 $(cat $x)
+ done
+
+ rm -rf $CONFDIR
+
+ local found=0
+ for x in $(list_running_conf); do
+ if [[ -f $x/nat_internet_iface && $(cat $x/nat_internet_iface) == $INTERNET_IFACE ]]; then
+ found=1
+ break
+ fi
+ done
+
+ if [[ $found -eq 0 ]]; then
+ cp -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding \
+ /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding
+ rm -f $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding
+ fi
+
+ # if we are the last create_ap instance then set back the common values
+ if ! has_running_instance; then
+ # kill common processes
+ for x in $COMMON_CONFDIR/*.pid; do
+ [[ -f $x ]] && kill -9 $(cat $x)
+ done
+
+ # set old ip_forward
+ if [[ -f $COMMON_CONFDIR/ip_forward ]]; then
+ cp -f $COMMON_CONFDIR/ip_forward /proc/sys/net/ipv4
+ rm -f $COMMON_CONFDIR/ip_forward
+ fi
+
+ # set old bridge-nf-call-iptables
+ if [[ -f $COMMON_CONFDIR/bridge-nf-call-iptables ]]; then
+ if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then
+ cp -f $COMMON_CONFDIR/bridge-nf-call-iptables /proc/sys/net/bridge
+ fi
+ rm -f $COMMON_CONFDIR/bridge-nf-call-iptables
+ fi
+
+ rm -rf $COMMON_CONFDIR
+ fi
+
+ if [[ "$SHARE_METHOD" != "none" ]]; then
+ if [[ "$SHARE_METHOD" == "nat" ]]; then
+ iptables -w -t nat -D POSTROUTING -s ${GATEWAY%.*}.0/24 ! -o ${WIFI_IFACE} -j MASQUERADE
+ iptables -w -D FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT
+ iptables -w -D FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT
+ elif [[ "$SHARE_METHOD" == "bridge" ]]; then
+ if ! is_bridge_interface $INTERNET_IFACE; then
+ ip link set dev $BRIDGE_IFACE down
+ ip link set dev $INTERNET_IFACE down
+ ip link set dev $INTERNET_IFACE promisc off
+ ip link set dev $INTERNET_IFACE nomaster
+ ip link delete $BRIDGE_IFACE type bridge
+ ip addr flush $INTERNET_IFACE
+ ip link set dev $INTERNET_IFACE up
+ dealloc_iface $BRIDGE_IFACE
+
+ for x in "${IP_ADDRS[@]}"; do
+ x="${x/inet/}"
+ x="${x/secondary/}"
+ x="${x/dynamic/}"
+ x=$(echo $x | sed 's/\([0-9]\)sec/\1/g')
+ x="${x/${INTERNET_IFACE}/}"
+ ip addr add $x dev $INTERNET_IFACE
+ done
+
+ ip route flush dev $INTERNET_IFACE
+
+ for x in "${ROUTE_ADDRS[@]}"; do
+ [[ -z "$x" ]] && continue
+ [[ "$x" == default* ]] && continue
+ ip route add $x dev $INTERNET_IFACE
+ done
+
+ for x in "${ROUTE_ADDRS[@]}"; do
+ [[ -z "$x" ]] && continue
+ [[ "$x" != default* ]] && continue
+ ip route add $x dev $INTERNET_IFACE
+ done
+
+ networkmanager_rm_unmanaged_if_needed $INTERNET_IFACE
+ fi
+ fi
+ fi
+
+ if [[ "$SHARE_METHOD" != "bridge" ]]; then
+ if [[ $NO_DNS -eq 0 ]]; then
+ iptables -w -D INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT
+ iptables -w -D INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT
+ iptables -w -t nat -D PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \
+ -p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT
+ iptables -w -t nat -D PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \
+ -p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT
+ fi
+ iptables -w -D INPUT -p udp -m udp --dport 67 -j ACCEPT
+ fi
+
+ if [[ $NO_VIRT -eq 0 ]]; then
+ if [[ -n "$VWIFI_IFACE" ]]; then
+ ip link set down dev ${VWIFI_IFACE}
+ ip addr flush ${VWIFI_IFACE}
+ networkmanager_rm_unmanaged_if_needed ${VWIFI_IFACE} ${OLD_MACADDR}
+ iw dev ${VWIFI_IFACE} del
+ dealloc_iface $VWIFI_IFACE
+ fi
+ else
+ ip link set down dev ${WIFI_IFACE}
+ ip addr flush ${WIFI_IFACE}
+ if [[ -n "$NEW_MACADDR" ]]; then
+ ip link set dev ${WIFI_IFACE} address ${OLD_MACADDR}
+ fi
+ networkmanager_rm_unmanaged_if_needed ${WIFI_IFACE} ${OLD_MACADDR}
+ fi
+
+ mutex_unlock
+ cleanup_lock
+
+ if [[ $RUNNING_AS_DAEMON -eq 1 && -n "$DAEMON_PIDFILE" && -f "$DAEMON_PIDFILE" ]]; then
+ rm $DAEMON_PIDFILE
+ fi
+}
+
+cleanup() {
+ echo
+ echo -n "Doing cleanup.. "
+ _cleanup > /dev/null 2>&1
+ echo "done"
+}
+
+die() {
+ [[ -n "$1" ]] && echo -e "\nERROR: $1\n" >&2
+ # send die signal to the main process
+ [[ $BASHPID -ne $$ ]] && kill -USR2 $$
+ # we don't need to call cleanup because it's traped on EXIT
+ exit 1
+}
+
+clean_exit() {
+ # send clean_exit signal to the main process
+ [[ $BASHPID -ne $$ ]] && kill -USR1 $$
+ # we don't need to call cleanup because it's traped on EXIT
+ exit 0
+}
+
+list_running_conf() {
+ local x
+ mutex_lock
+ for x in /tmp/create_ap.*; do
+ if [[ -f $x/pid && -f $x/wifi_iface && -d /proc/$(cat $x/pid) ]]; then
+ echo $x
+ fi
+ done
+ mutex_unlock
+}
+
+list_running() {
+ local IFACE wifi_iface x
+ mutex_lock
+ for x in $(list_running_conf); do
+ IFACE=${x#*.}
+ IFACE=${IFACE%%.*}
+ wifi_iface=$(cat $x/wifi_iface)
+
+ if [[ $IFACE == $wifi_iface ]]; then
+ echo $(cat $x/pid) $IFACE
+ else
+ echo $(cat $x/pid) $IFACE '('$(cat $x/wifi_iface)')'
+ fi
+ done
+ mutex_unlock
+}
+
+get_wifi_iface_from_pid() {
+ list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E "^${1} " | cut -d' ' -f2
+}
+
+get_pid_from_wifi_iface() {
+ list_running | awk '{print $1 " " $NF}' | tr -d '\(\)' | grep -E " ${1}$" | cut -d' ' -f1
+}
+
+get_confdir_from_pid() {
+ local IFACE x
+ mutex_lock
+ for x in $(list_running_conf); do
+ if [[ $(cat $x/pid) == "$1" ]]; then
+ echo $x
+ break
+ fi
+ done
+ mutex_unlock
+}
+
+print_client() {
+ local line ipaddr hostname
+ local mac="$1"
+
+ if [[ -f $CONFDIR/dnsmasq.leases ]]; then
+ line=$(grep " $mac " $CONFDIR/dnsmasq.leases | tail -n 1)
+ ipaddr=$(echo $line | cut -d' ' -f3)
+ hostname=$(echo "$line" | cut -d' ' -f4)
+ fi
+
+ [[ -z "$ipaddr" ]] && ipaddr="*"
+ [[ -z "$hostname" ]] && hostname="*"
+
+ printf "%-20s %-18s %s\n" "$mac" "$ipaddr" "$hostname"
+}
+
+list_clients() {
+ local wifi_iface pid
+
+ # If PID is given, get the associated wifi iface
+ if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then
+ pid="$1"
+ wifi_iface=$(get_wifi_iface_from_pid "$pid")
+ [[ -z "$wifi_iface" ]] && die "'$pid' is not the pid of a running $PROGNAME instance."
+ fi
+
+ [[ -z "$wifi_iface" ]] && wifi_iface="$1"
+ is_wifi_interface "$wifi_iface" || die "'$wifi_iface' is not a WiFi interface."
+
+ [[ -z "$pid" ]] && pid=$(get_pid_from_wifi_iface "$wifi_iface")
+ [[ -z "$pid" ]] && die "'$wifi_iface' is not used from $PROGNAME instance.\n\
+ Maybe you need to pass the virtual interface instead.\n\
+ Use --list-running to find it out."
+ [[ -z "$CONFDIR" ]] && CONFDIR=$(get_confdir_from_pid "$pid")
+
+ if [[ $USE_IWCONFIG -eq 0 ]]; then
+ local awk_cmd='($1 ~ /Station$/) {print $2}'
+ local client_list=$(iw dev "$wifi_iface" station dump | awk "$awk_cmd")
+
+ if [[ -z "$client_list" ]]; then
+ echo "No clients connected"
+ return
+ fi
+
+ printf "%-20s %-18s %s\n" "MAC" "IP" "Hostname"
+
+ local mac
+ for mac in $client_list; do
+ print_client $mac
+ done
+ else
+ die "This option is not supported for the current driver."
+ fi
+}
+
+has_running_instance() {
+ local PID x
+
+ mutex_lock
+ for x in /tmp/create_ap.*; do
+ if [[ -f $x/pid ]]; then
+ PID=$(cat $x/pid)
+ if [[ -d /proc/$PID ]]; then
+ mutex_unlock
+ return 0
+ fi
+ fi
+ done
+ mutex_lock
+
+ return 1
+}
+
+is_running_pid() {
+ list_running | grep -E "^${1} " > /dev/null 2>&1
+}
+
+send_stop() {
+ local x
+
+ mutex_lock
+ # send stop signal to specific pid
+ if is_running_pid $1; then
+ kill -USR1 $1
+ mutex_unlock
+ return
+ fi
+
+ # send stop signal to specific interface
+ for x in $(list_running | grep -E " \(?${1}( |\)?\$)" | cut -f1 -d' '); do
+ kill -USR1 $x
+ done
+ mutex_unlock
+}
+
+# Storing configs
+write_config() {
+ local i=1
+
+ if ! eval 'echo -n > "$STORE_CONFIG"' > /dev/null 2>&1; then
+ echo "ERROR: Unable to create config file $STORE_CONFIG" >&2
+ exit 1
+ fi
+
+ WIFI_IFACE=$1
+ if [[ "$SHARE_METHOD" == "none" ]]; then
+ SSID="$2"
+ PASSPHRASE="$3"
+ else
+ INTERNET_IFACE="$2"
+ SSID="$3"
+ PASSPHRASE="$4"
+ fi
+
+ for config_opt in "${CONFIG_OPTS[@]}"; do
+ eval echo $config_opt=\$$config_opt
+ done >> "$STORE_CONFIG"
+
+ echo -e "Config options written to '$STORE_CONFIG'"
+ exit 0
+}
+
+is_config_opt() {
+ local elem opt="$1"
+
+ for elem in "${CONFIG_OPTS[@]}"; do
+ if [[ "$elem" == "$opt" ]]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+# Load options from config file
+read_config() {
+ local opt_name opt_val line
+
+ while read line; do
+ # Read switches and their values
+ opt_name="${line%%=*}"
+ opt_val="${line#*=}"
+ if is_config_opt "$opt_name" ; then
+ eval $opt_name="\$opt_val"
+ else
+ echo "WARN: Unrecognized configuration entry $opt_name" >&2
+ fi
+ done < "$LOAD_CONFIG"
+}
+
+
+ARGS=( "$@" )
+
+# Preprocessing for --config before option-parsing starts
+for ((i=0; i<$#; i++)); do
+ if [[ "${ARGS[i]}" = "--config" ]]; then
+ if [[ -f "${ARGS[i+1]}" ]]; then
+ LOAD_CONFIG="${ARGS[i+1]}"
+ read_config
+ else
+ echo "ERROR: No config file found at given location" >&2
+ exit 1
+ fi
+ break
+ fi
+done
+
+GETOPT_ARGS=$(getopt -o hc:w:g:de:nm: -l "help","hidden","hostapd-debug:","redirect-to-localhost","mac-filter","mac-filter-accept:","isolate-clients","ieee80211n","ieee80211ac","ht_capab:","vht_capab:","driver:","no-virt","fix-unmanaged","country:","freq-band:","mac:","dhcp-dns:","daemon","pidfile:","logfile:","stop:","list","list-running","list-clients:","version","psk","no-haveged","no-dns","no-dnsmasq","mkconfig:","config:" -n "$PROGNAME" -- "$@")
+[[ $? -ne 0 ]] && exit 1
+eval set -- "$GETOPT_ARGS"
+
+while :; do
+ case "$1" in
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ --version)
+ echo $VERSION
+ exit 0
+ ;;
+ --hidden)
+ shift
+ HIDDEN=1
+ ;;
+ --mac-filter)
+ shift
+ MAC_FILTER=1
+ ;;
+ --mac-filter-accept)
+ shift
+ MAC_FILTER_ACCEPT="$1"
+ shift
+ ;;
+ --isolate-clients)
+ shift
+ ISOLATE_CLIENTS=1
+ ;;
+ -c)
+ shift
+ CHANNEL="$1"
+ shift
+ ;;
+ -w)
+ shift
+ WPA_VERSION="$1"
+ [[ "$WPA_VERSION" == "2+1" ]] && WPA_VERSION=1+2
+ shift
+ ;;
+ -g)
+ shift
+ GATEWAY="$1"
+ shift
+ ;;
+ -d)
+ shift
+ ETC_HOSTS=1
+ ;;
+ -e)
+ shift
+ ADDN_HOSTS="$1"
+ shift
+ ;;
+ -n)
+ shift
+ SHARE_METHOD=none
+ ;;
+ -m)
+ shift
+ SHARE_METHOD="$1"
+ shift
+ ;;
+ --ieee80211n)
+ shift
+ IEEE80211N=1
+ ;;
+ --ieee80211ac)
+ shift
+ IEEE80211AC=1
+ ;;
+ --ht_capab)
+ shift
+ HT_CAPAB="$1"
+ shift
+ ;;
+ --vht_capab)
+ shift
+ VHT_CAPAB="$1"
+ shift
+ ;;
+ --driver)
+ shift
+ DRIVER="$1"
+ shift
+ ;;
+ --no-virt)
+ shift
+ NO_VIRT=1
+ ;;
+ --fix-unmanaged)
+ shift
+ FIX_UNMANAGED=1
+ ;;
+ --country)
+ shift
+ COUNTRY="$1"
+ shift
+ ;;
+ --freq-band)
+ shift
+ FREQ_BAND="$1"
+ shift
+ ;;
+ --mac)
+ shift
+ NEW_MACADDR="$1"
+ shift
+ ;;
+ --dhcp-dns)
+ shift
+ DHCP_DNS="$1"
+ shift
+ ;;
+ --daemon)
+ shift
+ DAEMONIZE=1
+ ;;
+ --pidfile)
+ shift
+ DAEMON_PIDFILE="$1"
+ shift
+ ;;
+ --logfile)
+ shift
+ DAEMON_LOGFILE="$1"
+ shift
+ ;;
+ --stop)
+ shift
+ STOP_ID="$1"
+ shift
+ ;;
+ --list)
+ shift
+ LIST_RUNNING=1
+ echo -e "WARN: --list is deprecated, use --list-running instead.\n" >&2
+ ;;
+ --list-running)
+ shift
+ LIST_RUNNING=1
+ ;;
+ --list-clients)
+ shift
+ LIST_CLIENTS_ID="$1"
+ shift
+ ;;
+ --no-haveged)
+ shift
+ NO_HAVEGED=1
+ ;;
+ --psk)
+ shift
+ USE_PSK=1
+ ;;
+ --no-dns)
+ shift
+ NO_DNS=1
+ ;;
+ --no-dnsmasq)
+ shift
+ NO_DNSMASQ=1
+ ;;
+ --redirect-to-localhost)
+ shift
+ REDIRECT_TO_LOCALHOST=1
+ ;;
+ --hostapd-debug)
+ shift
+ if [ "x$1" = "x1" ]; then
+ HOSTAPD_DEBUG_ARGS="-d"
+ elif [ "x$1" = "x2" ]; then
+ HOSTAPD_DEBUG_ARGS="-dd"
+ else
+ printf "Error: argument for --hostapd-debug expected 1 or 2, got %s\n" "$1"
+ exit 1
+ fi
+ shift
+ ;;
+ --mkconfig)
+ shift
+ STORE_CONFIG="$1"
+ shift
+ ;;
+ --config)
+ shift
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ esac
+done
+
+# Load positional args from config file, if needed
+if [[ -n "$LOAD_CONFIG" && $# -eq 0 ]]; then
+ i=0
+ # set arguments in order
+ for x in WIFI_IFACE INTERNET_IFACE SSID PASSPHRASE; do
+ if eval "[[ -n \"\$${x}\" ]]"; then
+ eval "set -- \"\${@:1:$i}\" \"\$${x}\""
+ ((i++))
+ fi
+ # we unset the variable to avoid any problems later
+ eval "unset $x"
+ done
+fi
+
+# Check if required number of positional args are present
+if [[ $# -lt 1 && $FIX_UNMANAGED -eq 0 && -z "$STOP_ID" &&
+ $LIST_RUNNING -eq 0 && -z "$LIST_CLIENTS_ID" ]]; then
+ usage >&2
+ exit 1
+fi
+
+# Set NO_DNS, if dnsmasq is disabled
+if [[ $NO_DNSMASQ -eq 1 ]]; then
+ NO_DNS=1
+fi
+
+trap "cleanup_lock" EXIT
+
+if ! init_lock; then
+ echo "ERROR: Failed to initialize lock" >&2
+ exit 1
+fi
+
+# if the user press ctrl+c or we get USR1 signal
+# then run clean_exit()
+trap "clean_exit" SIGINT SIGUSR1
+# if we get USR2 signal then run die().
+trap "die" SIGUSR2
+
+[[ -n "$STORE_CONFIG" ]] && write_config "$@"
+
+if [[ $LIST_RUNNING -eq 1 ]]; then
+ echo -e "List of running $PROGNAME instances:\n"
+ list_running
+ exit 0
+fi
+
+if [[ -n "$LIST_CLIENTS_ID" ]]; then
+ list_clients "$LIST_CLIENTS_ID"
+ exit 0
+fi
+
+if [[ $(id -u) -ne 0 ]]; then
+ echo "You must run it as root." >&2
+ exit 1
+fi
+
+if [[ -n "$STOP_ID" ]]; then
+ echo "Trying to kill $PROGNAME instance associated with $STOP_ID..."
+ send_stop "$STOP_ID"
+ exit 0
+fi
+
+if [[ $FIX_UNMANAGED -eq 1 ]]; then
+ echo "Trying to fix unmanaged status in NetworkManager..."
+ networkmanager_fix_unmanaged
+ exit 0
+fi
+
+if [[ $DAEMONIZE -eq 1 && $RUNNING_AS_DAEMON -eq 0 ]]; then
+ # Assume we're running underneath a service manager if PIDFILE is set
+ # and don't clobber it's output with a useless message
+ if [ -z "$DAEMON_PIDFILE" ]; then
+ echo "Running as Daemon..."
+ fi
+ # run a detached create_ap
+ RUNNING_AS_DAEMON=1 setsid "$0" "${ARGS[@]}" >>$DAEMON_LOGFILE 2>&1 &
+ exit 0
+elif [[ $RUNNING_AS_DAEMON -eq 1 && -n "$DAEMON_PIDFILE" ]]; then
+ echo $$ >$DAEMON_PIDFILE
+fi
+
+if [[ $FREQ_BAND != 2.4 && $FREQ_BAND != 5 ]]; then
+ echo "ERROR: Invalid frequency band" >&2
+ exit 1
+fi
+
+if [[ $CHANNEL == default ]]; then
+ if [[ $FREQ_BAND == 2.4 ]]; then
+ CHANNEL=1
+ else
+ CHANNEL=36
+ fi
+fi
+
+if [[ $FREQ_BAND != 5 && $CHANNEL -gt 14 ]]; then
+ echo "Channel number is greater than 14, assuming 5GHz frequency band"
+ FREQ_BAND=5
+fi
+
+WIFI_IFACE=$1
+
+if ! is_wifi_interface ${WIFI_IFACE}; then
+ echo "ERROR: '${WIFI_IFACE}' is not a WiFi interface" >&2
+ exit 1
+fi
+
+if ! can_be_ap ${WIFI_IFACE}; then
+ echo "ERROR: Your adapter does not support AP (master) mode" >&2
+ exit 1
+fi
+
+if ! can_be_sta_and_ap ${WIFI_IFACE}; then
+ if is_wifi_connected ${WIFI_IFACE}; then
+ echo "ERROR: Your adapter can not be a station (i.e. be connected) and an AP at the same time" >&2
+ exit 1
+ elif [[ $NO_VIRT -eq 0 ]]; then
+ echo "WARN: Your adapter does not fully support AP virtual interface, enabling --no-virt" >&2
+ NO_VIRT=1
+ fi
+fi
+
+HOSTAPD=$(which hostapd)
+
+if [[ ! -x "$HOSTAPD" ]]; then
+ echo "ERROR: hostapd not found." >&2
+ exit 1
+fi
+
+if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^(8192[cd][ue]|8723a[sue])$ ]]; then
+ if ! strings "$HOSTAPD" | grep -m1 rtl871xdrv > /dev/null 2>&1; then
+ echo "ERROR: You need to patch your hostapd with rtl871xdrv patches." >&2
+ exit 1
+ fi
+
+ if [[ $DRIVER != "rtl871xdrv" ]]; then
+ echo "WARN: Your adapter needs rtl871xdrv, enabling --driver=rtl871xdrv" >&2
+ DRIVER=rtl871xdrv
+ fi
+fi
+
+if [[ "$SHARE_METHOD" != "nat" && "$SHARE_METHOD" != "bridge" && "$SHARE_METHOD" != "none" ]]; then
+ echo "ERROR: Wrong Internet sharing method" >&2
+ echo
+ usage >&2
+ exit 1
+fi
+
+if [[ -n "$NEW_MACADDR" ]]; then
+ if ! is_macaddr "$NEW_MACADDR"; then
+ echo "ERROR: '${NEW_MACADDR}' is not a valid MAC address" >&2
+ exit 1
+ fi
+
+ if ! is_unicast_macaddr "$NEW_MACADDR"; then
+ echo "ERROR: The first byte of MAC address (${NEW_MACADDR}) must be even" >&2
+ exit 1
+ fi
+
+ if [[ $(get_all_macaddrs | grep -c ${NEW_MACADDR}) -ne 0 ]]; then
+ echo "WARN: MAC address '${NEW_MACADDR}' already exists. Because of this, you may encounter some problems" >&2
+ fi
+fi
+
+if [[ "$SHARE_METHOD" != "none" ]]; then
+ MIN_REQUIRED_ARGS=2
+else
+ MIN_REQUIRED_ARGS=1
+fi
+
+if [[ $# -gt $MIN_REQUIRED_ARGS ]]; then
+ if [[ "$SHARE_METHOD" != "none" ]]; then
+ if [[ $# -ne 3 && $# -ne 4 ]]; then
+ usage >&2
+ exit 1
+ fi
+ INTERNET_IFACE="$2"
+ SSID="$3"
+ PASSPHRASE="$4"
+ else
+ if [[ $# -ne 2 && $# -ne 3 ]]; then
+ usage >&2
+ exit 1
+ fi
+ SSID="$2"
+ PASSPHRASE="$3"
+ fi
+else
+ if [[ "$SHARE_METHOD" != "none" ]]; then
+ if [[ $# -ne 2 ]]; then
+ usage >&2
+ exit 1
+ fi
+ INTERNET_IFACE="$2"
+ fi
+ if tty -s; then
+ while :; do
+ read -p "SSID: " SSID
+ if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then
+ echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2
+ continue
+ fi
+ break
+ done
+ while :; do
+ if [[ $USE_PSK -eq 0 ]]; then
+ read -p "Passphrase: " -s PASSPHRASE
+ echo
+ if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then
+ echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2
+ continue
+ fi
+ read -p "Retype passphrase: " -s PASSPHRASE2
+ echo
+ if [[ "$PASSPHRASE" != "$PASSPHRASE2" ]]; then
+ echo "Passphrases do not match."
+ else
+ break
+ fi
+ else
+ read -p "PSK: " PASSPHRASE
+ echo
+ if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then
+ echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2
+ continue
+ fi
+ fi
+ done
+ else
+ read SSID
+ read PASSPHRASE
+ fi
+fi
+
+if [[ "$SHARE_METHOD" != "none" ]] && ! is_interface $INTERNET_IFACE; then
+ echo "ERROR: '${INTERNET_IFACE}' is not an interface" >&2
+ exit 1
+fi
+
+if [[ ${#SSID} -lt 1 || ${#SSID} -gt 32 ]]; then
+ echo "ERROR: Invalid SSID length ${#SSID} (expected 1..32)" >&2
+ exit 1
+fi
+
+if [[ $USE_PSK -eq 0 ]]; then
+ if [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -lt 8 ]] || [[ ${#PASSPHRASE} -gt 63 ]]; then
+ echo "ERROR: Invalid passphrase length ${#PASSPHRASE} (expected 8..63)" >&2
+ exit 1
+ fi
+elif [[ ${#PASSPHRASE} -gt 0 && ${#PASSPHRASE} -ne 64 ]]; then
+ echo "ERROR: Invalid pre-shared-key length ${#PASSPHRASE} (expected 64)" >&2
+ exit 1
+fi
+
+if [[ $(get_adapter_kernel_module ${WIFI_IFACE}) =~ ^rtl[0-9].*$ ]]; then
+ if [[ -n "$PASSPHRASE" ]]; then
+ echo "WARN: Realtek drivers usually have problems with WPA1, enabling -w 2" >&2
+ WPA_VERSION=2
+ fi
+ echo "WARN: If AP doesn't work, please read: howto/realtek.md" >&2
+fi
+
+if [[ $NO_VIRT -eq 1 && "$WIFI_IFACE" == "$INTERNET_IFACE" ]]; then
+ echo -n "ERROR: You can not share your connection from the same" >&2
+ echo " interface if you are using --no-virt option." >&2
+ exit 1
+fi
+
+mutex_lock
+trap "cleanup" EXIT
+CONFDIR=$(mktemp -d /tmp/create_ap.${WIFI_IFACE}.conf.XXXXXXXX)
+echo "Config dir: $CONFDIR"
+echo "PID: $$"
+echo $$ > $CONFDIR/pid
+
+# to make --list-running work from any user, we must give read
+# permissions to $CONFDIR and $CONFDIR/pid
+chmod 755 $CONFDIR
+chmod 444 $CONFDIR/pid
+
+COMMON_CONFDIR=/tmp/create_ap.common.conf
+mkdir -p $COMMON_CONFDIR
+
+if [[ "$SHARE_METHOD" == "nat" ]]; then
+ echo $INTERNET_IFACE > $CONFDIR/nat_internet_iface
+ cp_n /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding \
+ $COMMON_CONFDIR/${INTERNET_IFACE}_forwarding
+fi
+cp_n /proc/sys/net/ipv4/ip_forward $COMMON_CONFDIR
+if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then
+ cp_n /proc/sys/net/bridge/bridge-nf-call-iptables $COMMON_CONFDIR
+fi
+mutex_unlock
+
+if [[ "$SHARE_METHOD" == "bridge" ]]; then
+ if is_bridge_interface $INTERNET_IFACE; then
+ BRIDGE_IFACE=$INTERNET_IFACE
+ else
+ BRIDGE_IFACE=$(alloc_new_iface br)
+ fi
+fi
+
+if [[ $USE_IWCONFIG -eq 0 ]]; then
+ iw dev ${WIFI_IFACE} set power_save off
+fi
+
+if [[ $NO_VIRT -eq 0 ]]; then
+ VWIFI_IFACE=$(alloc_new_iface ap)
+
+ # in NetworkManager 0.9.9 and above we can set the interface as unmanaged without
+ # the need of MAC address, so we set it before we create the virtual interface.
+ if networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]]; then
+ echo -n "Network Manager found, set ${VWIFI_IFACE} as unmanaged device... "
+ networkmanager_add_unmanaged ${VWIFI_IFACE}
+ # do not call networkmanager_wait_until_unmanaged because interface does not
+ # exist yet
+ echo "DONE"
+ fi
+
+ if is_wifi_connected ${WIFI_IFACE}; then
+ WIFI_IFACE_FREQ=$(iw dev ${WIFI_IFACE} link | grep -i freq | awk '{print $2}')
+ WIFI_IFACE_CHANNEL=$(ieee80211_frequency_to_channel ${WIFI_IFACE_FREQ})
+ echo -n "${WIFI_IFACE} is already associated with channel ${WIFI_IFACE_CHANNEL} (${WIFI_IFACE_FREQ} MHz)"
+ if is_5ghz_frequency $WIFI_IFACE_FREQ; then
+ FREQ_BAND=5
+ else
+ FREQ_BAND=2.4
+ fi
+ if [[ $WIFI_IFACE_CHANNEL -ne $CHANNEL ]]; then
+ echo ", fallback to channel ${WIFI_IFACE_CHANNEL}"
+ CHANNEL=$WIFI_IFACE_CHANNEL
+ else
+ echo
+ fi
+ fi
+
+ VIRTDIEMSG="Maybe your WiFi adapter does not fully support virtual interfaces.
+ Try again with --no-virt."
+ echo -n "Creating a virtual WiFi interface... "
+
+ if iw dev ${WIFI_IFACE} interface add ${VWIFI_IFACE} type __ap; then
+ # now we can call networkmanager_wait_until_unmanaged
+ networkmanager_is_running && [[ $NM_OLDER_VERSION -eq 0 ]] && networkmanager_wait_until_unmanaged ${VWIFI_IFACE}
+ echo "${VWIFI_IFACE} created."
+ else
+ VWIFI_IFACE=
+ die "$VIRTDIEMSG"
+ fi
+ OLD_MACADDR=$(get_macaddr ${VWIFI_IFACE})
+ if [[ -z "$NEW_MACADDR" && $(get_all_macaddrs | grep -c ${OLD_MACADDR}) -ne 1 ]]; then
+ NEW_MACADDR=$(get_new_macaddr ${VWIFI_IFACE})
+ fi
+ WIFI_IFACE=${VWIFI_IFACE}
+else
+ OLD_MACADDR=$(get_macaddr ${WIFI_IFACE})
+fi
+
+mutex_lock
+echo $WIFI_IFACE > $CONFDIR/wifi_iface
+chmod 444 $CONFDIR/wifi_iface
+mutex_unlock
+
+if [[ -n "$COUNTRY" && $USE_IWCONFIG -eq 0 ]]; then
+ iw reg set "$COUNTRY"
+fi
+
+can_transmit_to_channel ${WIFI_IFACE} ${CHANNEL} || die "Your adapter can not transmit to channel ${CHANNEL}, frequency band ${FREQ_BAND}GHz."
+
+if networkmanager_exists && ! networkmanager_iface_is_unmanaged ${WIFI_IFACE}; then
+ echo -n "Network Manager found, set ${WIFI_IFACE} as unmanaged device... "
+ networkmanager_add_unmanaged ${WIFI_IFACE}
+
+ if networkmanager_is_running; then
+ networkmanager_wait_until_unmanaged ${WIFI_IFACE}
+ fi
+
+ echo "DONE"
+fi
+
+[[ $HIDDEN -eq 1 ]] && echo "Access Point's SSID is hidden!"
+
+[[ $MAC_FILTER -eq 1 ]] && echo "MAC address filtering is enabled!"
+
+[[ $ISOLATE_CLIENTS -eq 1 ]] && echo "Access Point's clients will be isolated!"
+
+# hostapd config
+cat << EOF > $CONFDIR/hostapd.conf
+beacon_int=100
+ssid=${SSID}
+interface=${WIFI_IFACE}
+driver=${DRIVER}
+channel=${CHANNEL}
+ctrl_interface=$CONFDIR/hostapd_ctrl
+ctrl_interface_group=0
+ignore_broadcast_ssid=$HIDDEN
+ap_isolate=$ISOLATE_CLIENTS
+EOF
+
+if [[ -n "$COUNTRY" ]]; then
+ cat << EOF >> $CONFDIR/hostapd.conf
+country_code=${COUNTRY}
+ieee80211d=1
+EOF
+fi
+
+if [[ $FREQ_BAND == 2.4 ]]; then
+ echo "hw_mode=g" >> $CONFDIR/hostapd.conf
+else
+ echo "hw_mode=a" >> $CONFDIR/hostapd.conf
+fi
+
+if [[ $MAC_FILTER -eq 1 ]]; then
+ cat << EOF >> $CONFDIR/hostapd.conf
+macaddr_acl=${MAC_FILTER}
+accept_mac_file=${MAC_FILTER_ACCEPT}
+EOF
+fi
+
+if [[ $IEEE80211N -eq 1 ]]; then
+ cat << EOF >> $CONFDIR/hostapd.conf
+ieee80211n=1
+ht_capab=${HT_CAPAB}
+EOF
+fi
+
+if [[ $IEEE80211AC -eq 1 ]]; then
+ echo "ieee80211ac=1" >> $CONFDIR/hostapd.conf
+fi
+
+if [[ -n "$VHT_CAPAB" ]]; then
+ echo "vht_capab=${VHT_CAPAB}" >> $CONFDIR/hostapd.conf
+fi
+
+if [[ $IEEE80211N -eq 1 ]] || [[ $IEEE80211AC -eq 1 ]]; then
+ echo "wmm_enabled=1" >> $CONFDIR/hostapd.conf
+fi
+
+if [[ -n "$PASSPHRASE" ]]; then
+ [[ "$WPA_VERSION" == "1+2" ]] && WPA_VERSION=3
+ if [[ $USE_PSK -eq 0 ]]; then
+ WPA_KEY_TYPE=passphrase
+ else
+ WPA_KEY_TYPE=psk
+ fi
+ cat << EOF >> $CONFDIR/hostapd.conf
+wpa=${WPA_VERSION}
+wpa_${WPA_KEY_TYPE}=${PASSPHRASE}
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=TKIP CCMP
+rsn_pairwise=CCMP
+EOF
+fi
+
+if [[ "$SHARE_METHOD" == "bridge" ]]; then
+ echo "bridge=${BRIDGE_IFACE}" >> $CONFDIR/hostapd.conf
+elif [[ $NO_DNSMASQ -eq 0 ]]; then
+ # dnsmasq config (dhcp + dns)
+ DNSMASQ_VER=$(dnsmasq -v | grep -m1 -oE '[0-9]+(\.[0-9]+)*\.[0-9]+')
+ version_cmp $DNSMASQ_VER 2.63
+ if [[ $? -eq 1 ]]; then
+ DNSMASQ_BIND=bind-interfaces
+ else
+ DNSMASQ_BIND=bind-dynamic
+ fi
+ if [[ "$DHCP_DNS" == "gateway" ]]; then
+ DHCP_DNS="$GATEWAY"
+ fi
+ cat << EOF > $CONFDIR/dnsmasq.conf
+listen-address=${GATEWAY}
+${DNSMASQ_BIND}
+dhcp-range=${GATEWAY%.*}.1,${GATEWAY%.*}.254,255.255.255.0,24h
+dhcp-option-force=option:router,${GATEWAY}
+dhcp-option-force=option:dns-server,${DHCP_DNS}
+EOF
+ MTU=$(get_mtu $INTERNET_IFACE)
+ [[ -n "$MTU" ]] && echo "dhcp-option-force=option:mtu,${MTU}" >> $CONFDIR/dnsmasq.conf
+ [[ $ETC_HOSTS -eq 0 ]] && echo no-hosts >> $CONFDIR/dnsmasq.conf
+ [[ -n "$ADDN_HOSTS" ]] && echo "addn-hosts=${ADDN_HOSTS}" >> $CONFDIR/dnsmasq.conf
+ if [[ "$SHARE_METHOD" == "none" && "$REDIRECT_TO_LOCALHOST" == "1" ]]; then
+ cat << EOF >> $CONFDIR/dnsmasq.conf
+address=/#/$GATEWAY
+EOF
+ fi
+fi
+
+# initialize WiFi interface
+if [[ $NO_VIRT -eq 0 && -n "$NEW_MACADDR" ]]; then
+ ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die "$VIRTDIEMSG"
+fi
+
+ip link set down dev ${WIFI_IFACE} || die "$VIRTDIEMSG"
+ip addr flush ${WIFI_IFACE} || die "$VIRTDIEMSG"
+
+if [[ $NO_VIRT -eq 1 && -n "$NEW_MACADDR" ]]; then
+ ip link set dev ${WIFI_IFACE} address ${NEW_MACADDR} || die
+fi
+
+if [[ "$SHARE_METHOD" != "bridge" ]]; then
+ ip link set up dev ${WIFI_IFACE} || die "$VIRTDIEMSG"
+ ip addr add ${GATEWAY}/24 broadcast ${GATEWAY%.*}.255 dev ${WIFI_IFACE} || die "$VIRTDIEMSG"
+fi
+
+# enable Internet sharing
+if [[ "$SHARE_METHOD" != "none" ]]; then
+ echo "Sharing Internet using method: $SHARE_METHOD"
+ if [[ "$SHARE_METHOD" == "nat" ]]; then
+ iptables -w -t nat -I POSTROUTING -s ${GATEWAY%.*}.0/24 ! -o ${WIFI_IFACE} -j MASQUERADE || die
+ iptables -w -I FORWARD -i ${WIFI_IFACE} -s ${GATEWAY%.*}.0/24 -j ACCEPT || die
+ iptables -w -I FORWARD -i ${INTERNET_IFACE} -d ${GATEWAY%.*}.0/24 -j ACCEPT || die
+ echo 1 > /proc/sys/net/ipv4/conf/$INTERNET_IFACE/forwarding || die
+ echo 1 > /proc/sys/net/ipv4/ip_forward || die
+ # to enable clients to establish PPTP connections we must
+ # load nf_nat_pptp module
+ modprobe nf_nat_pptp > /dev/null 2>&1
+ elif [[ "$SHARE_METHOD" == "bridge" ]]; then
+ # disable iptables rules for bridged interfaces
+ if [[ -e /proc/sys/net/bridge/bridge-nf-call-iptables ]]; then
+ echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables
+ fi
+
+ # to initialize the bridge interface correctly we need to do the following:
+ #
+ # 1) save the IPs and route table of INTERNET_IFACE
+ # 2) if NetworkManager is running set INTERNET_IFACE as unmanaged
+ # 3) create BRIDGE_IFACE and attach INTERNET_IFACE to it
+ # 4) set the previously saved IPs and route table to BRIDGE_IFACE
+ #
+ # we need the above because BRIDGE_IFACE is the master interface from now on
+ # and it must know where is connected, otherwise connection is lost.
+ if ! is_bridge_interface $INTERNET_IFACE; then
+ echo -n "Create a bridge interface... "
+ OLD_IFS="$IFS"
+ IFS=$'\n'
+
+ IP_ADDRS=( $(ip addr show $INTERNET_IFACE | grep -A 1 -E 'inet[[:blank:]]' | paste - -) )
+ ROUTE_ADDRS=( $(ip route show dev $INTERNET_IFACE) )
+
+ IFS="$OLD_IFS"
+
+ if networkmanager_is_running; then
+ networkmanager_add_unmanaged $INTERNET_IFACE
+ networkmanager_wait_until_unmanaged $INTERNET_IFACE
+ fi
+
+ # create bridge interface
+ ip link add name $BRIDGE_IFACE type bridge || die
+ ip link set dev $BRIDGE_IFACE up || die
+ # set 0ms forward delay
+ echo -n 0 > /sys/class/net/$BRIDGE_IFACE/bridge/forward_delay
+
+ # attach internet interface to bridge interface
+ ip link set dev $INTERNET_IFACE promisc on || die
+ ip link set dev $INTERNET_IFACE up || die
+ ip link set dev $INTERNET_IFACE master $BRIDGE_IFACE || die
+
+ ip addr flush $INTERNET_IFACE
+ for x in "${IP_ADDRS[@]}"; do
+ x="${x/inet/}"
+ x="${x/secondary/}"
+ x="${x/dynamic/}"
+ x=$(echo $x | sed 's/\([0-9]\)sec/\1/g')
+ x="${x/${INTERNET_IFACE}/}"
+ ip addr add $x dev $BRIDGE_IFACE || die
+ done
+
+ # remove any existing entries that were added from 'ip addr add'
+ ip route flush dev $INTERNET_IFACE
+ ip route flush dev $BRIDGE_IFACE
+
+ # we must first add the entries that specify the subnets and then the
+ # gateway entry, otherwise 'ip addr add' will return an error
+ for x in "${ROUTE_ADDRS[@]}"; do
+ [[ "$x" == default* ]] && continue
+ ip route add $x dev $BRIDGE_IFACE || die
+ done
+
+ for x in "${ROUTE_ADDRS[@]}"; do
+ [[ "$x" != default* ]] && continue
+ ip route add $x dev $BRIDGE_IFACE || die
+ done
+
+ echo "$BRIDGE_IFACE created."
+ fi
+ fi
+else
+ echo "No Internet sharing"
+fi
+
+# start dhcp + dns (optional)
+if [[ "$SHARE_METHOD" != "bridge" ]]; then
+ if [[ $NO_DNS -eq 0 ]]; then
+ DNS_PORT=5353
+ iptables -w -I INPUT -p tcp -m tcp --dport $DNS_PORT -j ACCEPT || die
+ iptables -w -I INPUT -p udp -m udp --dport $DNS_PORT -j ACCEPT || die
+ iptables -w -t nat -I PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \
+ -p tcp -m tcp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die
+ iptables -w -t nat -I PREROUTING -s ${GATEWAY%.*}.0/24 -d ${GATEWAY} \
+ -p udp -m udp --dport 53 -j REDIRECT --to-ports $DNS_PORT || die
+ else
+ DNS_PORT=0
+ fi
+
+ if [[ $NO_DNSMASQ -eq 0 ]]; then
+ iptables -w -I INPUT -p udp -m udp --dport 67 -j ACCEPT || die
+
+ if which complain > /dev/null 2>&1; then
+ # openSUSE's apparmor does not allow dnsmasq to read files.
+ # remove restriction.
+ complain dnsmasq
+ fi
+
+ umask 0033
+ dnsmasq -C $CONFDIR/dnsmasq.conf -x $CONFDIR/dnsmasq.pid -l $CONFDIR/dnsmasq.leases -p $DNS_PORT || die
+ umask $SCRIPT_UMASK
+ fi
+fi
+
+# start access point
+echo "hostapd command-line interface: hostapd_cli -p $CONFDIR/hostapd_ctrl"
+
+if [[ $NO_HAVEGED -eq 0 ]]; then
+ haveged_watchdog &
+ HAVEGED_WATCHDOG_PID=$!
+fi
+
+# start hostapd (use stdbuf when available for no delayed output in programs that redirect stdout)
+STDBUF_PATH=`which stdbuf`
+if [ $? -eq 0 ]; then
+ STDBUF_PATH=$STDBUF_PATH" -oL"
+fi
+$STDBUF_PATH $HOSTAPD $HOSTAPD_DEBUG_ARGS $CONFDIR/hostapd.conf &
+HOSTAPD_PID=$!
+echo $HOSTAPD_PID > $CONFDIR/hostapd.pid
+
+if ! wait $HOSTAPD_PID; then
+ echo -e "\nError: Failed to run hostapd, maybe a program is interfering." >&2
+ if networkmanager_is_running; then
+ echo "If an error like 'n80211: Could not configure driver mode' was thrown" >&2
+ echo "try running the following before starting create_ap:" >&2
+ if [[ $NM_OLDER_VERSION -eq 1 ]]; then
+ echo " nmcli nm wifi off" >&2
+ else
+ echo " nmcli r wifi off" >&2
+ fi
+ echo " rfkill unblock wlan" >&2
+ fi
+ die
+fi
+
+clean_exit
+
+# Local Variables:
+# tab-width: 4
+# indent-tabs-mode: nil
+# End:
+
+# vim: et sts=4 sw=4
diff --git a/external/packages/bsp/overlays_arm64/usr/bin/desktop_login.sh b/external/packages/bsp/overlays_arm64/usr/bin/desktop_login.sh
new file mode 100755
index 000000000000..92b298f07802
--- /dev/null
+++ b/external/packages/bsp/overlays_arm64/usr/bin/desktop_login.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+if [[ -z $1 ]]; then
+ user=root
+else
+ user=$1
+fi
+
+sudo sed -i '/autologin-user=/d' /etc/lightdm/lightdm.conf.d/22-orangepi-autologin.conf
+sudo echo autologin-user=${user} >> /etc/lightdm/lightdm.conf.d/22-orangepi-autologin.conf
+sudo sed -i 's/root/anything/' /etc/pam.d/lightdm-autologin
diff --git a/external/packages/bsp/overlays_arm64/usr/bin/disable_desktop_autologin.sh b/external/packages/bsp/overlays_arm64/usr/bin/disable_desktop_autologin.sh
new file mode 100755
index 000000000000..430e9b21b104
--- /dev/null
+++ b/external/packages/bsp/overlays_arm64/usr/bin/disable_desktop_autologin.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+sudo sed -i \
+"s/autologin-user=.*/#&/" \
+/etc/lightdm/lightdm.conf.d/22-orangepi-autologin.conf
diff --git a/external/packages/bsp/overlays_arm64/usr/bin/enable_docker.sh b/external/packages/bsp/overlays_arm64/usr/bin/enable_docker.sh
new file mode 100755
index 000000000000..460c9da60c61
--- /dev/null
+++ b/external/packages/bsp/overlays_arm64/usr/bin/enable_docker.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+sudo systemctl enable docker.service
+sudo systemctl start docker.service
diff --git a/external/packages/bsp/overlays_arm64/usr/bin/install_bt_panel.sh b/external/packages/bsp/overlays_arm64/usr/bin/install_bt_panel.sh
new file mode 100755
index 000000000000..0df0464c93ca
--- /dev/null
+++ b/external/packages/bsp/overlays_arm64/usr/bin/install_bt_panel.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+wget -O install.sh \
+http://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh
diff --git a/external/packages/bsp/overlays_arm64/usr/bin/install_docker.sh b/external/packages/bsp/overlays_arm64/usr/bin/install_docker.sh
new file mode 100755
index 000000000000..2ba89fbeead3
--- /dev/null
+++ b/external/packages/bsp/overlays_arm64/usr/bin/install_docker.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+distributor_id=$(lsb_release -is)
+distributor_id=${distributor_id,}
+
+sudo apt-get remove -y docker docker-engine docker-ce docker.io
+sudo apt-get update
+sudo apt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common
+
+curl -fsSL https://repo.huaweicloud.com/docker-ce/linux/${distributor_id}/gpg | sudo apt-key add -
+echo "deb [arch=$(dpkg --print-architecture)] https://repo.huaweicloud.com/docker-ce/linux/${distributor_id} $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list
+
+sudo apt-get update
+sudo apt-get install -y docker-ce docker-ce-cli containerd.io
+
+sudo groupadd docker
+sudo usermod -aG docker $USER
diff --git a/external/packages/bsp/overlays_arm64/usr/bin/install_qt.sh b/external/packages/bsp/overlays_arm64/usr/bin/install_qt.sh
new file mode 100755
index 000000000000..6ebd6d5da374
--- /dev/null
+++ b/external/packages/bsp/overlays_arm64/usr/bin/install_qt.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+release=$(lsb_release -cs)
+
+sudo apt update
+if [[ $release =~ focal|bionic|buster ]]; then
+ sudo apt-get -y install qt5-default qttools5-dev-tools qtbase5-doc-html qt5-assistant qt5-doc
+elif [[ $release =~ bullseye|bookworm|jammy ]]; then
+ sudo apt-get -y install qttools5-dev-tools qtbase5-doc-html qt5-assistant qt5-doc qt5-qmake qt5-qmake-bin
+else
+ echo "Unsupported system!"
+ exit
+fi
+
+sudo apt-get -y install qtcreator qmlscene gdb qtdeclarative5-dev qtbase5-examples cmake
+
+sudo chown orangepi:orangepi /usr/lib/aarch64-linux-gnu/qt5/examples -R
+
+qmake -v
diff --git a/external/packages/bsp/overlays_arm64/usr/bin/install_ros.sh b/external/packages/bsp/overlays_arm64/usr/bin/install_ros.sh
new file mode 100755
index 000000000000..e7c99880c815
--- /dev/null
+++ b/external/packages/bsp/overlays_arm64/usr/bin/install_ros.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+#mirror_url=http://mirrors.ustc.edu.cn
+mirror_url=https://repo.huaweicloud.com
+
+if [[ -n $1 && $1 =~ ros1|ros2 ]]; then
+ version=$1
+else
+ echo "usage: install_ros.sh ros1/ros2"
+ exit
+fi
+
+release=$(lsb_release -cs)
+
+if [[ $version == "ros1" && $release =~ focal ]]; then
+
+ [[ -f /etc/apt/sources.list.d/ros-latest.list ]] && sudo rm /etc/apt/sources.list.d/ros-latest.list
+ sudo sh -c "echo deb ${mirror_url}/ros/ubuntu $(lsb_release -sc) main > /etc/apt/sources.list.d/ros1.list"
+ sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
+ sudo apt update
+ sudo apt install -y ros-noetic-desktop-full
+
+ sudo sh -c 'echo "source /opt/ros/noetic/setup.bash" >> /root/.bashrc'
+ echo "source /opt/ros/noetic/setup.bash" >> /home/orangepi/.bashrc
+
+ sudo apt install -y python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
+
+ sudo sh -c 'echo "151.101.84.133 raw.githubusercontent.com" >> /etc/hosts'
+ source /opt/ros/noetic/setup.bash
+ sudo rosdep init
+ rosdep update
+
+ exit
+fi
+
+if [[ $version == "ros2" && $release =~ focal ]]; then
+
+ sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
+ echo "deb [arch=$(dpkg --print-architecture)] ${mirror_url}/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list
+
+ sudo apt update
+ sudo apt install -y ros-galactic-desktop
+ sudo apt install -y ros-dev-tools
+
+ sudo sh -c 'echo "source /opt/ros/galactic/setup.bash" >> /root/.bashrc'
+ echo "source /opt/ros/galactic/setup.bash" >> /home/orangepi/.bashrc
+
+ source /opt/ros/galactic/setup.bash
+ ros2 -h
+
+ exit
+
+fi
+
+if [[ $version == "ros2" && $release =~ jammy ]]; then
+
+ sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
+ echo "deb [arch=$(dpkg --print-architecture)] ${mirror_url}/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list
+
+ sudo apt update
+ sudo apt install -y ros-humble-desktop
+ sudo apt install -y ros-dev-tools
+
+ sudo sh -c 'echo "source /opt/ros/humble/setup.bash" >> /root/.bashrc'
+ echo "source /opt/ros/humble/setup.bash" >> /home/orangepi/.bashrc
+
+ source /opt/ros/humble/setup.bash
+ ros2 -h
+
+ exit
+
+fi
+
+echo "Unsupported System!"
diff --git a/external/packages/bsp/overlays_arm64/usr/bin/io b/external/packages/bsp/overlays_arm64/usr/bin/io
new file mode 100755
index 0000000000000000000000000000000000000000..7365344ea1d2f40fb2ce624eb8989faf15682c50
GIT binary patch
literal 76188
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