#!/bin/bash # # Wifi Hotspot app for YunoHost # Copyright (C) 2015 Julien Vaubourg # Contribute at https://github.com/labriqueinternet/hotspot_ynh # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . source /usr/share/yunohost/helpers # Functions ## State functions has_ip6delegatedprefix() { [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]] } ip6addrfromdelegatedprefix() { echo "${ip6_net}1" } is_nat_set() { local gateway_interface=${1} iptables -w -nvt nat -L POSTROUTING | grep MASQUERADE | grep -q "${gateway_interface}" } is_ip4nataddr_set() { ip address show dev "${wifi_device}" 2>/dev/null | grep -q "${ip4_nat_prefix}.1/24" } is_ip6addr_set() { ip address show dev "${wifi_device}" 2>/dev/null | grep -q "$(ip6addrfromdelegatedprefix)/64" } is_ip6firewall_set() { ip6tables -w -nvL FORWARD | grep DROP | grep -q "${wifi_device}" } is_forwarding_set() { local ip6=$(sysctl net.ipv6.conf.all.forwarding | awk '{ print $NF; }') local ip4=$(sysctl net.ipv4.conf.all.forwarding | awk '{ print $NF; }') [[ "${ip6}" -eq 1 ]] && [[ "${ip4}" -eq 1 ]] } is_dhcpd6_running() { [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-$app.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv6-$app.pid") > /dev/null } is_dhcpd4_running() { [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-$app.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv4-$app.pid") > /dev/null } is_hostapd_running() { systemctl is-active "hostapd@${app}" &>/dev/null } is_other_hostapd_running() { other_hostapd_services=$(systemctl list-units --state=running hostapd@*.service | grep -v "^hostapd@$app.service") [[ -n "${other_hostapd_service}" ]] } is_running() { if has_ip6delegatedprefix; then if ! is_ip6addr_set; then return 1 fi if [[ "${ip6_firewall}" -eq 1 ]] && ! is_ip6firewall_set; then return 1 fi if ! is_dhcpd6_running; then return 1 fi fi if ! is_ip4nataddr_set; then return 1 fi if ! is_dhcpd4_running; then return 1 fi if ! is_hostapd_running; then return 1 fi if ! is_forwarding_set; then return 1 fi if [[ -n ${new_gateway_interface} ]] && ! is_nat_set "${new_gateway_interface}"; then return 1 fi return 0 } ## Setters set_nat() { local gateway_interface=${1} iptables -w -t nat -A POSTROUTING -o "${gateway_interface}" -j MASQUERADE } set_ipaddr() { if ! is_ip4nataddr_set; then echo "hotspot ${wifi_device}: Set IPv4 NAT address" ip address add "${ip4_nat_prefix}.1/24" dev "${wifi_device}" fi if has_ip6delegatedprefix && ! is_ip6addr_set; then echo "hotspot ${wifi_device}: Set IPv6 address" ip address delete "$(ip6addrfromdelegatedprefix)/64" dev tun0 &>/dev/null ip address add "$(ip6addrfromdelegatedprefix)/64" dev "${wifi_device}" fi } set_ipfirewall() { # Set ipv6 firewalling if has_ip6delegatedprefix && [[ "${ip6_firewall}" -eq 1 ]] && ! is_ip6firewall_set; then echo "hotspot ${wifi_device}: Set IPv6 firewalling" ip6tables -w -A FORWARD -i "${wifi_device}" -j ACCEPT ip6tables -w -A FORWARD -o "${wifi_device}" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT ip6tables -w -A FORWARD -o "${wifi_device}" -j DROP fi } set_forwarding() { sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null sysctl -w net.ipv4.conf.all.forwarding=1 >/dev/null } start_dhcpd() { # Run DHCPv4 server if ! is_dhcpd4_running; then echo "hotspot ${wifi_device}: Start the DHCPv4 server (dnsmasq)" dnsmasq -C /etc/dnsmasq.$app/dhcpdv4.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid fi # Run DHCPv6 server if has_ip6delegatedprefix && ! is_dhcpd6_running; then echo "hotspot ${wifi_device}: Start the NDP and DHCPv6 server (dnsmasq)" dnsmasq -C /etc/dnsmasq.$app/dhcpdv6.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid fi } configure_hostapd() { local ethaddr=$(ip link show dev "${wifi_device}" | grep link/ether | awk -F: '{ printf "02:%s:%s:%s:%s:00", $2, $3, $4, $5 }') ip link set addr "${ethaddr}" dev "${wifi_device}" } ## Unsetters unset_nat() { local gateway_interface=${1} iptables -w -t nat -D POSTROUTING -o "${gateway_interface}" -j MASQUERADE } unset_ipaddr() { if is_ip4nataddr_set; then echo "hotspot ${wifi_device}: Unset IPv4 NAT address" ip address delete "${ip4_nat_prefix}.1/24" dev "${wifi_device}" fi if has_ip6delegatedprefix && is_ip6addr_set; then echo "hotspot ${wifi_device}: Unset IPv6 address" ip address delete "$(ip6addrfromdelegatedprefix)/64" dev "${wifi_device}" fi } unset_ipfirewall() { if has_ip6delegatedprefix && [[ "${ip6_firewall}" -eq 1 ]] && is_ip6firewall_set; then echo "hotspot ${wifi_device}: Unset IPv6 firewalling" ip6tables -w -D FORWARD -i "${wifi_device}" -j ACCEPT ip6tables -w -D FORWARD -o "${wifi_device}" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT ip6tables -w -D FORWARD -o "${wifi_device}" -j DROP fi } unset_forwarding() { sysctl -w net.ipv6.conf.all.forwarding=0 >/dev/null sysctl -w net.ipv4.conf.all.forwarding=0 >/dev/null } stop_dhcpd() { if is_dhcpd6_running; then echo "hotspot ${wifi_device}: Stop the NDP and DHCPv6 server (dnsmasq)" kill $(cat /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid) rm -f /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid fi if is_dhcpd4_running; then echo "hotspot ${wifi_device}: Stop the DHCPv4 server (dnsmasq)" kill $(cat /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid) rm -f /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid fi } stop_hostapd() { systemctl stop "hostapd@${app}" } if [ "$1" != restart ]; then # Variables echo -n "Retrieving Yunohost settings... " app=__APP__ service_enabled=$(ynh_app_setting_get --app=$app --key=service_enabled) wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) wifi_channel=$(ynh_app_setting_get --app=$app --key=wifi_channel) wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid) wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure) wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase) ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall) ip6_dns=$(ynh_app_setting_get --app=$app --key=ip6_dns) ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) ip4_dns=$(ynh_app_setting_get --app=$app --key=ip4_dns) ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) old_gateway_interface=$(ynh_app_setting_get --app=$app --key=gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') echo "OK" fi # Script case "$1" in start) if is_running; then echo "Already started" exit 0 elif [[ "${service_enabled}" -eq 0 ]]; then echo "Not starting because hotspot service is disabled" exit 1 fi if [[ -z "${wifi_device}" ]]; then echo "[FAIL] No wifi device selected. Make sure your wifi antenna is plugged-in / available and select it in the Hotspot admin" exit 1 fi echo "[$app] Starting..." touch /tmp/.${service_name}-started # Check old state of the ipv4 NAT settings if [[ -n "${old_gateway_interface}" ]] && [[ "${new_gateway_interface}" != "${old_gateway_interface}" ]] && is_nat_set "${old_gateway_interface}"; then unset_nat "${old_gateway_interface}" fi # Set ipv4 NAT if [[ -n "${new_gateway_interface}" ]] && ! is_nat_set "${new_gateway_interface}"; then echo "Set NAT" set_nat "${new_gateway_interface}" fi # Set forwarding for ipv6 and ipv4 echo "Set forwarding" set_forwarding # Run hostapd if ! is_hostapd_running; then echo "Configuring hostapd" configure_hostapd echo "Starting hostapd..." if ! systemctl start "hostapd@${app}"; then journalctl -u hostapd -n 100 --no-hostname --no-pager exit 1 fi sleep 1 fi set_ipaddr set_ipfirewall start_dhcpd # Update dynamic settings ynh_app_setting_set --app=$app --key=gateway_interface --value="${new_gateway_interface}" # Regen-conf dnsmasq to enable dns resolution on dnsmasq for the new interface yunohost tools regen-conf dnsmasq ;; stop) echo "[$app] Stopping..." rm -f /tmp/.${service_name}-started if ! is_other_hostapd_running; then if [[ -n "${old_gateway_interface}" ]] && is_nat_set "${old_gateway_interface}"; then echo "Unset NAT" unset_nat "${old_gateway_interface}" fi echo "Unset forwarding" unset_forwarding fi unset_ipaddr unset_ipfirewall stop_dhcpd if is_hostapd_running; then echo "Stop hostapd" stop_hostapd fi # Regen-conf dnsmasq to disable dns resolution on dnsmasq for the previous interface yunohost tools regen-conf dnsmasq ;; restart) $0 stop $0 start ;; status) exitcode=0 if [[ "${service_enabled}" -eq 0 ]]; then echo "[FAIL] Hotspot Service disabled" exit 1 fi if [[ -z "${wifi_device}" ]]; then echo "[FAIL] No wifi device selected. Make sure your wifi antenna is plugged-in / available and select it in the Hotspot admin" exit 1 fi echo "[INFO] Autodetected internet interface: ${new_gateway_interface} (last start: ${old_gateway_interface})" if is_nat_set "${new_gateway_interface}"; then echo "[ OK ] IPv4 NAT set" else if [[ -z "${new_gateway_interface}" ]]; then echo "[INFO] No IPv4 NAT set (no internet interface)" else echo "[FAIL] No IPv4 NAT set" fi exitcode=1 fi if is_forwarding_set; then echo "[ OK ] IPv6/IPv4 forwarding set" else echo "[FAIL] No IPv6/IPv4 forwarding set" exitcode=1 fi if is_hostapd_running; then echo "[ OK ] Hostapd is running" else echo "[FAIL] Hostapd is not running" exitcode=1 fi if has_ip6delegatedprefix; then echo "[INFO] hotspot ${wifi_device}: IPv6 delegated prefix found" echo "[INFO] hotspot ${wifi_device}: IPv6 address computed from the delegated prefix: $(ip6addrfromdelegatedprefix)" if is_ip6addr_set; then echo "[ OK ] hotspot ${wifi_device}: IPv6 address set" else echo "[FAIL] hotspot ${wifi_device}: No IPv6 address set" exitcode=1 fi if is_ip6firewall_set; then echo "[ OK ] hotspot ${wifi_device}: IPv6 firewalling set" else if [[ "${ip6_firewall}" -eq 1 ]]; then echo "[FAIL] hotspot ${wifi_device}: No IPv6 firewalling set" else echo "[INFO] hotspot ${wifi_device}: No IPv6 firewalling set" fi exitcode=1 fi if is_dhcpd6_running; then echo "[ OK ] hotspot ${wifi_device}: NDP and DHCPv6 server (dnsmasq) are running" else echo "[FAIL] hotspot ${wifi_device}: NDP and DHCPv6 server (dnsmasq) are not running" exitcode=1 fi else echo "[INFO] hotspot ${wifi_device}: No IPv6 delegated prefix found" fi if is_dhcpd4_running; then echo "[ OK ] hotspot ${wifi_device}: DHCPv4 server (dnsmasq) is running" else echo "[FAIL] hotspot ${zifi_device}: DHCPv4 (dnsmasq) is not running" exitcode=1 fi if is_ip4nataddr_set; then echo "[ OK ] hotspot ${wifi_device}: IPv4 NAT address set" else echo "[FAIL] hotspot ${wifi_device}: No IPv4 NAT address set" exitcode=1 fi exit ${exitcode} ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 ;; esac exit 0