ynh-hotspot 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. #!/bin/bash
  2. #
  3. # Wifi Hotspot app for YunoHost
  4. # Copyright (C) 2015 Julien Vaubourg <julien@vaubourg.com>
  5. # Contribute at https://github.com/labriqueinternet/hotspot_ynh
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. source /usr/share/yunohost/helpers
  20. # Functions
  21. ## State functions
  22. has_ip6delegatedprefix() {
  23. [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]
  24. }
  25. is_nat_set() {
  26. local gateway_interface=${1}
  27. iptables -w -nvt nat -L POSTROUTING | grep MASQUERADE | grep -q "${gateway_interface}"
  28. }
  29. is_ip4nataddr_set() {
  30. ip address show dev "${wifi_device}" 2>/dev/null | grep -q "${ip4_nat_prefix}.1/24"
  31. }
  32. is_ip6addr_set() {
  33. ip address show dev "${wifi_device}" 2>/dev/null | grep -q "${ip6_addr}/64"
  34. }
  35. is_ip6firewall_set() {
  36. ip6tables -w -nvL FORWARD | grep DROP | grep -q "${wifi_device}"
  37. }
  38. is_forwarding_set() {
  39. local ip6=$(sysctl net.ipv6.conf.all.forwarding | awk '{ print $NF; }')
  40. local ip4=$(sysctl net.ipv4.conf.all.forwarding | awk '{ print $NF; }')
  41. [[ "${ip6}" -eq 1 ]] && [[ "${ip4}" -eq 1 ]]
  42. }
  43. is_dhcpd6_running() {
  44. [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-$app.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv6-$app.pid") > /dev/null
  45. }
  46. is_dhcpd4_running() {
  47. [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-$app.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv4-$app.pid") > /dev/null
  48. }
  49. is_hostapd_running() {
  50. systemctl is-active "hostapd@${app}" &>/dev/null
  51. }
  52. is_other_hostapd_running() {
  53. other_hostapd_services=$(systemctl list-units --state=running hostapd@*.service | grep -v "^hostapd@$app.service")
  54. [[ -n "${other_hostapd_service}" ]]
  55. }
  56. is_running() {
  57. if has_ip6delegatedprefix; then
  58. if ! is_ip6addr_set; then
  59. return 1
  60. fi
  61. if [[ "${ip6_firewall}" -eq 1 ]] && ! is_ip6firewall_set; then
  62. return 1
  63. fi
  64. if ! is_dhcpd6_running; then
  65. return 1
  66. fi
  67. fi
  68. if ! is_ip4nataddr_set; then
  69. return 1
  70. fi
  71. if ! is_dhcpd4_running; then
  72. return 1
  73. fi
  74. if ! is_hostapd_running; then
  75. return 1
  76. fi
  77. if ! is_forwarding_set; then
  78. return 1
  79. fi
  80. if [[ -n ${new_gateway_interface} ]] && ! is_nat_set "${new_gateway_interface}"; then
  81. return 1
  82. fi
  83. return 0
  84. }
  85. ## Setters
  86. set_nat() {
  87. local gateway_interface=${1}
  88. iptables -w -t nat -A POSTROUTING -o "${gateway_interface}" -j MASQUERADE
  89. }
  90. set_ipaddr() {
  91. if ! is_ip4nataddr_set; then
  92. echo "hotspot ${wifi_device}: Set IPv4 NAT address"
  93. ip address add "${ip4_nat_prefix}.1/24" dev "${wifi_device}"
  94. fi
  95. if has_ip6delegatedprefix && ! is_ip6addr_set && ip route get 1.2.3.4 | grep -q tun0; then
  96. echo "hotspot ${wifi_device}: Set IPv6 address ${ip6_addr}"
  97. ip address delete "${ip6_addr}/64" dev tun0 &>/dev/null
  98. ip address add "${ip6_addr}/64" dev "${wifi_device}"
  99. fi
  100. }
  101. set_ipfirewall() {
  102. # Set ipv6 firewalling
  103. if has_ip6delegatedprefix && [[ "${ip6_firewall}" -eq 1 ]] && ! is_ip6firewall_set; then
  104. echo "hotspot ${wifi_device}: Set IPv6 firewalling"
  105. ip6tables -w -A FORWARD -i "${wifi_device}" -j ACCEPT
  106. ip6tables -w -A FORWARD -o "${wifi_device}" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
  107. ip6tables -w -A FORWARD -o "${wifi_device}" -j DROP
  108. fi
  109. }
  110. set_forwarding() {
  111. local ip6_gateway=$(ip -6 route | awk '/default via/ { print $3; }')
  112. local wired_interface=$(ip -6 route | awk '/default via/ { print $5; }')
  113. sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null
  114. sysctl -w net.ipv4.conf.all.forwarding=1 >/dev/null
  115. if [[ -n "${ip6_gateway}" ]]; then
  116. # Enabling IPv6 forwarding removes the default route, so we need to add it back.
  117. # See https://askubuntu.com/questions/463625/ipv6-forwarding-kills-ipv6-connection/463654#463654
  118. ip route add default via "${ip6_gateway}" dev ${wired_interface}
  119. fi
  120. }
  121. start_dhcpd() {
  122. # Run DHCPv4 server
  123. if ! is_dhcpd4_running; then
  124. echo "hotspot ${wifi_device}: Start the DHCPv4 server (dnsmasq)"
  125. dnsmasq -C /etc/dnsmasq.$app/dhcpdv4.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid
  126. fi
  127. # Run DHCPv6 server
  128. if has_ip6delegatedprefix && ! is_dhcpd6_running; then
  129. echo "hotspot ${wifi_device}: Start the NDP and DHCPv6 server (dnsmasq)"
  130. dnsmasq -C /etc/dnsmasq.$app/dhcpdv6.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid
  131. fi
  132. }
  133. configure_hostapd() {
  134. local ethaddr=$(ip link show dev "${wifi_device}" | grep link/ether | awk -F: '{ printf "02:%s:%s:%s:%s:00", $2, $3, $4, $5 }')
  135. ip link set addr "${ethaddr}" dev "${wifi_device}"
  136. }
  137. ## Unsetters
  138. unset_nat() {
  139. local gateway_interface=${1}
  140. iptables -w -t nat -D POSTROUTING -o "${gateway_interface}" -j MASQUERADE
  141. }
  142. unset_ipaddr() {
  143. if is_ip4nataddr_set; then
  144. echo "hotspot ${wifi_device}: Unset IPv4 NAT address"
  145. ip address delete "${ip4_nat_prefix}.1/24" dev "${wifi_device}"
  146. fi
  147. if has_ip6delegatedprefix && is_ip6addr_set; then
  148. echo "hotspot ${wifi_device}: Unset IPv6 address ${ip6_addr}"
  149. ip address delete "${ip6_addr}/64" dev "${wifi_device}"
  150. fi
  151. }
  152. unset_ipfirewall() {
  153. if has_ip6delegatedprefix && [[ "${ip6_firewall}" -eq 1 ]] && is_ip6firewall_set; then
  154. echo "hotspot ${wifi_device}: Unset IPv6 firewalling"
  155. ip6tables -w -D FORWARD -i "${wifi_device}" -j ACCEPT
  156. ip6tables -w -D FORWARD -o "${wifi_device}" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
  157. ip6tables -w -D FORWARD -o "${wifi_device}" -j DROP
  158. fi
  159. }
  160. unset_forwarding() {
  161. sysctl -w net.ipv6.conf.all.forwarding=0 >/dev/null
  162. sysctl -w net.ipv4.conf.all.forwarding=0 >/dev/null
  163. }
  164. stop_dhcpd() {
  165. if is_dhcpd6_running; then
  166. echo "hotspot ${wifi_device}: Stop the NDP and DHCPv6 server (dnsmasq)"
  167. kill $(cat /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid)
  168. rm -f /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid
  169. fi
  170. if is_dhcpd4_running; then
  171. echo "hotspot ${wifi_device}: Stop the DHCPv4 server (dnsmasq)"
  172. kill $(cat /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid)
  173. rm -f /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid
  174. fi
  175. }
  176. stop_hostapd() {
  177. systemctl stop "hostapd@${app}"
  178. }
  179. if [ "$1" != restart ]; then
  180. # Variables
  181. echo -n "Retrieving Yunohost settings... "
  182. app=__APP__
  183. service_enabled=$(ynh_app_setting_get --app=$app --key=service_enabled)
  184. wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device)
  185. wifi_channel=$(ynh_app_setting_get --app=$app --key=wifi_channel)
  186. wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid)
  187. wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure)
  188. wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase)
  189. ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall)
  190. ip6_dns=$(ynh_app_setting_get --app=$app --key=ip6_dns)
  191. ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net)
  192. ip6_addr="${ip6_net}1"
  193. ip4_dns=$(ynh_app_setting_get --app=$app --key=ip4_dns)
  194. ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix)
  195. old_gateway_interface=$(ynh_app_setting_get --app=$app --key=gateway_interface)
  196. # The awk syntax is to accomodate to the fact that the ip route output may look like:
  197. # 1.2.3.4 via 192.168.1.254 dev end0 src 192.168.1.35 uid 0
  198. # 1.2.3.4 dev vpn_iloth table 51820 src 5.6.7.8 uid 0
  199. new_gateway_interface=$(ip route get 1.2.3.4 | awk '$2 ~ /^dev$/ { print $3; } $4 ~ /^dev$/ { print $5; }')
  200. echo "OK"
  201. fi
  202. # Script
  203. case "$1" in
  204. start)
  205. if is_running; then
  206. echo "Already started"
  207. exit 0
  208. elif [[ "${service_enabled}" -eq 0 ]]; then
  209. echo "Not starting because hotspot service is disabled"
  210. exit 1
  211. fi
  212. if [[ -z "${wifi_device}" ]]; then
  213. echo "[FAIL] No wifi device selected. Make sure your wifi antenna is plugged-in / available and select it in the Hotspot admin"
  214. exit 1
  215. fi
  216. echo "[$app] Starting..."
  217. touch /tmp/.${service_name}-started
  218. # Check old state of the ipv4 NAT settings
  219. if [[ -n "${old_gateway_interface}" ]] && [[ "${new_gateway_interface}" != "${old_gateway_interface}" ]] && is_nat_set "${old_gateway_interface}"; then
  220. unset_nat "${old_gateway_interface}"
  221. fi
  222. # Set ipv4 NAT
  223. if [[ -n "${new_gateway_interface}" ]] && ! is_nat_set "${new_gateway_interface}"; then
  224. echo "Set NAT"
  225. set_nat "${new_gateway_interface}"
  226. fi
  227. # Set forwarding for ipv6 and ipv4
  228. echo "Set forwarding"
  229. set_forwarding
  230. # Run hostapd
  231. if ! is_hostapd_running; then
  232. echo "Configuring hostapd"
  233. configure_hostapd
  234. echo "Starting hostapd..."
  235. if ! systemctl start "hostapd@${app}"; then
  236. journalctl -u hostapd -n 100 --no-hostname --no-pager
  237. exit 1
  238. fi
  239. sleep 1
  240. fi
  241. set_ipaddr
  242. set_ipfirewall
  243. start_dhcpd
  244. # Update dynamic settings
  245. ynh_app_setting_set --app=$app --key=gateway_interface --value="${new_gateway_interface}"
  246. # Regen-conf dnsmasq to enable dns resolution on dnsmasq for the new interface
  247. yunohost tools regen-conf dnsmasq
  248. ;;
  249. stop)
  250. echo "[$app] Stopping..."
  251. rm -f /tmp/.${service_name}-started
  252. if ! is_other_hostapd_running; then
  253. if [[ -n "${old_gateway_interface}" ]] && is_nat_set "${old_gateway_interface}"; then
  254. echo "Unset NAT"
  255. unset_nat "${old_gateway_interface}"
  256. fi
  257. echo "Unset forwarding"
  258. unset_forwarding
  259. fi
  260. unset_ipaddr
  261. unset_ipfirewall
  262. stop_dhcpd
  263. if is_hostapd_running; then
  264. echo "Stop hostapd"
  265. stop_hostapd
  266. fi
  267. # Regen-conf dnsmasq to disable dns resolution on dnsmasq for the previous interface
  268. yunohost tools regen-conf dnsmasq
  269. ;;
  270. restart)
  271. $0 stop
  272. $0 start
  273. ;;
  274. status)
  275. exitcode=0
  276. if [[ "${service_enabled}" -eq 0 ]]; then
  277. echo "[FAIL] Hotspot Service disabled"
  278. exit 1
  279. fi
  280. if [[ -z "${wifi_device}" ]]; then
  281. echo "[FAIL] No wifi device selected. Make sure your wifi antenna is plugged-in / available and select it in the Hotspot admin"
  282. exit 1
  283. fi
  284. echo "[INFO] Autodetected internet interface: ${new_gateway_interface} (last start: ${old_gateway_interface})"
  285. if is_nat_set "${new_gateway_interface}"; then
  286. echo "[ OK ] IPv4 NAT set"
  287. else
  288. if [[ -z "${new_gateway_interface}" ]]; then
  289. echo "[INFO] No IPv4 NAT set (no internet interface)"
  290. else
  291. echo "[FAIL] No IPv4 NAT set"
  292. fi
  293. exitcode=1
  294. fi
  295. if is_forwarding_set; then
  296. echo "[ OK ] IPv6/IPv4 forwarding set"
  297. else
  298. echo "[FAIL] No IPv6/IPv4 forwarding set"
  299. exitcode=1
  300. fi
  301. if is_hostapd_running; then
  302. echo "[ OK ] Hostapd is running"
  303. else
  304. echo "[FAIL] Hostapd is not running"
  305. exitcode=1
  306. fi
  307. if has_ip6delegatedprefix; then
  308. echo "[INFO] hotspot ${wifi_device}: IPv6 delegated prefix found"
  309. echo "[INFO] hotspot ${wifi_device}: IPv6 address computed from the delegated prefix: ${ip6_addr}"
  310. if is_ip6addr_set; then
  311. echo "[ OK ] hotspot ${wifi_device}: IPv6 address set"
  312. else
  313. echo "[FAIL] hotspot ${wifi_device}: No IPv6 address set"
  314. exitcode=1
  315. fi
  316. if is_ip6firewall_set; then
  317. echo "[ OK ] hotspot ${wifi_device}: IPv6 firewalling set"
  318. else
  319. if [[ "${ip6_firewall}" -eq 1 ]]; then
  320. echo "[FAIL] hotspot ${wifi_device}: No IPv6 firewalling set"
  321. else
  322. echo "[INFO] hotspot ${wifi_device}: No IPv6 firewalling set"
  323. fi
  324. exitcode=1
  325. fi
  326. if is_dhcpd6_running; then
  327. echo "[ OK ] hotspot ${wifi_device}: NDP and DHCPv6 server (dnsmasq) are running"
  328. else
  329. echo "[FAIL] hotspot ${wifi_device}: NDP and DHCPv6 server (dnsmasq) are not running"
  330. exitcode=1
  331. fi
  332. else
  333. echo "[INFO] hotspot ${wifi_device}: No IPv6 delegated prefix found"
  334. fi
  335. if is_dhcpd4_running; then
  336. echo "[ OK ] hotspot ${wifi_device}: DHCPv4 server (dnsmasq) is running"
  337. else
  338. echo "[FAIL] hotspot ${zifi_device}: DHCPv4 (dnsmasq) is not running"
  339. exitcode=1
  340. fi
  341. if is_ip4nataddr_set; then
  342. echo "[ OK ] hotspot ${wifi_device}: IPv4 NAT address set"
  343. else
  344. echo "[FAIL] hotspot ${wifi_device}: No IPv4 NAT address set"
  345. exitcode=1
  346. fi
  347. exit ${exitcode}
  348. ;;
  349. *)
  350. echo "Usage: $0 {start|stop|restart|status}"
  351. exit 1
  352. ;;
  353. esac
  354. exit 0