connection_tether-ppp.sh 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. #!/bin/bash
  2. #
  3. # Copyright (c) 2019 Clementine Computing LLC.
  4. #
  5. # This file is part of PopuFare.
  6. #
  7. # PopuFare 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. # PopuFare 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 PopuFare. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. #
  21. # This script establishes and maintains the SSH tunnel connection to the central
  22. # server. The SSH tunnel is the transport method all communication the local
  23. # services use to communicate to the central server's complementary services.
  24. #
  25. # This script is the PPP VERSION, meant for production.
  26. #
  27. export BASEDIR='/home/bus/'
  28. echo "## connection_tether-ppp $BASEDIR"
  29. . $BASEDIR/bin/common_values.sh
  30. # Do this once at boot time, but do it again after a tunnel abort request...
  31. #
  32. generate_ssh_targets
  33. ssh_fail_counter=0
  34. debug_print()
  35. {
  36. echo $@
  37. }
  38. # Every once in a while pppd will report connection, and still have a ppp session open to the modem, but
  39. # the router on the other end will have croaked, or the underlying GPRS connection will be too unreliable, or
  40. # sometimes even a pppd will hang such that it needs a SIGKILL to get rid of it... This function does that
  41. # and then cleans up after the dead pppd by removing its dropfiles and locks.
  42. #
  43. force_kill_pppd()
  44. {
  45. /usr/bin/killall pppd
  46. rm -rf /var/lock/*ttyGPRS
  47. rm -rf $GPRS_DROPFILE
  48. }
  49. # This function goes and looks at the version dropfiles for packages, gathering their package names
  50. # and versions to report when we check in with the server to log that we connected, and at what firmware and
  51. # config revision.
  52. #
  53. output_versions()
  54. {
  55. for file in `ls $CHECKSUM_AND_VERSION_PATH/*.version`; do
  56. echo -n " `echo $file | sed -r 's/^.*\/(.*)\.version$/\1/'`=`cat $file`";
  57. done
  58. }
  59. # This function extracts a field from the network ID dropfile by name (also used by the checkin process)
  60. #
  61. output_net_ids_field()
  62. {
  63. field="$1"
  64. cat $NETWORK_ID_DROPFILE | grep "$field" | cut -d'=' -f2 | xargs -n1 echo -n
  65. }
  66. # This function attempts to check in with the version server and report hardware and network serial numbers
  67. # so that us sysadmin types can see each time a unit attached to the network, which unit it was, and what software
  68. # it was running at the time.
  69. #
  70. perform_post_connect_checkin()
  71. {
  72. if [ -f $SERIAL_NUM_FILE ]; then
  73. busunitnum="`cat $SERIAL_NUM_FILE 2> /dev/null`"
  74. fi
  75. equipnum="`cat $EQUIP_NUM_FILE 2> /dev/null || echo 0`"
  76. version="`output_versions`"
  77. imei="`output_net_ids_field IMEI`"
  78. imsi="`output_net_ids_field IMSI`"
  79. mac="`output_net_ids_field ETH0`"
  80. # Send these gathered data to the update daemon. The leading '#' tells the server that this is a
  81. #checkin, not an update request.
  82. echo -e "#$busunitnum\t$equipnum\t$mac\t$imei\t$imsi\t$version" | nc -q1 localhost $UPDATE_DAEMON_PORT
  83. }
  84. # This function generates the server->client port forwards to allow a sysadmin to log into any unit that is on
  85. # the network by equipment number, serial number, or bus number. The three parameters are:
  86. #
  87. # 1: The path to the file containing the identifying number
  88. # 2: The base port number on the remote server to add the identifying number to to get the server-side port that will
  89. # forward to port 22 (sshd) on the client side.
  90. # 3: An optional parameter which if present is taken as a set of command line flags to cut to apply to the contents
  91. # of the file specified by $1 to extract the numeric component (for instance, serial numbers may be in the form XYZ-1234
  92. # in which case it's really the 1234 part we're after...
  93. #
  94. generate_reverse_phonehome_component()
  95. {
  96. file="$1"
  97. base="$2"
  98. cut_cmdline="$3"
  99. #Make sure the candidate dropfile exists
  100. if [ -f "$file" ]; then
  101. if [ -n "$cut_cmdline" ]; then
  102. #Grab the desired substring
  103. num="`cat $file | cut $cut_cmdline`"
  104. else
  105. #Grab its contents
  106. num="`cat $file`";
  107. fi
  108. #Make sure those contents are indeed numeric...
  109. if (echo "$num" | egrep -q '^[0-9]+$'); then
  110. #Make sure that number is within an acceptable range so as not to overflow
  111. if [ "$num" -gt "0" -a "$num" -le "$REVERSE_PHONE_HOME_MAX_TOKEN" ]; then
  112. echo -n " -R$((base + num)):localhost:22";
  113. fi
  114. fi
  115. fi
  116. }
  117. # This function calls the above component function for each identifying number we want to do a port forward based on...
  118. #
  119. generate_reverse_phonehome_string()
  120. {
  121. #If the reverse phone home feature is disabled, return without printing any commandline args
  122. if [ "$REVERSE_PHONE_HOME" -eq "0" ]; then return; fi
  123. generate_reverse_phonehome_component $EQUIP_NUM_FILE $REVERSE_PHONE_HOME_EQNUM_BASE
  124. generate_reverse_phonehome_component $SERIAL_NUM_FILE $REVERSE_PHONE_HOME_SERIALNUM_BASE "-d- -f3"
  125. }
  126. # This function performs teardown on a dead ssh connection, and increments the ssh connect failure counter. If
  127. # that counter has reached its maximum value, we force pppd down and try a clean redial. Otherwise, we just sleep and
  128. # try again.
  129. #
  130. clean_up_after_tunnel_teardown()
  131. {
  132. #If the tunnel was intentionally aborted for the purpose of switching servers
  133. if [ -f $TUNNEL_ABORT_DROPFILE ]; then
  134. debug_print "SSH tunnel aborted, removing dropfiles..."
  135. #Remove the dropfiles...
  136. /bin/rm -f $TUNNEL_DROPFILE $SSH_TUNNEL_PIDFILE
  137. #Generate new ssh target from server config dropfiles
  138. generate_ssh_targets
  139. /bin/rm -f $TUNNEL_ABORT_DROPFILE
  140. #Reset the failure counter
  141. ssh_fail_counter=0
  142. #Sleep until it is time to try again
  143. /bin/sleep $SLEEP_AFTER_TUNNEL_ABORT
  144. else
  145. #OTHERWISE, we assume that the modem lost signal, or the router or remote server went wonky...
  146. debug_print "SSH client dead, removing dropfiles..."
  147. #Remove the dropfiles...
  148. /bin/rm -f $TUNNEL_DROPFILE $SSH_TUNNEL_PIDFILE
  149. ssh_fail_counter=$((ssh_fail_counter + 1))
  150. if [ "$ssh_fail_counter" -ge "$MAX_FAIL_HANGUP" ]; then
  151. debug_print "pppd claims to be up; tunnel failed $ssh_fail_counter times, killing pppd to force redial."
  152. force_kill_pppd
  153. fi
  154. debug_print "Sleeping before any retry attempts..."
  155. #Sleep before trying this again...
  156. /bin/sleep $SLEEP_AFTER_TUNNEL_FAILURE
  157. fi
  158. }
  159. while true; do
  160. # If our GRPS connection has died...
  161. #
  162. if [ ! -f $GPRS_DROPFILE ]; then
  163. ssh_fail_counter=0
  164. debug_print "Attempting to dial..."
  165. # Try to re-"dial" our ISP
  166. #
  167. /usr/bin/pon gprs
  168. # Give the modem a minute to do its thing...
  169. #
  170. /bin/sleep $SLEEP_AFTER_DIAL
  171. fi
  172. # If we've just been asked to abort the SSH tunnel
  173. #
  174. if [ -f $TUNNEL_ABORT_DROPFILE ]; then
  175. # Generate new ssh target from server config dropfiles
  176. #
  177. generate_ssh_targets
  178. /bin/rm -f $TUNNEL_ABORT_DROPFILE
  179. # Reset the failure counter
  180. #
  181. ssh_fail_counter=0
  182. fi
  183. # If we now have an active GPRS network connection, try and bring a tunnel up...
  184. #
  185. if [ -f $GPRS_DROPFILE ]; then
  186. # If we have no active tunnel already...
  187. #
  188. if [ ! -f $TUNNEL_DROPFILE ]; then
  189. debug_print "Attempting to establish SSH tunnel... (Attempt number $((ssh_fail_counter + 1)))"
  190. # Attempt to create our tunnel... (incliding (if REVERSE_PHONE_HOME != 0) reverse phone home support
  191. #
  192. ssh $SSH_OPTIONS -i $SSH_IDENTITY $SSH_FORWARDS `generate_reverse_phonehome_string` -p $SSH_PORT $SSH_TARGET &
  193. # Remember its PID
  194. #
  195. echo "$!" > $SSH_TUNNEL_PIDFILE
  196. # Wait a few seconds to allow SSH negotiations
  197. #
  198. /bin/sleep $SLEEP_BEFORE_TUNNEL_TEST
  199. debug_print -n "Testing our new tunnel..."
  200. # Test to see if our tunnel is really up...
  201. #
  202. if [ "`nc localhost $HELLO_DAEMON_PORT < /dev/null`" = $HELLO_DAEMON_MESSAGE ]; then
  203. debug_print " It works."
  204. ssh_fail_counter=0
  205. debug_print "Checking in with server to report net IDs and package versions... "
  206. perform_post_connect_checkin
  207. debug_print "Touching dropfile and waiting for SSH to terminate..."
  208. # Touch our dropfile indicating the tunnel is up...
  209. #
  210. /bin/touch $TUNNEL_DROPFILE
  211. # and wait for the the SSH client process to end
  212. #
  213. wait `cat $SSH_TUNNEL_PIDFILE`
  214. # Clean Up...
  215. #
  216. clean_up_after_tunnel_teardown
  217. else
  218. debug_print " No luck..."
  219. debug_print "Issuing kill to SSH client...."
  220. # Kill the defunct and/or too slow to use SSH client
  221. #
  222. /bin/kill `cat $SSH_TUNNEL_PIDFILE`
  223. # Wait for the process to terminate
  224. #
  225. wait `cat $SSH_TUNNEL_PIDFILE`
  226. # Clean Up...
  227. #
  228. clean_up_after_tunnel_teardown
  229. fi
  230. else
  231. # This means we _think_ we have an SSH tunnel, but it's not one we set up...
  232. #
  233. debug_print -n "We seem to already have a pre-existing tunnel... Monitoring it."
  234. # Loop and periodically test this tunnel... When this condition fails, we're done...
  235. #
  236. while [ "`nc localhost $HELLO_DAEMON_PORT < /dev/null`" = $HELLO_DAEMON_MESSAGE ]; do
  237. # Sleep for a while before testing this tunnel again...
  238. #
  239. /bin/sleep $SLEEP_MONITORING_TUNNEL
  240. done
  241. # Clean Up...
  242. #
  243. clean_up_after_tunnel_teardown
  244. fi
  245. else
  246. # If we don't have an active GPRS session, that means we just failed at dialing
  247. #
  248. debug_print "Dialing failed... Sleeping"
  249. /bin/sleep $SLEEP_BETWEEN_REDIALS
  250. fi
  251. done