pppoe.ncdi 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. include_guard "pppoe"
  2. template pppoe {
  3. alias("_arg0") dev;
  4. alias("_arg1") username;
  5. alias("_arg2") password;
  6. alias("_arg3") pre_up_template;
  7. # Choose which NCD interpreter will be used for the pppd event scripts.
  8. var("/usr/local/badvpn/bin/badvpn-ncd") ncd_interpreter_path;
  9. # Retry point here.
  10. var("false") retrying;
  11. backtrack_point() retry_point;
  12. If (retrying) {
  13. sleep("5000");
  14. };
  15. retrying->set("true");
  16. # Create a temporary directory.
  17. concat("/run/ncd-pppoe-", dev) run_dir;
  18. run({"/bin/rm", "-rf", run_dir}, {});
  19. run({"/bin/mkdir", run_dir}, {"/bin/rm", "-rf", run_dir});
  20. # Build paths for pppd scripts and other files.
  21. concat(run_dir, "/ncd-request.socket") socket_path;
  22. concat(run_dir, "/pppoe.pid") pppoe_pid_path;
  23. concat(run_dir, "/pap-secrets") pap_secrets_path;
  24. concat(run_dir, "/chap-secrets") chap_secrets_path;
  25. concat(run_dir, "/pppd2.tdb") pppdb_path;
  26. concat(run_dir, "/resolv.conf") resolv_conf_path;
  27. concat(run_dir, "/script-auth-up") path_auth_up;
  28. concat(run_dir, "/script-auth-down") path_auth_down;
  29. concat(run_dir, "/script-auth-fail") path_auth_fail;
  30. concat(run_dir, "/script-ip-pre-up") path_ip_pre_up;
  31. concat(run_dir, "/script-ip-up") path_ip_up;
  32. concat(run_dir, "/script-ip-down") path_ip_down;
  33. concat(run_dir, "/script-ipv6-up") path_ipv6_up;
  34. concat(run_dir, "/script-ipv6-down") path_ipv6_down;
  35. concat(run_dir, "/script-ipx-up") path_ipx_up;
  36. concat(run_dir, "/script-ipx-down") path_ipx_down;
  37. # Write secrets files.
  38. call("pppoe__write_secrets", {pap_secrets_path, username, password});
  39. call("pppoe__write_secrets", {chap_secrets_path, username, password});
  40. # Write pppd scripts. These will contact us via the request socket.
  41. call("pppoe__write_script", {"ip-pre-up", path_ip_pre_up});
  42. call("pppoe__write_script", {"ip-up", path_ip_up});
  43. call("pppoe__write_script", {"ip-down", path_ip_down});
  44. # Build path arguments for pppd;
  45. concat("pid-dir=", run_dir) arg_pid_dir;
  46. concat("pap-secrets=", pap_secrets_path) arg_pap_secrets;
  47. concat("chap-secrets=", chap_secrets_path) arg_chap_secrets;
  48. concat("pppdb=", pppdb_path) arg_pppdb;
  49. concat("resolv.conf=", resolv_conf_path) arg_resolv_conf;
  50. concat("auth-up=", path_auth_up) arg_auth_up;
  51. concat("auth-down=", path_auth_down) arg_auth_down;
  52. concat("auth-fail=", path_auth_fail) arg_auth_fail;
  53. concat("ip-pre-up=", path_ip_pre_up) arg_ip_pre_up;
  54. concat("ip-up=", path_ip_up) arg_ip_up;
  55. concat("ip-down=", path_ip_down) arg_ip_down;
  56. concat("ipv6-up=", path_ipv6_up) arg_ipv6_up;
  57. concat("ipv6-down=", path_ipv6_down) arg_ipv6_down;
  58. concat("ipx-up=", path_ipx_up) arg_ipx_up;
  59. concat("ipx-down=", path_ipx_down) arg_ipx_down;
  60. # Create state variables and blockers. When the request server
  61. # receives requests it will update those variables and blockers.
  62. var("down") state;
  63. var("") current_ifname;
  64. var("") current_local_ip;
  65. var("") current_remote_ip;
  66. value({}) current_dns_servers;
  67. blocker() ip_pre_up_blocker;
  68. blocker() ip_pre_up_done_blocker;
  69. blocker() ip_up_blocker;
  70. # Start request server.
  71. sys.request_server({"unix", socket_path}, "pppoe__request_handler", {});
  72. # Start pppd.
  73. sys.start_process({
  74. "/usr/sbin/pppd", "nodetach", "plugin", "rp-pppoe.so", dev, "noipdefault", "hide-password",
  75. "usepeerdns", "user", username,
  76. "path", arg_pid_dir, "path", arg_pap_secrets, "path", arg_chap_secrets, "path", arg_pppdb,
  77. "path", arg_resolv_conf,
  78. "path", arg_auth_up, "path", arg_auth_down, "path", arg_auth_fail, "path", arg_ip_pre_up,
  79. "path", arg_ip_up, "path", arg_ip_down, "path", arg_ipv6_up, "path", arg_ipv6_down,
  80. "path", arg_ipx_up, "path", arg_ipx_down
  81. }, "", ["deinit_kill_time":"2000"]) pppd;
  82. # Start a process which will cause retrying when pppd dies.
  83. spawn("pppoe__pppd_wait", {});
  84. # Wait for ip-pre-up.
  85. ip_pre_up_blocker->use();
  86. # Grab the current state variables, so the user doesn't
  87. # see any unexpected changes.
  88. var(current_ifname) ifname;
  89. var(current_local_ip) local_ip;
  90. var(current_remote_ip) remote_ip;
  91. var(current_dns_servers) dns_servers;
  92. # Call pre-up callback template.
  93. call_with_caller_target(pre_up_template, {ifname, local_ip, remote_ip, dns_servers}, "_caller");
  94. # Allow pre-up script to terminate.
  95. ip_pre_up_done_blocker->up();
  96. # Wait for connection to go up.
  97. ip_up_blocker->use();
  98. }
  99. template pppoe__pppd_wait {
  100. # Wait for pppd to die.
  101. _caller.pppd->wait();
  102. # Retry.
  103. _caller.retry_point->go();
  104. }
  105. template pppoe__write_secrets {
  106. alias("_arg0") file_path;
  107. alias("_arg1") username;
  108. alias("_arg2") password;
  109. # Escape username and password.
  110. regex_replace(username, {"\""}, {"\\\""}) username_esc;
  111. regex_replace(password, {"\""}, {"\\\""}) password_esc;
  112. # Write empty file and chmod it.
  113. file_write(file_path, "");
  114. run({"/bin/chmod", "600", file_path}, {});
  115. # Build contents.
  116. concat("\"", username_esc, "\" * \"", password_esc, "\"\n") contents;
  117. # Write file.
  118. file_write(file_path, contents);
  119. }
  120. template pppoe__write_script {
  121. alias("_arg0") event;
  122. alias("_arg1") script_path;
  123. # This string is an NCD script which will be run by pppd.
  124. # When run, it will contact us via the request server,
  125. # and the requests will be processed in pppoe__request_handler.
  126. var("#!<NCD_INTERPRETER_PATH>
  127. process main {
  128. # Hardcoded strings.
  129. var(\"<EVENT>\") hardcoded_event;
  130. var(\"<SOCKET>\") hardcoded_socket;
  131. # Start timeout to kill us after some time if we don't manage
  132. # to contact the server.
  133. spawn(\"timeout_process\", {});
  134. # Build event data map.
  135. getargs() args;
  136. value([\"EVENT\":hardcoded_event, \"ARGS\":args]) msg_map;
  137. var({\"DEVICE\", \"IFNAME\", \"IPLOCAL\", \"IPREMOTE\", \"PEERNAME\", \"LOCALNAME\",
  138. \"SPEED\", \"ORIG_UID\", \"PPPLOGNAME\", \"CONNECT_TIME\", \"BYTES_SENT\",
  139. \"BYTES_RCVD\", \"LINKNAME\", \"DNS1\", \"DNS2\", \"WINS1\", \"WINS2\"}) var_names;
  140. Foreach (var_names As var_name) {
  141. getenv(var_name) env;
  142. If (env.exists) {
  143. msg_map->insert(var_name, env);
  144. };
  145. };
  146. # Connect to socket.
  147. sys.request_client({\"unix\", hardcoded_socket}) client;
  148. # Send request.
  149. client->request(msg_map, \"reply_handler\", \"finished_handler\", {});
  150. }
  151. template reply_handler {
  152. print();
  153. }
  154. template finished_handler {
  155. # Request was received by server, exit now.
  156. exit(\"0\");
  157. }
  158. template timeout_process {
  159. # Sleep some time.
  160. sleep(\"5000\");
  161. # Timed out, exit now.
  162. exit(\"1\");
  163. }
  164. " ) script_contents_template;
  165. # Replace some constants in the script with the right values.
  166. regex_replace(script_contents_template,
  167. {"<NCD_INTERPRETER_PATH>", "<EVENT>", "<SOCKET>"},
  168. {_caller.ncd_interpreter_path, event, _caller.socket_path}
  169. ) script_contents;
  170. # Write the script.
  171. file_write(script_path, script_contents);
  172. # Make it executable.
  173. run({"/bin/chmod", "+x", script_path}, {});
  174. }
  175. template pppoe__request_handler {
  176. alias("_caller") pppoe;
  177. # Get event type from request.
  178. value(_request.data) request;
  179. request->get("EVENT") event;
  180. # Match to known types.
  181. val_equal(event, "ip-down") is_ip_down;
  182. val_equal(event, "ip-pre-up") is_ip_pre_up;
  183. val_equal(event, "ip-up") is_ip_up;
  184. If (is_ip_down) {
  185. # Set state.
  186. pppoe.state->set("down");
  187. # Set blockers down.
  188. pppoe.ip_up_blocker->down();
  189. pppoe.ip_pre_up_done_blocker->down();
  190. pppoe.ip_pre_up_blocker->down();
  191. }
  192. Elif (is_ip_pre_up) {
  193. # Expecting to be in "down" state here.
  194. val_different(pppoe.state, "down") state_is_wrong;
  195. If (state_is_wrong) {
  196. pppoe.retry_point->go();
  197. _request->finish();
  198. };
  199. # Get variables from request.
  200. request->get("IFNAME") ifname;
  201. request->get("IPLOCAL") local_ip;
  202. request->get("IPREMOTE") remote_ip;
  203. request->try_get("DNS1") dns1;
  204. request->try_get("DNS2") dns2;
  205. # Write variables.
  206. pppoe.current_ifname->set(ifname);
  207. pppoe.current_local_ip->set(local_ip);
  208. pppoe.current_remote_ip->set(remote_ip);
  209. pppoe.current_dns_servers->reset({});
  210. If (dns1.exists) {
  211. pppoe.current_dns_servers->insert(pppoe.current_dns_servers.length, dns1);
  212. };
  213. If (dns2.exists) {
  214. pppoe.current_dns_servers->insert(pppoe.current_dns_servers.length, dns2);
  215. };
  216. # Set state.
  217. pppoe.state->set("pre-up");
  218. # Set ip-pre-up blocker up.
  219. pppoe.ip_pre_up_blocker->up();
  220. # Wait for pre-up to be finished. This causes the script contacting
  221. # us to not return until then, and effectively delays pppd in setting
  222. # the device up and calling the ip-up script.
  223. pppoe.ip_pre_up_done_blocker->use();
  224. }
  225. Elif(is_ip_up) {
  226. # Expecting to be in "pre-up" state here.
  227. val_different(pppoe.state, "pre-up") state_is_wrong;
  228. If (state_is_wrong) {
  229. pppoe.retry_point->go();
  230. _request->finish();
  231. };
  232. # Set state.
  233. pppoe.state->set("up");
  234. # Set ip-up blocker up.
  235. pppoe.ip_up_blocker->up();
  236. };
  237. # Finish request.
  238. _request->finish();
  239. }
  240. template pppoe__escapeshellarg {
  241. alias("_arg0") input;
  242. regex_replace(input, {"'"}, {"\\'"}) replaced;
  243. concat("'", replaced, "'") result;
  244. }