]> git.ipfire.org Git - people/ms/network.git/blob - functions.ppp
7673648a61be8c2cfe9dc14a9d1d649b66ef72ed
[people/ms/network.git] / functions.ppp
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 print
511 ) >> ${file}
512
513 # Plugin settings
514 if isset plugin; then
515 (
516 print "# Plugin settings"
517 print "plugin ${plugin} ${plugin_options}"
518 print
519 ) >> ${file}
520 fi
521
522 # pty settings
523 if isset pty; then
524 (
525 print "# pty settings"
526 print "pty \"${pty}\""
527 print
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 print
547 ) >> ${file}
548 fi
549
550 # IPv6
551 if enabled ipv6; then
552 (
553 print "# IPv6 support"
554 print "+ipv6"
555 print
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 print
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 print
579 ) >> ${file}
580
581 # Connect command
582 if isset connect_cmd; then
583 (
584 print "# Connect command"
585 print "connect \"${connect_cmd}\""
586 print
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 print
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 print
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 }