I've made a script that works alongside Irfan Latif's CLI-based AP. I've made some minor modifications to their script, so here's the final result which works in Android 12 (LineageOS 19)(SELinux: Enforcing):
#!/system/bin/sh
# SCRIPT SOURCE: https://android.stackexchange.com/a/215514
# SEE ALSO: https://android.stackexchange.com/a/202335
set -e
#set -x
# this scripts creates a hotspot network
[ "$(id -u)" = 0 ] || { echo 'Not running as root!' >&2; exit 1; }
# Add /vendor/bin/hw to path. That’s where hostapd is in LineageOS 19.0
export PATH=$PATH:/vendor/bin/hw
# check required binaries are on PATH
for bin in iw ip iptables hostapd dnsmasq
do
! which $bin >/dev/null || continue
echo "$bin not found." >&2
exit 1
done
####################
# define variables #
####################
source /data/data/com.termux/files/home/customAP/config.sh
##########################
# start / stop tethering #
##########################
STOP()
(
echo 'Cleaning up. Feel free to ignore any errors in this stage..'
# don't print error messages
#exec >/dev/null 2>&1
# hope there are no other instances of same daemons
pkill -15 hostapd
# Do not kill wpa_supplicant to avoid WifiSelfRecovery from disabling wifi
#pkill -15 wpa_supplicant
pkill -15 dnsmasq
# remove RPDB rule and iptables rule
ip rule del lookup main
iptables -D INPUT -i $AP_INTERFACE -p udp -m udp --dport 67 -j ACCEPT
# delete AP interface
#ip link show
iw dev $AP_INTERFACE del
#ip link show
rm -rf $DIR
echo "Cleanup done."
)
if [ "$1" = stop ]
then
STOP || true
exit
elif [ "$1" != start ]
then
echo 'Usage:' >&2
printf '\t%s\n' "$(basename "$0") start|stop" >&2
exit 1
fi
################
# basic checks #
################
if ! iw phy | grep -A10 'Supported interface modes:' | grep -q '\*[ ]*AP'
then
echo 'AP mode not supported.' >&2
exit 1
fi
if ! iw dev $WIFI_INTERFACE link | grep -q '^Not connected'
then
echo 'First disconnect form Wi-Fi.' >&2
exit 1
fi
##########################
# stop running instances #
##########################
STOP || true
#####################################
# create virtual wireless interface #
#####################################
if ! iw dev $WIFI_INTERFACE interface add $AP_INTERFACE type __ap
then
echo "Couldn't create AP interface." >&2
exit 1
fi
#####################################
# configure newly created interface #
#####################################
echo 'Configuring network...'
# activate the interface and add IP
ip link set up dev $AP_INTERFACE
ip addr add ${IP}/24 broadcast ${SUBNET}.255 dev $AP_INTERFACE
# Android doesn't look up into main table by default
ip rule add lookup main
#######################
# access point daemon #
#######################
# create configuration file
mkdir -p "$DIR"
cat <<-EOF >$DIR/hostapd.conf
logger_syslog=1
logger_syslog_level=0
logger_stdout=-1
logger_stdout_level=0
country_code=AR
# network name
ssid=$SSID
# passphrase to use for protected access
wpa_passphrase=$PASSCODE
# network interface to listen on
interface=$AP_INTERFACE
# wi-fi driver
driver=nl80211
# set operation mode, 'g' for 2.4GHz band
hw_mode=g
# WLAN frequency channel to use
channel=9
#Win10 compatibility stuff
ieee8021x=0
eap_server=0
ignore_broadcast_ssid=0
# Security
wpa=2
# key management protocol; use pre-share key
wpa_key_mgmt=WPA-PSK
#Win10 compatibility stuff
wpa_pairwise=TKIP
rsn_pairwise=CCMP
EOF
#Show condiguration
#echo '========================================='
#echo 'Using this hostapd.conf configuration file:'
#cat $DIR/hostapd.conf
#echo '========================================='
echo 'Starting hostapd...'
hostapd -B $DIR/hostapd.conf
################################################
# run a dhcp server to assign IP's dynamically #
################################################
# create configuration file
cat <<-EOF >$DIR/dnsmasq.conf
# we dont want DNS server, only DHCP
port=0
# nameservers to be sent to clients
dhcp-option=6,1.1.1.1,1.0.0.1
# range of IPs to make available to wlan devices and when to renew IP
dhcp-range=$IP,${SUBNET}.254,24h
# where to save leases
dhcp-leasefile=$DIR/dnsmasq.leases
# respond to requests from a different IP broadcast subnet
dhcp-authoritative
# don't look for any hosts file and resolv file
no-hosts
no-resolv
EOF
# open listening port
iptables -I INPUT -i $AP_INTERFACE -p udp -m udp --dport 67 -j ACCEPT
#echo "dnsmasq config:"
#cat $DIR/dnsmasq.conf
echo 'Starting DHCP server...'
dnsmasq --pid-file -C $DIR/dnsmasq.conf </dev/null
echo "All Done!"
If you're having issues with the AP starting up feel free to uncomment some of my left-over debugging commands.
Also note that most times it takes a couple of minutes (2mins) until Windows 10 actually detects the AP and allows you to connect to it.
For more information about that script, checkout the links at the beginning.
The contents of config.sh:
SSID=MyAP # set this to your desired string (avoid spaces and non-ascii characters)
PASSCODE=foobarfoobar # set this to your desired string (8 to 63 characters)
WIFI_INTERFACE=wlan0 # set this according to your device (check with 'lshw' or 'ip link show')
AP_INTERFACE=${WIFI_INTERFACE}-AP
#AP_INTERFACE=p2p0
DIR=/data/local/tmp/$AP_INTERFACE
SUBNET=192.168.43
IP=${SUBNET}.1
Now, this is the script I made. Run it as root to enable the traffic redirection. Note that running it with "sudo" doesn't work.
# ./captive_portal.sh start
#!/system/bin/sh
source /data/data/com.termux/files/home/customAP/config.sh
# IP the traffic will be redirected to
REDIRECT_IP=192.168.43.1
STOP()(
echo "Stopping dnsmasq..."
pkill -15 dnsmasq
echo "Restoring previous dnsmasq config..."
mv $DIR/dnsmasq.conf.bak $DIR/dnsmasq.conf
echo "Removing phishing redirect iptables rules..."
iptables -t nat -D PREROUTING -i wlan0-AP -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -D PREROUTING -i wlan0-AP -p udp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -D PREROUTING -i wlan0-AP -p tcp -j DNAT --to-destination $REDIRECT_IP
echo "Starting dnsmasq..."
dnsmasq --pid-file -C $DIR/dnsmasq.conf </dev/null
echo "Done."
)
if [ "$1" = "stop" ]
then
STOP || true
exit
elif [ "$1" != "start" ]
then
echo 'Usage:' >&2
printf '\t%s\n' "$(basename "$0") start|stop" >&2
exit 1
fi
echo "Stopping dnsmasq..."
pkill -15 dnsmasq
echo "Setting up iptables rules..."
iptables -t nat -A PREROUTING -i wlan0-AP -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i wlan0-AP -p udp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i wlan0-AP -p tcp -j DNAT --to-destination $REDIRECT_IP
# Backup the dnsmasq config
cp $DIR/dnsmasq.conf $DIR/dnsmasq.conf.bak
# Overwrite the config
rm $DIR/dnsmasq.conf
cat <<-EOF >$DIR/dnsmasq.conf
# nameservers to be sent to clients
dhcp-option=6,$IP,$IP
# range of IPs to make available to wlan devices and when to renew IP
dhcp-range=$IP,${SUBNET}.254,24h
# where to save leases
dhcp-leasefile=$DIR/dnsmasq.leases
# respond to requests from a different IP broadcast subnet
dhcp-authoritative
# don't look for any hosts file and resolv file
no-hosts
no-resolv
# Reply to all queries with my IP
address=/#/$REDIRECT_IP
EOF
echo "Starting dnsmasq..."
dnsmasq --pid-file -C $DIR/dnsmasq.conf </dev/null
echo "Done."
Then you just start a web server in the IP 192.168.43.1 and you're done!
python -m http.server 8080 --bind 192.168.43.1
Note that this doesn't do any authentication. The script above only does the traffic redirection. You'll have to implement authentication yourself, probably through nginx, PHP and iptables.