]>
Commit | Line | Data |
---|---|---|
1 | #!/bin/bash | |
2 | ############################################################################### | |
3 | # # | |
4 | # IPFire.org - A linux based firewall # | |
5 | # Copyright (C) 2010 Michael Tremer & Christian Schmidt # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU 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 General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
21 | ||
22 | PPP_SUPPORTED_AUTH_METHODS="chap pap" | |
23 | ||
24 | EXIT_PPPD_ERROR=${EXIT_ERROR} | |
25 | EXIT_PPPD_ERROR_FATAL=$(( ${EXIT_ERROR} + 1 )) | |
26 | ||
27 | # This function monitors the pppd activity. | |
28 | ||
29 | function pppd_angel() { | |
30 | local device="${1}" | |
31 | assert isset device | |
32 | ||
33 | local config_file="${2}" | |
34 | assert isset config_file | |
35 | shift 2 | |
36 | ||
37 | local holdoff_time | |
38 | ||
39 | while [ $# -gt 0 ]; do | |
40 | case "${1}" in | |
41 | --holdoff-time=*) | |
42 | holdoff_time="$(cli_get_val "${1}")" | |
43 | ;; | |
44 | *) | |
45 | warning "Unrecognized argument: ${1}" | |
46 | return ${EXIT_ERROR} | |
47 | ;; | |
48 | esac | |
49 | shift | |
50 | done | |
51 | ||
52 | local pppd_ret ret | |
53 | while :; do | |
54 | # Execute ppp daemon. | |
55 | pppd_exec "${device}" "${config_file}" | |
56 | ret=$? | |
57 | ||
58 | pppd_ret=$(( ${ret} % 0x0f )) | |
59 | ret=$(( ${ret} >> 6 )) | |
60 | ||
61 | log DEBUG "pppd exited with error code ${pppd_ret}" | |
62 | ||
63 | case "${ret}" in | |
64 | ${EXIT_OK}) | |
65 | # pppd terminated gracefully. Propagating... | |
66 | return ${EXIT_OK} | |
67 | ;; | |
68 | ${EXIT_PPPD_ERROR}) | |
69 | # pppd has a (non-fatal) error, in which case we | |
70 | # restart it instantly, so it will try to re-establish | |
71 | # the connection. | |
72 | ;; | |
73 | ${EXIT_PPPD_ERROR_FATAL}) | |
74 | # pppd has a fatal error. We cannot go on from here | |
75 | # because there is either no chance to establish a connection | |
76 | # without any user interaction, or we will damage the system. | |
77 | log ERROR "Fatal error. Not going to restart pppd." | |
78 | return ${pppd_ret} | |
79 | ;; | |
80 | *) | |
81 | log ERROR "Invalid return code: ${ret}" | |
82 | return ${EXIT_ERROR} | |
83 | ;; | |
84 | esac | |
85 | ||
86 | isset holdoff_time || continue | |
87 | ||
88 | # When we got here, we need to wait a little bit and restart the | |
89 | # ppp daemon soon. | |
90 | log INFO "Restarting pppd in ${holdoff_time}s" | |
91 | sleep ${holdoff_time} | |
92 | done | |
93 | } | |
94 | ||
95 | function pppd_exec() { | |
96 | local device="${1}" | |
97 | assert isset device | |
98 | ||
99 | local config_file="${2}" | |
100 | assert isset config_file | |
101 | shift 2 | |
102 | ||
103 | # Execute pppd. | |
104 | cmd "pppd file ${config_file} $@" | |
105 | local ret=$? | |
106 | ||
107 | # Evaluate return code. | |
108 | local error_code | |
109 | case "${ret}" in | |
110 | 0) | |
111 | # Pppd has detached, or otherwise the connection was successfully | |
112 | # established and terminated at the peer's request. | |
113 | log DEBUG "pppd exited gracefully" | |
114 | error_code=${EXIT_OK} | |
115 | ;; | |
116 | 1) | |
117 | # An immediately fatal error of some kind occurred, such as an | |
118 | # essential system call failing, or running out of virtual memory. | |
119 | log ERROR "pppd crashed for an unknown reason" | |
120 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
121 | ;; | |
122 | 2) | |
123 | # An error was detected in processing the options given, such as two | |
124 | # mutually exclusive options being used. | |
125 | log ERROR "pppd: Configuration error" | |
126 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
127 | ;; | |
128 | 3) | |
129 | # Pppd is not setuid-root and the invoking user is not root. | |
130 | log ERROR "pppd: Launched with insufficient privileges" | |
131 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
132 | ;; | |
133 | 4) | |
134 | # The kernel does not support PPP, for example, the PPP kernel driver is | |
135 | # not included or cannot be loaded. | |
136 | log ERROR "pppd: Kernel does not support PPP" | |
137 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
138 | ;; | |
139 | 5) | |
140 | # Pppd terminated because it was sent a SIGINT, SIGTERM or SIGHUP signal. | |
141 | log ERROR "pppd: Received SIGINT, SIGTERM or SIGHUP signal" | |
142 | error_code=${EXIT_PPPD_ERROR} | |
143 | ;; | |
144 | 6) | |
145 | # The serial port could not be locked. | |
146 | log ERROR "pppd: Serial port could not be locked" | |
147 | error_code=${EXIT_PPPD_ERROR} | |
148 | ;; | |
149 | 7) | |
150 | # The serial port could not be opened. | |
151 | log ERROR "pppd: Serial port could not be opened" | |
152 | error_code=${EXIT_PPPD_ERROR} | |
153 | ;; | |
154 | 8) | |
155 | # The connect script failed (returned a non-zero exit status). | |
156 | log ERROR "pppd: Connect script failed" | |
157 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
158 | ;; | |
159 | 9) | |
160 | # The command specified as the argument to the pty option could not be run. | |
161 | log ERROR "pppd: Could not run pty command" | |
162 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
163 | ;; | |
164 | 10) | |
165 | # The PPP negotiation failed, that is, it didn't reach the point where at | |
166 | # least one network protocol (e.g. IP) was running. | |
167 | log ERROR "pppd: Protocol negotiation failed" | |
168 | error_code=${EXIT_PPPD_ERROR} | |
169 | ;; | |
170 | 11) | |
171 | # The peer system failed (or refused) to authenticate itself. | |
172 | log ERROR "pppd: peer system failed (or refused) to authenticate itself" | |
173 | error_code=${EXIT_PPPD_ERROR} | |
174 | ;; | |
175 | 12) | |
176 | # The link was established successfully and terminated because it was idle. | |
177 | log ERROR "pppd: Terminated because of idleness" | |
178 | error_code=${EXIT_PPPD_ERROR} | |
179 | ;; | |
180 | 13) | |
181 | # The link was established successfully and terminated because the connect time | |
182 | # limit was reached. | |
183 | log ERROR "pppd: connect time limit was reached" | |
184 | error_code=${EXIT_PPPD_ERROR} | |
185 | ;; | |
186 | 14) | |
187 | # Callback was negotiated and an incoming call should arrive shortly. | |
188 | # We should not be using this, so make it fatal that nobody is able to | |
189 | # abuse the feature. | |
190 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
191 | ;; | |
192 | 15) | |
193 | # The link was terminated because the peer is not responding to echo requests. | |
194 | log ERROR "pppd: Peer is not responding to echo requests" | |
195 | error_code=${EXIT_PPPD_ERROR} | |
196 | ;; | |
197 | 16) | |
198 | # The link was terminated by the modem hanging up. | |
199 | log ERROR "pppd: Modem hung up" | |
200 | error_code=${EXIT_PPPD_ERROR} | |
201 | ;; | |
202 | 17) | |
203 | # The PPP negotiation failed because serial loopback was detected. | |
204 | log ERROR "pppd: Serial loopback detected" | |
205 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
206 | ;; | |
207 | 18) | |
208 | # The init script failed (returned a non-zero exit status). | |
209 | log ERROR "pppd: Init script failed" | |
210 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
211 | ;; | |
212 | 19) | |
213 | # We failed to authenticate ourselves to the peer. | |
214 | log ERROR "pppd: Authentication failed" | |
215 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
216 | ;; | |
217 | *) | |
218 | log ERROR "pppd: Unhandled exit code: ${ret}" | |
219 | error_code=${EXIT_PPPD_ERROR_FATAL} | |
220 | ;; | |
221 | esac | |
222 | ||
223 | return $(( (${error_code} << 6) + ${ret} )) | |
224 | } | |
225 | ||
226 | function pppd_start() { | |
227 | local device="${1}" | |
228 | assert isset device | |
229 | ||
230 | # This will block until the connection has been established or | |
231 | # pppd exited. | |
232 | service_start "pppd@${device}.service" | |
233 | } | |
234 | ||
235 | function pppd_stop() { | |
236 | local device="${1}" | |
237 | assert isset device | |
238 | ||
239 | service_stop "pppd@${device}.service" | |
240 | } | |
241 | ||
242 | function pppd_status() { | |
243 | local device="${1}" | |
244 | assert isset device | |
245 | ||
246 | service_status "pppd@${device}.service" | |
247 | } | |
248 | ||
249 | function ppp_common_ip_pre_up() { | |
250 | local zone=${1} | |
251 | shift | |
252 | ||
253 | if ! zone_exists ${zone}; then | |
254 | error "Zone '${zone}' does not exist." | |
255 | return ${EXIT_ERROR} | |
256 | fi | |
257 | ||
258 | routing_db_from_ppp ${zone} ipv4 | |
259 | ||
260 | return ${EXIT_OK} | |
261 | } | |
262 | ||
263 | function ppp_common_ipv4_up() { | |
264 | local zone=${1} | |
265 | shift | |
266 | ||
267 | if ! zone_exists ${zone}; then | |
268 | error "Zone '${zone}' does not exist." | |
269 | return ${EXIT_ERROR} | |
270 | fi | |
271 | ||
272 | routing_db_set ${zone} ipv4 active 1 | |
273 | routing_update ${zone} ipv4 | |
274 | routing_default_update | |
275 | ||
276 | return ${EXIT_OK} | |
277 | } | |
278 | ||
279 | function ppp_common_ipv4_down() { | |
280 | local zone=${1} | |
281 | shift | |
282 | ||
283 | if ! zone_exists ${zone}; then | |
284 | error "Zone '${zone}' does not exist." | |
285 | return ${EXIT_ERROR} | |
286 | fi | |
287 | ||
288 | # Remove the information about this zone from the routing database | |
289 | # and update the routing table. | |
290 | routing_db_remove ${zone} ipv4 | |
291 | routing_update ${zone} ipv4 | |
292 | routing_default_update | |
293 | ||
294 | # Save accounting information | |
295 | ppp_accounting ${zone} | |
296 | ||
297 | return ${EXIT_OK} | |
298 | } | |
299 | ||
300 | function ppp_common_ipv6_up() { | |
301 | local zone=${1} | |
302 | shift | |
303 | ||
304 | if ! zone_exists ${zone}; then | |
305 | error "Zone '${zone}' does not exist." | |
306 | return ${EXIT_ERROR} | |
307 | fi | |
308 | ||
309 | # Add information about this zone to the routing database. | |
310 | routing_db_from_ppp ${zone} ipv6 | |
311 | ||
312 | routing_db_set ${zone} ipv6 active 1 | |
313 | routing_update ${zone} ipv6 | |
314 | routing_default_update | |
315 | ||
316 | return ${EXIT_OK} | |
317 | } | |
318 | ||
319 | function ppp_common_ipv6_down() { | |
320 | local zone=${1} | |
321 | shift | |
322 | ||
323 | if ! zone_exists ${zone}; then | |
324 | error "Zone '${zone}' does not exist." | |
325 | return ${EXIT_ERROR} | |
326 | fi | |
327 | ||
328 | # Remove the information about this zone from the routing database | |
329 | # and update the routing table. | |
330 | routing_db_remove ${zone} ipv6 | |
331 | routing_update ${zone} ipv6 | |
332 | routing_default_update | |
333 | ||
334 | # Save accounting information | |
335 | ppp_accounting ${zone} | |
336 | ||
337 | return ${EXIT_OK} | |
338 | } | |
339 | ||
340 | function ppp_secret() { | |
341 | local USER=${1} | |
342 | local SECRET=${2} | |
343 | local a | |
344 | local secret | |
345 | local user | |
346 | ||
347 | # Updateing secret file | |
348 | > ${PPP_SECRETS}.tmp | |
349 | while read user a secret; do | |
350 | if [ "'${USER}'" != "${user}" ]; then | |
351 | echo "${user} ${a} ${secret}" >> ${PPP_SECRETS}.tmp | |
352 | fi | |
353 | done < ${PPP_SECRETS} | |
354 | echo "'${USER}' * '${SECRET}'" >> ${PPP_SECRETS}.tmp | |
355 | cat ${PPP_SECRETS}.tmp > ${PPP_SECRETS} | |
356 | rm -f ${PPP_SECRETS}.tmp | |
357 | } | |
358 | ||
359 | function ppp_accounting() { | |
360 | local zone=${1} | |
361 | shift | |
362 | ||
363 | db_ppp_update ${zone} --duration="${CONNECT_TIME}" \ | |
364 | --rcvd="${BYTES_RCVD}" --sent="${BYTES_SENT}" | |
365 | } | |
366 | ||
367 | function pppd_write_config() { | |
368 | local file=${1}; shift | |
369 | assert isset file | |
370 | ||
371 | local auth | |
372 | local baudrate | |
373 | local connect_cmd | |
374 | local default_asyncmap="true" | |
375 | local interface | |
376 | local ipv6="true" | |
377 | local lcp_echo_failure=3 | |
378 | local lcp_echo_interval=20 | |
379 | local linkname | |
380 | local mtu mru | |
381 | local password | |
382 | local plugin plugin_options | |
383 | local pty | |
384 | local refuses | |
385 | local serial="false" | |
386 | local username | |
387 | local value | |
388 | ||
389 | while [ $# -gt 0 ]; do | |
390 | case "${1}" in | |
391 | --auth=*) | |
392 | auth=$(cli_get_val ${1}) | |
393 | ;; | |
394 | --baudrate=*) | |
395 | baudrate=$(cli_get_val ${1}) | |
396 | assert isoneof baudrate ${SERIAL_BAUDRATES} | |
397 | ;; | |
398 | --connect-command=*) | |
399 | connect_cmd=$(cli_get_val ${1}) | |
400 | ;; | |
401 | # Enable or disable the use of the default asyncmap. | |
402 | --default-asyncmap=*) | |
403 | value=$(cli_get_val ${1}) | |
404 | if enabled value; then | |
405 | default_asyncmap="true" | |
406 | else | |
407 | default_asyncmap="false" | |
408 | fi | |
409 | ;; | |
410 | # The name of the created ppp interface. | |
411 | --interface=*) | |
412 | interface=$(cli_get_val ${1}) | |
413 | ;; | |
414 | # IPv6 | |
415 | --ipv6=*) | |
416 | ipv6="$(cli_get_val ${1})" | |
417 | ;; | |
418 | # LCP echo failure. | |
419 | --lcr-echo-failure=*) | |
420 | lcr_echo_failure=$(cli_get_val ${1}) | |
421 | ||
422 | if ! isinteger ${lcr_echo_failure}; then | |
423 | error "--lcr-echo-failure= requires a number" | |
424 | return ${EXIT_ERROR} | |
425 | fi | |
426 | ;; | |
427 | # LCP echo interval. | |
428 | --lcr-echo-interval=*) | |
429 | lcr_echo_interval=$(cli_get_val ${1}) | |
430 | ||
431 | if ! isinteger ${lcr_echo_failure}; then | |
432 | error "--lcr-echo-interval= requires a number" | |
433 | return ${EXIT_ERROR} | |
434 | fi | |
435 | ;; | |
436 | # Maximum Transmission Unit | |
437 | --mtu=*) | |
438 | mtu=$(cli_get_val ${1}) | |
439 | ;; | |
440 | # Maximum Receive Unit | |
441 | --mru=*) | |
442 | mru=$(cli_get_val ${1}) | |
443 | ;; | |
444 | --password=*) | |
445 | password=$(cli_get_val ${1}) | |
446 | ;; | |
447 | --plugin=*) | |
448 | plugin=$(cli_get_val ${1}) | |
449 | ;; | |
450 | --plugin-options=*) | |
451 | plugin_options=$(cli_get_val ${1}) | |
452 | ;; | |
453 | --pty=*) | |
454 | pty=$(cli_get_val ${1}) | |
455 | ;; | |
456 | # Refused authentication methods | |
457 | --refuse=*) | |
458 | list_append refuses "$(cli_get_val "${1}")" | |
459 | error_log "REFUSES $refuses $1" | |
460 | ;; | |
461 | # Sets if the modem is a serial device. | |
462 | --serial=*) | |
463 | serial=$(cli_get_val ${1}) | |
464 | ;; | |
465 | --serial-device=*) | |
466 | serial_device=$(cli_get_val ${1}) | |
467 | ;; | |
468 | --username=*) | |
469 | username=$(cli_get_val ${1}) | |
470 | ;; | |
471 | *) | |
472 | log WARNING "Unhandled argument: ${1}" | |
473 | ;; | |
474 | esac | |
475 | shift | |
476 | done | |
477 | ||
478 | if [ -z "${interface}" ]; then | |
479 | log ERROR "You need to set the interface name: ${interface}" | |
480 | return ${EXIT_ERROR} | |
481 | fi | |
482 | linkname="${interface}" | |
483 | ||
484 | if isset auth; then | |
485 | if ! isoneof ${auth} ${PPP_SUPPORTED_AUTH_METHODS}; then | |
486 | log ERROR "Unsupported auth method: ${auth}" | |
487 | return ${EXIT_ERROR} | |
488 | fi | |
489 | fi | |
490 | ||
491 | if enabled serial; then | |
492 | assert isset serial_device | |
493 | assert [ -c "${serial_device}" ] | |
494 | fi | |
495 | ||
496 | # Set the user credentials. | |
497 | ppp_secret "${username}" "${password}" | |
498 | ||
499 | # Write the configuration header. | |
500 | mkdir -p $(dirname ${file}) 2>/dev/null | |
501 | config_header "PPP daemon configuration file" > ${file} | |
502 | ||
503 | # At first, set the name of the link. | |
504 | print "linkname ${linkname}\n" >> ${file} | |
505 | ||
506 | # Configure the interface/zone name. | |
507 | ( | |
508 | print "# Interface name" | |
509 | print "ifname ${interface}" | |
510 | ||
511 | ) >> ${file} | |
512 | ||
513 | # Plugin settings | |
514 | if isset plugin; then | |
515 | ( | |
516 | print "# Plugin settings" | |
517 | print "plugin ${plugin} ${plugin_options}" | |
518 | ||
519 | ) >> ${file} | |
520 | fi | |
521 | ||
522 | # pty settings | |
523 | if isset pty; then | |
524 | ( | |
525 | print "# pty settings" | |
526 | print "pty \"${pty}\"" | |
527 | ||
528 | ) >> ${file} | |
529 | fi | |
530 | ||
531 | # User authentication | |
532 | if isset username; then | |
533 | ( | |
534 | print "# User authentication" | |
535 | print "user ${username}" | |
536 | ||
537 | print "noauth" | |
538 | if isset auth; then | |
539 | print "require-${auth}" | |
540 | fi | |
541 | ||
542 | # Refused authentication methods | |
543 | for refuse in ${refuses}; do | |
544 | print "refuse-${refuse}" | |
545 | done | |
546 | ||
547 | ) >> ${file} | |
548 | fi | |
549 | ||
550 | # IPv6 | |
551 | if enabled ipv6; then | |
552 | ( | |
553 | print "# IPv6 support" | |
554 | print "+ipv6" | |
555 | ||
556 | ) >> ${file} | |
557 | fi | |
558 | ||
559 | # MTU/MRU settings | |
560 | if isset mtu; then | |
561 | isset mru || mru=${mtu} | |
562 | ||
563 | ( | |
564 | print "# MTU/MRU settings" | |
565 | print "mtu ${mtu}" | |
566 | print "mru ${mru}" | |
567 | ||
568 | ) >> ${file} | |
569 | fi | |
570 | ||
571 | if enabled serial; then | |
572 | ( | |
573 | print "# Serial modem settings" | |
574 | print "${serial_device} ${baudrate}" | |
575 | print "crtscts" | |
576 | print "lock" | |
577 | print "modem" | |
578 | ||
579 | ) >> ${file} | |
580 | ||
581 | # Connect command | |
582 | if isset connect_cmd; then | |
583 | ( | |
584 | print "# Connect command" | |
585 | print "connect \"${connect_cmd}\"" | |
586 | ||
587 | ) >> ${file} | |
588 | fi | |
589 | fi | |
590 | ||
591 | # Default asyncmap. | |
592 | if enabled default_asyncmap; then | |
593 | ( | |
594 | print "# Use the default asyncmap." | |
595 | print "default-asyncmap" | |
596 | ||
597 | ) >> ${file} | |
598 | fi | |
599 | ||
600 | # LCP settings. | |
601 | ( | |
602 | print "# LCP settings" | |
603 | print "lcp-echo-failure ${lcp_echo_failure}" | |
604 | print "lcp-echo-interval ${lcp_echo_interval}" | |
605 | ||
606 | ) >> ${file} | |
607 | ||
608 | # Add the default settings. | |
609 | ( | |
610 | print "# Disable the compression" | |
611 | print "noccp noaccomp nodeflate nopcomp novj novjccomp nobsdcomp nomppe" | |
612 | ||
613 | print "noipdefault nodetach debug" | |
614 | ) >> ${file} | |
615 | ||
616 | return ${EXIT_OK} | |
617 | } |