#!/bin/bash ### BEGIN INIT INFO # Provides: ynh-hotspot # Required-Start: $network $remote_fs $syslog $all # Required-Stop: $network $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Set prerequisites for wifi hotspot. # Description: Set prerequisites for wifi hotspot. ### END INIT INFO # Wifi Hotspot app for YunoHost # Copyright (C) 2015 Julien Vaubourg # Contribute at https://github.com/jvaubourg/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 . # Functions ## State functions has_vpnclient_app() { [ -e /tmp/.ynh-vpnclient-started ] } has_ip6delegatedprefix() { [ "${ynh_ip6_net}" != none ] } is_nat_set() { internet_device=${1} iptables -nvt nat -L POSTROUTING | grep MASQUERADE | grep -q "${internet_device}" } is_ip4nataddr_set() { ip address show dev "${ynh_wifi_device}" 2> /dev/null | grep -q "${ynh_ip4_nat_prefix}.1/24" } is_ip6addr_set() { ip address show dev "${ynh_wifi_device}" 2> /dev/null | grep -q "${ynh_ip6_addr}/64" } is_forwarding_set() { ip6=$(sysctl net.ipv6.conf.all.forwarding | awk '{ print $NF; }') ip4=$(sysctl net.ipv4.conf.all.forwarding | awk '{ print $NF; }') [ "${ip6}" -eq 1 -a "${ip4}" -eq 1 ] } is_dhcpdv6_set() { [ -e /etc/dnsmasq.d/dhcpdv6.conf ] } is_dhcpdv4_set() { [ -e /etc/dnsmasq.d/dhcpdv4.conf ] } is_hostapd_running() { service hostapd status &> /dev/null if [ $? -eq 0 ]; then # If the wifi antenna was unplugged if ip link show dev "${ynh_wifi_device}" | grep -q DOWN; then service hostapd stop &> /dev/null return 1 fi return 0 fi return 1 } is_dnsmasq_running() { service dnsmasq status &> /dev/null } is_running() { ( has_ip6delegatedprefix && is_ip6addr_set && is_dhcpdv6_set || ! has_ip6delegatedprefix )\ && is_nat_set "${new_internet_device}" && is_ip4nataddr_set && is_forwarding_set && is_hostapd_running\ && is_dhcpdv4_set && is_dnsmasq_running } ## Setters set_nat() { internet_device=${1} iptables -t nat -A POSTROUTING -o "${internet_device}" -j MASQUERADE } set_ip4nataddr() { ip address add "${ynh_ip4_nat_prefix}.1/24" dev "${ynh_wifi_device}" } set_ip6addr() { ip address delete "${ynh_ip6_addr}/64" dev tun0 &> /dev/null ip address add "${ynh_ip6_addr}/64" dev "${ynh_wifi_device}" } set_forwarding() { sysctl -w net.ipv6.conf.all.forwarding=1 > /dev/null sysctl -w net.ipv4.conf.all.forwarding=1 > /dev/null } set_dhcpd() { if has_ip6delegatedprefix; then cp /etc/dnsmasq.d.tpl/dhcpdv6.conf.tpl /etc/dnsmasq.d/dhcpdv6.conf sed "s||${ynh_wifi_device}|g" -i /etc/dnsmasq.d/dhcpdv6.conf sed "s||${ynh_ip6_net}|g" -i /etc/dnsmasq.d/dhcpdv6.conf sed "s||${ynh_ip6_dns0}|g" -i /etc/dnsmasq.d/dhcpdv6.conf sed "s||${ynh_ip6_dns1}|g" -i /etc/dnsmasq.d/dhcpdv6.conf fi cp /etc/dnsmasq.d.tpl/dhcpdv4.conf.tpl /etc/dnsmasq.d/dhcpdv4.conf sed "s||${ynh_ip4_dns0}|g" -i /etc/dnsmasq.d/dhcpdv4.conf sed "s||${ynh_ip4_dns1}|g" -i /etc/dnsmasq.d/dhcpdv4.conf sed "s||${ynh_wifi_device}|g" -i /etc/dnsmasq.d/dhcpdv4.conf sed "s||${ynh_ip4_nat_prefix}|g" -i /etc/dnsmasq.d/dhcpdv4.conf } start_hostapd() { cp /etc/hostapd/hostapd.conf{.tpl,} sed "s||${ynh_wifi_device}|g" -i /etc/hostapd/hostapd.conf sed "s||${ynh_wifi_ssid}|g" -i /etc/hostapd/hostapd.conf sed "s||${ynh_wifi_passphrase}|g" -i /etc/hostapd/hostapd.conf sed "s||${ynh_wifi_channel}|g" -i /etc/hostapd/hostapd.conf if [ "${ynh_wifi_n}" -eq 1 ]; then sed "s|||g" -i /etc/hostapd/hostapd.conf else sed "s||#|g" -i /etc/hostapd/hostapd.conf fi if [ "${ynh_wifi_secure}" -eq 1 ]; then sed "s|||g" -i /etc/hostapd/hostapd.conf else sed "s||#|g" -i /etc/hostapd/hostapd.conf fi service hostapd start } ## Unsetters unset_nat() { internet_device=${1} iptables -t nat -D POSTROUTING -o "${internet_device}" -j MASQUERADE } unset_ip4nataddr() { ip address delete "${ynh_ip4_nat_prefix}.1/24" dev "${ynh_wifi_device}" } unset_ip6addr() { ip address delete "${ynh_ip6_addr}/64" dev "${ynh_wifi_device}" } unset_dhcpd() { rm -f /etc/dnsmasq.d/dhcpdv?.conf service dnsmasq restart } unset_forwarding() { sysctl -w net.ipv6.conf.all.forwarding=0 > /dev/null sysctl -w net.ipv4.conf.all.forwarding=0 > /dev/null } stop_hostapd() { service hostapd stop } ## Tools moulinette_get() { var=${1} value=$(yunohost app setting hotspot "${var}") if [[ "${value}" =~ "An instance is already running" ]]; then echo "${value}" >&2 exit 1 fi echo "${value}" } moulinette_vpnclient_get() { var=${1} value=$(yunohost app setting vpnclient "${var}") if [[ "${value}" =~ "An instance is already running" ]]; then echo "${value}" >&2 exit 1 fi echo "${value}" } moulinette_set() { var=${1} value=${2} msg=$(yunohost app setting hotspot "${var}" -v "${value}") if [ ! $? -eq 0 ]; then echo "${msg}" >&2 exit 1 fi } if [ "$1" != restart ]; then # Restart php5-fpm at the first start (it needs to be restarted after the slapd start) if [ ! -e /tmp/.ynh-hotspot-boot ]; then touch /tmp/.ynh-hotspot-boot service php5-fpm restart fi # Variables echo -n "Retrieving Yunohost settings... " ynh_service_enabled=$(moulinette_get service_enabled) ynh_wifi_device=$(moulinette_get wifi_device) ynh_wifi_ssid=$(moulinette_get wifi_ssid) ynh_wifi_secure=$(moulinette_get wifi_secure) ynh_wifi_passphrase=$(moulinette_get wifi_passphrase) ynh_wifi_channel=$(moulinette_get wifi_channel) ynh_wifi_n=$(moulinette_get wifi_n) ynh_ip6_addr=$(moulinette_get ip6_addr) ynh_ip6_net=$(moulinette_get ip6_net) ynh_ip6_dns0=$(moulinette_get ip6_dns0) ynh_ip6_dns1=$(moulinette_get ip6_dns1) ynh_ip4_dns0=$(moulinette_get ip4_dns0) ynh_ip4_dns1=$(moulinette_get ip4_dns1) ynh_ip4_nat_prefix=$(moulinette_get ip4_nat_prefix) old_internet_device=$(moulinette_get internet_device) new_internet_device=$(ip route | awk '/default via/ { print $NF; }') # Switch the NAT interface if there is a VPN ip link show dev tun0 &> /dev/null if [ "$?" -eq 0 ]; then new_internet_device=tun0 fi echo "OK" # Check IPv6 delegated prefix from vpnclient vpnclient_ip6_net=$(moulinette_vpnclient_get ip6_net) if [ ! -z "${vpnclient_ip6_addr}" ]; then if [ "${ynh_ip6_net}" == none ]; then ynh_ip6_net=$vpnclient_ip6_net ynh_ip6_addr=$(moulinette_vpnclient_get ip6_addr) moulinette_set ip6_net "${ynh_ip6_net}" moulinette_set ip6_addr "${ynh_ip6_addr}" else if [ "${ynh_ip6_net}" != "${vpnclient_ip6_net}" ]; then echo "[WARN] The IPv6 delegated prefix is different from the vpnclient one" fi fi fi fi # Script case "$1" in start) if is_running; then echo "Already started" elif [ "${ynh_service_enabled}" -eq 0 ]; then echo "Disabled service" else echo "[hotspot] Starting..." touch /tmp/.ynh-hotspot-started if [ "${new_internet_device}" == tun0 ]; then moulinette_set vpnclient yes else moulinette_set vpnclient no fi # Check old state of the ipv4 NAT settings if [ ! -z "${old_internet_device}" -a "${new_internet_device}" != "${old_internet_device}" ]\ && is_nat_set "${old_internet_device}"; then unset_nat "${old_internet_device}" fi # Set ipv4 NAT if ! is_nat_set "${new_internet_device}"; then echo "Set NAT" set_nat "${new_internet_device}" fi # Set ipv4 NAT address if ! is_ip4nataddr_set; then echo "Set IPv4 NAT address" set_ip4nataddr fi # Set the ipv6 address if has_ip6delegatedprefix && ! is_ip6addr_set; then echo "Set IPv6 address" set_ip6addr fi # Set forwarding for ipv6 and ipv4 if ! is_forwarding_set; then echo "Set forwarding" set_forwarding fi # Run hostapd if ! is_hostapd_running; then echo "Run hostapd" start_hostapd fi # Run DHCP servers if ( has_ip6delegatedprefix && ! is_dhcpdv6_set ) || ! is_dhcpdv4_set; then echo "Set DHCP servers (dnsmasq)" set_dhcpd fi service bind9 stop &> /dev/null service dnsmasq restart # Update dynamic settings moulinette_set internet_device "${new_internet_device}" fi ;; stop) echo "[hotspot] Stopping..." rm -f /tmp/.ynh-hotspot-started if is_nat_set "${old_internet_device}"; then echo "Unset NAT" unset_nat "${old_internet_device}" fi if is_ip4nataddr_set; then echo "Unset IPv4 NAT address" unset_ip4nataddr fi if has_ip6delegatedprefix && is_ip6addr_set; then echo "Unset IPv6 address" unset_ip6addr fi if is_forwarding_set; then echo "Unset forwarding" unset_forwarding fi if is_dhcpdv6_set || is_dhcpdv4_set; then echo "Stop DHCP servers" unset_dhcpd fi if is_hostapd_running; then echo "Stop hostapd" stop_hostapd fi if has_vpnclient_app; then service ynh-vpnclient start fi ;; restart) $0 stop $0 start ;; status) exitcode=0 if [ "${ynh_service_enabled}" -eq 0 ]; then echo "[ERR] Hotspot Service disabled" exitcode=1 fi echo "[INFO] Autodetected internet interface: ${new_internet_device} (last start: ${old_internet_device})" if has_ip6delegatedprefix; then echo "[INFO] IPv6 delegated prefix found" echo "[INFO] IPv6 address computed from the delegated prefix: ${ynh_ip6_addr}" if is_ip6addr_set; then echo "[OK] IPv6 address set" else echo "[ERR] No IPv6 address set" exitcode=1 fi if is_dhcpdv6_set; then echo "[OK] SLAAC & DHCPv6 server set" else echo "[ERR] No SLAAC & DHCPv6 server set" exitcode=1 fi else echo "[INFO] No IPv6 delegated prefix found" fi if is_dhcpdv4_set; then echo "[OK] DHCPv4 server set" else echo "[ERR] No DHCPv4 server set" exitcode=1 fi if is_nat_set "${new_internet_device}"; then echo "[OK] IPv4 NAT set" else echo "[ERR] No IPv4 NAT set" exitcode=1 fi if is_ip4nataddr_set; then echo "[OK] IPv4 NAT address set" else echo "[ERR] No IPv4 NAT address set" exitcode=1 fi if is_forwarding_set; then echo "[OK] IPv6/IPv4 forwarding set" else echo "[ERR] No IPv6/IPv4 forwarding set" exitcode=1 fi if is_dnsmasq_running; then echo "[OK] Dnsmasq is running" else echo "[ERR] Dnsmasq is not running" exitcode=1 fi if is_hostapd_running; then echo "[OK] Hostapd is running" else echo "[ERR] Hostapd is not running" exitcode=1 fi exit ${exitcode} ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 ;; esac exit 0