Remove remains of the "real" device type
[people/stevee/network.git] / src / network
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 # Parse the command line
23 while [ $# -gt 0 ]; do
24         case "${1}" in
25                 -d|--debug)
26                         DEBUG=1
27                         ;;
28                 *)
29                         action=${1}
30                         ;;
31         esac
32         shift
33         [ -n "${action}" ] && break
34 done
35
36 . /usr/lib/network/functions
37
38 # Read network settings
39 network_settings_read
40
41 function cli_settings() {
42         if cli_help_requested $@; then
43                 cli_show_man network-settings
44                 exit ${EXIT_OK}
45         fi
46
47         if [ -n "${1}" ]; then
48                 settings_set $@
49                 network_settings_write
50         else
51                 network_settings_print
52         fi
53 }
54
55 function cli_device() {
56         if cli_help_requested $@; then
57                 cli_show_man network-device
58                 exit ${EXIT_OK}
59         fi
60
61         local device=${1}
62         local action=${2}
63         shift 2
64
65         if ! isset device; then
66                 cli_show_man network-device
67                 return ${EXIT_ERROR}
68         fi
69
70         assert device_exists ${device}
71
72         case "${action}" in
73                 discover)
74                         cli_device_discover ${device} $@
75                         ;;
76                 monitor)
77                         cli_device_monitor "${device}" $@
78                         ;;
79                 status)
80                         cli_device_status ${device}
81                         ;;
82                 unlock)
83                         cli_device_serial_unlock ${device} $@
84                         ;;
85                 ussd)
86                         cli_device_send_ussd_command "${device}" $@
87                         ;;
88                 *)
89                         cli_show_man network-device
90                         ;;
91         esac
92
93         return ${EXIT_OK}
94 }
95
96 function cli_device_status() {
97         local device=${1}
98         assert device_exists ${device}
99
100         # Disable debugging output here.
101         local log_disable_stdout=${LOG_DISABLE_STDOUT}
102         LOG_DISABLE_STDOUT="true"
103
104         # Save the type of the device for later.
105         local type=$(device_get_type ${device})
106
107         cli_headline 1 "Device status: ${device}"
108         cli_print_fmt1 1 "Name"         "${device}"
109
110         # Handle serial devices.
111         if [ "${type}" = "serial" ]; then
112                 cli_device_status_serial ${device}
113                 return $?
114         fi
115
116         # Print the device status.
117         device_is_up ${device} &>/dev/null
118         local status=$?
119
120         case "${status}" in
121                 ${EXIT_TRUE})
122                         status="${CLR_GREEN_B}UP${CLR_RESET}"
123                         ;;
124                 ${EXIT_FALSE})
125                         status="${CLR_RED_B}DOWN${CLR_RESET}"
126                         ;;
127         esac
128
129         cli_print_fmt1 1 "Status"       "${status}"
130         cli_print_fmt1 1 "Type"         "${type}"
131
132         # Ethernet-compatible?
133         device_is_ethernet_compatible "${device}" &>/dev/null
134         cli_print_fmt1 1 "Ethernet-compatible" "$(cli_print_bool $?)"
135
136         cli_print_fmt1 1 "Address"      "$(device_get_address ${device})"
137         cli_space
138
139         # Print the link speed for ethernet devices.
140         if device_is_up ${device} &>/dev/null; then
141                 local link="$(device_get_link_string "${device}")"
142                 if isset link; then
143                         cli_print_fmt1 1 "Link" "${link}"
144                 fi
145         fi
146
147         cli_print_fmt1 1 "MTU"          "$(device_get_mtu ${device})"
148         cli_space
149
150         # Print device statistics.
151         cli_device_stats 2 ${device}
152
153         # Print some more information.
154         device_has_carrier ${device} &>/dev/null
155         cli_print_fmt1 1 "Has carrier?" "$(cli_print_bool $?)"
156
157         device_is_promisc ${device} &>/dev/null
158         cli_print_fmt1 1 "Promisc"      "$(cli_print_bool $?)"
159         cli_space
160
161         # Print all vlan devices.
162         local vlans=$(device_get_vlans ${device})
163         if [ -n "${vlans}" ]; then
164                 cli_headline 2 "VLAN devices"
165
166                 local vlan
167                 for vlan in ${vlans}; do
168                         cli_print 2 "* %-6s - %s" "${vlan}" "$(device_get_address ${vlan})"
169                 done
170                 cli_space
171         fi
172
173         # Reset the logging level.
174         LOG_DISABLE_STDOUT=${log_disable_stdout}
175 }
176
177 function cli_device_status_serial() {
178         local device=${1}
179         assert device_is_serial ${device}
180
181         serial_is_locked ${device} &>/dev/null
182         local locked=$?
183
184         cli_print_fmt1 1 "Locked" "$(cli_print_bool ${locked})"
185         cli_space
186
187         # Cannot go on when the device is locked.
188         [ ${locked} -eq ${EXIT_TRUE} ] && return ${EXIT_OK}
189
190         cli_print_fmt1 1 "Manufacturer" \
191                 "$(modem_get_manufacturer ${device})"
192         cli_print_fmt1 1 "Model" \
193                 "$(modem_get_model ${device})"
194         cli_print_fmt1 1 "Software version" \
195                 "$(modem_get_software_version ${device})"
196
197         if modem_is_mobile ${device}; then
198                 cli_print_fmt1 1 "IMEI" \
199                         "$(modem_get_device_imei ${device})"
200                 cli_space
201
202                 cli_headline 2 "Network status"
203                 modem_sim_status ${device} &>/dev/null
204                 local sim_status_code=$?
205
206                 local sim_status="unknown"
207                 case "${sim_status_code}" in
208                         ${EXIT_SIM_READY})
209                                 sim_status="SIM ready"
210                                 ;;
211                         ${EXIT_SIM_PIN})
212                                 sim_status="PIN locked"
213                                 ;;
214                         ${EXIT_SIM_PUK})
215                                 sim_status="PUK locked"
216                                 ;;
217                 esac
218                 cli_print_fmt1 2 "SIM status" "${sim_status}"
219
220                 if [ ${sim_status_code} -eq ${EXIT_SIM_READY} ]; then
221                         cli_print_fmt1 2 "IMSI" \
222                                 "$(modem_get_sim_imsi ${device})"
223                         cli_print_fmt1 2 "Operator" \
224                                 "$(modem_get_network_operator ${device})"
225                         cli_print_fmt1 2 "Mode" \
226                                 "$(modem_get_network_mode ${device})"
227                         cli_print_fmt1 2 "Signal quality" \
228                                 "$(modem_get_signal_quality ${device}) dBm"
229
230                         local ber=$(modem_get_bit_error_rate ${device})
231                         isset ber || ber="unknown"
232                         cli_print_fmt1 2 "Bit Error Rate" "${ber}"
233                 fi
234         fi
235         cli_space
236 }
237
238 function cli_device_discover() {
239         local device=${1}
240         shift
241
242         # This can only be executed for ethernet (or compatible) devices
243         if ! device_is_ethernet_compatible "${device}"; then
244                 return ${EXIT_OK}
245         fi
246
247         local raw
248
249         while [ $# -gt 0 ]; do
250                 case "${1}" in
251                         --raw)
252                                 raw=1
253                                 ;;
254                 esac
255                 shift
256         done
257
258         local up
259         device_is_up ${device} && up=1
260         device_set_up ${device}
261
262         enabled raw || echo "${device}"
263
264         local hook
265         local out
266         local ret
267         for hook in $(hook_zone_get_all); do
268                 out=$(hook_zone_exec ${hook} discover ${device})
269                 ret=$?
270
271                 [ ${ret} -eq ${DISCOVER_NOT_SUPPORTED} ] && continue
272
273                 if enabled raw; then
274                         case "${ret}" in
275                                 ${DISCOVER_OK})
276                                         echo "${hook}: OK"
277                                         local line
278                                         while read line; do
279                                                 echo "${hook}: ${line}"
280                                         done <<<"${out}"
281                                         ;;
282
283                                 ${DISCOVER_ERROR})
284                                         echo "${hook}: FAILED"
285                                         ;;
286                         esac
287                 else
288                         case "${ret}" in
289                                 ${DISCOVER_OK})
290                                         echo "  ${hook} was successful."
291                                         local line
292                                         while read line; do
293                                                 echo "  ${line}"
294                                         done <<<"${out}"
295                                         ;;
296
297                                 ${DISCOVER_ERROR})
298                                         echo "  ${hook} failed."
299                                         ;;
300                         esac
301                 fi
302         done
303
304         echo # New line
305
306         [ "${up}" = "1" ] || device_set_down ${device}
307 }
308
309 function cli_device_serial_unlock() {
310         if cli_help_requested $@; then
311                 cli_show_man network-device
312                 exit ${EXIT_OK}
313         fi
314
315         local device=${1}
316         assert isset device
317
318         if ! device_is_serial ${device}; then
319                 error "${device} is not a serial device."
320                 error "Unlocking is only supported for serial devices."
321                 exit ${EXIT_ERROR}
322         fi
323
324         # Read the current state of the SIM card.
325         modem_sim_status ${device} &>/dev/null
326         local sim_status_code=$?
327
328         # If the SIM card is already unlocked, we don't need to do anything.
329         if [ ${sim_status_code} -eq ${EXIT_SIM_READY} ]; then
330                 print "The SIM card is already unlocked."
331                 exit ${EXIT_OK}
332
333         # If the SIM card is in an unknown state, we cannot do anything.
334         elif [ ${sim_status_code} -eq ${EXIT_SIM_UNKNOWN} ]; then
335                 error "The SIM card is in an unknown state."
336                 exit ${EXIT_ERROR}
337         fi
338
339         # Ask for the code.
340         local code=${2}
341         local require_new_pin="false"
342         local new_pin
343
344         while ! isinteger code; do
345                 local message
346                 case "${sim_status_code}" in
347                         ${EXIT_SIM_PIN})
348                                 message="Please enter PIN:"
349                                 ;;
350                         ${EXIT_SIM_PUK})
351                                 message="Please enter PUK:"
352                                 require_new_pin="true"
353                                 ;;
354                 esac
355                 assert isset message
356
357                 echo -n "${message} "
358                 read -s code
359                 echo # Print newline.
360
361                 if enabled require_new_pin; then
362                         local i new_pin2
363                         for i in 0 1; do
364                                 case "${i}" in
365                                         0)
366                                                 message="Please enter a new PIN code:"
367                                                 ;;
368                                         1)
369                                                 message="Please confirm the new PIN code:"
370                                                 ;;
371                                 esac
372
373                                 echo -n "${message} "
374                                 read -s new_pin2
375                                 echo # Print newline.
376
377                                 if [ -n "${new_pin}" ]; then
378                                         if [ "${new_pin}" != "${new_pin2}" ]; then
379                                                 error "The entered PIN codes did not match."
380                                                 exit ${EXIT_ERROR}
381                                         fi
382                                 else
383                                         new_pin=${new_pin2}
384                                 fi
385                         done
386                 fi
387         done
388
389         # Trying to unlock the SIM card.
390         modem_sim_unlock ${device} ${code} ${new_pin}
391
392         exit $?
393 }
394
395 function cli_device_send_ussd_command() {
396         local device="${1}"
397         assert isset device
398         shift
399
400         local command
401         local timeout
402
403         while [ $# -gt 0 ]; do
404                 case "${1}" in
405                         --timeout=*)
406                                 timeout="$(cli_get_val "${1}")"
407                                 ;;
408                         *)
409                                 if isset command; then
410                                         warning "Unrecognized argument: ${1}"
411                                 else
412                                         command="${1}"
413                                 fi
414                                 ;;
415                 esac
416                 shift
417         done
418
419         assert device_is_serial "${device}"
420
421         local args
422         if isset timeout; then
423                 args="${args} --timeout=${timeout}"
424         fi
425
426         modem_ussd_send_command "${device}" "${command}" ${args}
427         exit $?
428 }
429
430 function cli_device_monitor() {
431         local device="${1}"
432         assert isset device
433
434         if ! device_is_wireless "${device}"; then
435                 error "This action only works with wireless devices. Exiting."
436                 exit ${EXIT_ERROR}
437         fi
438
439         wireless_monitor "${device}"
440         exit $?
441 }
442
443 function cli_hostname() {
444         if cli_help_requested $@; then
445                 cli_show_man network
446                 exit ${EXIT_OK}
447         fi
448
449         local hostname=${1}
450
451         if [ -n "${hostname}" ]; then
452                 config_hostname ${hostname}
453                 log INFO "Hostname was set to '${hostname}'."
454                 log INFO "Changes do only take affect after reboot."
455                 exit ${EXIT_OK}
456         fi
457
458         echo "$(config_hostname)"
459         exit ${EXIT_OK}
460 }
461
462 function cli_port() {
463         if cli_help_requested $@; then
464                 cli_show_man network-port
465                 exit ${EXIT_OK}
466         fi
467
468         local action
469         local port
470
471         if port_exists ${1}; then
472                 port=${1}
473                 action=${2}
474                 shift 2
475
476                 case "${action}" in
477                         edit|create|remove|up|down|status)
478                                 port_${action} "${port}" $@
479                                 ;;
480                         *)
481                                 error "Unrecognized argument: ${action}"
482                                 exit ${EXIT_ERROR}
483                                 ;;
484                 esac
485         else
486                 action=${1}
487                 shift
488
489                 case "${action}" in
490                         new|destroy)
491                                 port_${action} $@
492                                 ;;
493                         *)
494                                 error "Unrecognized argument: ${action}"
495                                 exit ${EXIT_ERROR}
496                                 ;;
497                 esac
498         fi
499 }
500
501 function cli_zone() {
502         if cli_help_requested $@; then
503                 cli_show_man network-zone
504                 exit ${EXIT_OK}
505         fi
506
507         local action
508         local zone
509
510         if zone_name_is_valid ${1}; then
511                 zone=${1}
512                 action=${2}
513                 shift 2
514
515                 # Action aliases
516                 case "${action}" in
517                         start|reload)
518                                 action="up"
519                                 ;;
520                         stop)
521                                 action="down"
522                                 ;;
523                         show)
524                                 action="status"
525                                 ;;
526                 esac
527
528                 case "${action}" in
529                         config|disable|down|edit|enable|port|status|up)
530                                 zone_${action} ${zone} $@
531                                 ;;
532                         *)
533                                 error "Unrecognized argument: ${action}"
534                                 cli_show_man network-zone
535                                 exit ${EXIT_ERROR}
536                                 ;;
537                 esac
538         else
539                 action=${1}
540                 shift
541
542                 case "${action}" in
543                         create)
544                                 zone_${action} $@
545                                 ;;
546                         remove)
547                                 cli_zone_remove $@
548                                 ;;
549                         list-hooks)
550                                 cli_list_hooks zone $@
551                                 ;;
552                         ""|*)
553                                 if [ -n "${action}" ]; then
554                                         error "Unrecognized argument: '${action}'"
555                                         echo
556                                 fi
557
558                                 cli_show_man network-zone
559                                 exit ${EXIT_ERROR}
560                                 ;;
561                 esac
562         fi
563 }
564
565 # Removes a zone either immediately, if it is currently down,
566 # or adds a tag that the removal will be done when the zone
567 # is brought down the next time.
568 function cli_zone_remove() {
569         if cli_help_requested $@; then
570                 cli_show_man network-zone
571                 exit ${EXIT_OK}
572         fi
573
574         local zone=${1}
575         assert zone_exists ${zone}
576
577         if zone_is_up ${zone}; then
578                 echo "Zone '${zone}' is up and will be removed when it goes down the next time."
579                 zone_remove ${zone}
580         else
581                 echo "Removing zone '${zone}' now..."
582                 zone_remove_now ${zone}
583         fi
584
585         exit ${EXIT_OK}
586 }
587
588 function cli_list_hooks() {
589         local type=${1}
590         shift
591
592         if cli_help_requested $@; then
593                 cli_show_man network-zone
594                 exit ${EXIT_OK}
595         fi
596
597         local hook_dir=$(hook_dir ${type})
598         local hook
599
600         for hook in ${hook_dir}/*; do
601                 hook=$(basename ${hook})
602                 if hook_exists ${type} ${hook}; then
603                         echo "${hook}"
604                 fi
605         done | sort -u
606 }
607
608 function cli_route() {
609         if cli_help_requested $@; then
610                 cli_show_man network-route
611                 exit ${EXIT_OK}
612         fi
613
614         local action=${1}
615         shift
616
617         case "${action}" in
618                 # Add a new route.
619                 add)
620                         route_add $@
621                         ;;
622                 # Remove an existing route.
623                 remove)
624                         route_remove $@
625                         ;;
626                 # List all routes.
627                 list)
628                         route_list $@
629                         return ${EXIT_OK}
630                         ;;
631                 *)
632                         error "Unrecognized action: ${action}"
633                         cli_run_help network route
634
635                         exit ${EXIT_ERROR}
636                         ;;
637         esac
638
639         # Applying all routes.
640         route_apply
641
642         exit ${EXIT_OK}
643 }
644
645 function cli_dhcpd() {
646         local proto=${1}
647         shift
648
649         if cli_help_requested $@; then
650                 cli_show_man network-dhcp
651                 exit ${EXIT_OK}
652         fi
653
654         local action=${1}
655         shift
656
657         case "${action}" in
658                 edit)
659                         dhcpd_edit ${proto} $@
660                         ;;
661                 start)
662                         dhcpd_start ${proto}
663                         ;;
664                 stop)
665                         dhcpd_stop ${proto}
666                         ;;
667                 restart|reload)
668                         dhcpd_reload ${proto}
669                         ;;
670                 subnet)
671                         cli_dhcpd_subnet ${proto} $@
672                         ;;
673                 show|"")
674                         cli_dhcpd_show ${proto} $@
675                         ;;
676                 *)
677                         error "Unrecognized action: ${action}"
678                         cli_run_help network dhcpvN
679
680                         exit ${EXIT_ERROR}
681                         ;;
682         esac
683
684         exit ${EXIT_OK}
685 }
686
687 function cli_dhcpd_show() {
688         local proto=${1}
689         assert isset proto
690
691         local settings=$(dhcpd_settings ${proto})
692         assert isset settings
693
694         local ${settings}
695         dhcpd_global_settings_read ${proto}
696
697         cli_headline 1 "Dynamic Host Configuration Protocol Daemon for ${proto/ip/IP}"
698
699         case "${proto}" in
700                 ipv6)
701                         cli_headline 2 "Lease times"
702                         if isinteger VALID_LIFETIME; then
703                                 cli_print_fmt1 2 "Valid lifetime" "${VALID_LIFETIME}s"
704                         fi
705
706                         if isinteger PREFERRED_LIFETIME; then
707                                 cli_print_fmt1 2 "Preferred lifetime" "${PREFERRED_LIFETIME}s"
708                         fi
709
710                         cli_space
711                         ;;
712                 ipv4)
713                         cli_print_fmt1 1 "Authoritative" $(cli_print_enabled AUTHORITATIVE)
714                         cli_space
715
716                         cli_headline 2 "Lease times"
717                         cli_print_fmt1 2 "Default lease time" "${DEFAULT_LEASE_TIME}s"
718                         cli_print_fmt1 2 "Max. lease time" "${MAX_LEASE_TIME}s"
719
720                         if isset MIN_LEASE_TIME; then
721                                 cli_print_fmt1 2 "Min. lease time" "${MIN_LEASE_TIME}s"
722                         fi
723
724                         cli_space
725                         ;;
726         esac
727
728         # Read the options.
729         local -A options
730         dhcpd_global_options_read ${proto} ${subnet_id}
731
732         # Print the options if any.
733         if [ ${#options[*]} -gt 0 ]; then
734                 cli_headline 2 "Options"
735
736                 local option
737                 for option in $(dhcpd_options ${proto}); do
738                         [ -n "${options[${option}]}" ] || continue
739
740                         cli_print_fmt1 2 \
741                                 "${option}" "${options[${option}]}"
742                 done
743                 cli_space
744         fi
745
746         # Subnets.
747         local subnets=$(dhcpd_subnet_list ${proto})
748         if [ -n "${subnets}" ]; then
749                 cli_headline 2 "Subnets"
750                 local subnet_id
751                 for subnet_id in ${subnets}; do
752                         cli_dhcpd_subnet_show ${proto} ${subnet_id} 2
753                 done
754         fi
755 }
756
757 function cli_dhcpd_subnet() {
758         local proto=${1}
759         shift
760
761         if cli_help_requested $@; then
762                 cli_show_man network-dhcp-subnet
763                 exit ${EXIT_OK}
764         fi
765
766         local action=${1}
767         shift
768
769         case "${action}" in
770                 new)
771                         dhcpd_subnet_new ${proto} $@
772                         ;;
773                 remove)
774                         dhcpd_subnet_remove ${proto} $@
775                         ;;
776                 [0-9]*)
777                         local subnet_id=${action}
778
779                         if ! dhcpd_subnet_exists ${proto} ${subnet_id}; then
780                                 error "The given subnet with ID ${subnet_id} does not exist."
781                                 return ${EXIT_ERROR}
782                         fi
783
784                         # Update the action.
785                         action=${1}
786                         shift
787
788                         case "${action}" in
789                                 edit)
790                                         dhcpd_subnet_edit ${proto} ${subnet_id} $@
791                                         local ret=$?
792
793                                         if [ ${ret} -eq ${EXIT_OK} ]; then
794                                                 dhcpd_reload ${proto}
795                                         fi
796                                         exit ${ret}
797                                         ;;
798                                 range)
799                                         cli_dhcpd_subnet_range ${proto} ${subnet_id} $@
800                                         exit $?
801                                         ;;
802                                 show)
803                                         cli_dhcpd_subnet_show ${proto} ${subnet_id} $@
804                                         exit $?
805                                         ;;
806                                 options)
807                                         cli_dhcpd_subnet_options ${proto} ${subnet_id} $@
808                                         exit $?
809                                         ;;
810                                 *)
811                                         error "Unrecognized action: ${action}"
812                                         cli_run_help network dhcpvN subnet
813                                         exit ${EXIT_ERROR}
814                                         ;;
815                         esac
816                         ;;
817                 show)
818                         local subnet_id
819                         for subnet_id in $(dhcpd_subnet_list ${proto}); do
820                                 cli_dhcpd_subnet_show ${proto} ${subnet_id}
821                         done
822                         ;;
823                 *)
824                         error "Unrecognized action: ${action}"
825                         cli_run_help network dhcpvN subnet
826
827                         exit ${EXIT_ERROR}
828                         ;;
829         esac
830
831         exit ${EXIT_OK}
832 }
833
834 function cli_dhcpd_subnet_range() {
835         local proto=${1}
836         assert isset proto
837         shift
838
839         local subnet_id=${1}
840         assert isset subnet_id
841         shift
842
843         local action=${1}
844         shift
845
846         case "${action}" in
847                 new)
848                         dhcpd_subnet_range_new ${proto} ${subnet_id} $@
849                         exit $?
850                         ;;
851                 remove)
852                         dhcpd_subnet_range_remove ${proto} ${subnet_id} $@
853                         exit $?
854                         ;;
855                 *)
856                         error "Unrecognized action: ${action}"
857                         cli_run_help network dhcpvN subnet range
858                         exit ${EXIT_ERROR}
859                         ;;
860         esac
861 }
862
863 function cli_dhcpd_subnet_show() {
864         local proto=${1}
865         assert isset proto
866
867         local subnet_id=${2}
868         assert isset subnet_id
869
870         local level=${3}
871         isset level || level=0
872
873         local $(dhcpd_subnet_settings ${proto})
874
875         # Read in configuration settings.
876         dhcpd_subnet_read ${proto} ${subnet_id}
877
878         cli_headline $(( ${level} + 1 )) \
879                 "DHCP${proto/ip/} subnet declaration #${subnet_id}"
880         cli_print_fmt1 $(( ${level} + 1 )) \
881                 "Subnet" "${ADDRESS}/${PREFIX}"
882         cli_space
883
884         # Read the options.
885         local -A options
886         dhcpd_subnet_options_read ${proto} ${subnet_id}
887
888         # Print the options if any.
889         if [ ${#options[*]} -gt 0 ]; then
890                 cli_headline $(( ${level} + 2 )) "Options"
891
892                 local option
893                 for option in $(dhcpd_subnet_options ${proto}); do
894                         [ -n "${options[${option}]}" ] || continue
895
896                         cli_print_fmt1 $(( ${level} + 2 )) \
897                                 "${option}" "${options[${option}]}"
898                 done
899                 cli_space
900         fi
901
902         # Ranges.
903         cli_headline $(( ${level} + 2 )) "Ranges"
904
905         local ranges=$(dhcpd_subnet_range_list ${proto} ${subnet_id})
906         if isset ranges; then
907                 local range_id $(dhcpd_subnet_range_settings ${proto})
908                 for range_id in ${ranges}; do
909                         dhcpd_subnet_range_read ${proto} ${subnet_id} ${range_id}
910
911                         cli_print $(( ${level} + 2 )) \
912                                 "#%d: %s - %s" ${range_id} ${START} ${END}
913                 done
914         else
915                 cli_print $(( ${level} + 2 )) "No ranges have been defined."
916         fi
917
918         cli_space
919 }
920
921 function cli_dhcpd_options() {
922         local proto=${1}
923         assert isset proto
924         shift
925
926         local subnet_id=${1}
927         assert isset subnet_id
928         shift
929
930         local valid_options=$(dhcpd_subnet_options ${proto})
931
932         local key val
933         while [ $# -gt 0 ]; do
934                 case "${1}" in
935                         *=*)
936                                 key=$(cli_get_key ${1})
937                                 val=$(cli_get_val ${1})
938
939                                 dhcpd_subnet_option_set ${proto} ${subnet_id} ${key} ${val}
940                 esac
941         done
942 }
943
944 function cli_start() {
945         if cli_help_requested $@; then
946                 cli_show_man network
947                 exit ${EXIT_OK}
948         fi
949
950         local zones=$(zones_get $@)
951
952         local zone
953         for zone in ${zones}; do
954                 zone_start ${zone} &
955         done
956
957         wait # until everything is settled
958 }
959
960 function cli_stop() {
961         if cli_help_requested $@; then
962                 cli_show_man network
963                 exit ${EXIT_OK}
964         fi
965
966         local zones=$(zones_get $@)
967
968         local zone
969         for zone in ${zones}; do
970                 zone_stop ${zone} &
971         done
972
973         wait # until everything is settled
974 }
975
976 function cli_restart() {
977         if cli_help_requested $@; then
978                 cli_show_man network
979                 exit ${EXIT_OK}
980         fi
981
982         cli_stop $@
983
984         # Give the system some time to calm down
985         sleep ${TIMEOUT_RESTART}
986
987         cli_start $@
988 }
989
990 function cli_status() {
991         if cli_help_requested $@; then
992                 cli_show_man network
993                 exit ${EXIT_OK}
994         fi
995
996         # When dumping status information, the debug
997         # mode clutters the console which is not what we want.
998         # Logging on the console is disabled for a short time.
999         local log_disable_stdout=${LOG_DISABLE_STDOUT}
1000         LOG_DISABLE_STDOUT="true"
1001
1002         local zones=$(zones_get $@)
1003
1004         local zone
1005         for zone in ${zones}; do
1006                 zone_status ${zone}
1007         done
1008
1009         # Reset logging.
1010         LOG_DISABLE_STDOUT=${log_disable_stdout}
1011 }
1012
1013 function cli_reset() {
1014         if cli_help_requested $@; then
1015                 cli_show_man network
1016                 exit ${EXIT_OK}
1017         fi
1018
1019         warning_log "Will reset the whole network configuration!!!"
1020
1021         # Force mode is disabled by default
1022         local force=0
1023
1024         while [ $# -gt 0 ]; do
1025                 case "${1}" in
1026                         --force|-f)
1027                                 force=1
1028                                 ;;
1029                 esac
1030                 shift
1031         done
1032
1033         # If we are not running in force mode, we ask the user if he does know
1034         # what he is doing.
1035         if ! enabled force; then
1036                 if ! cli_yesno "Do you really want to reset the whole network configuration?"; then
1037                         exit ${EXIT_ERROR}
1038                 fi
1039         fi
1040
1041         local zone
1042         for zone in $(zones_get --all); do
1043                 zone_remove ${zone}
1044         done
1045
1046         local port
1047         for port in $(ports_get --all); do
1048                 port_destroy "${port}"
1049         done
1050
1051         # Flush all DNS servers.
1052         dns_server_flush
1053
1054         # Re-run the initialization functions
1055         init_run
1056
1057         exit ${EXIT_OK}
1058 }
1059
1060 # Help function: will show the default man page to the user.
1061 # Optionally, there are two arguments taken, the type of hook
1062 # and which hook should be shown.
1063 function cli_help() {
1064         local type=${1}
1065         local what=${2}
1066
1067         # Remove unknown types.
1068         if ! listmatch ${type} zone port config; then
1069                 type=""
1070         fi
1071
1072         # If no arguments were given, we will show the default page.
1073         if [ -z "${type}" ]; then
1074                 cli_show_man network
1075                 return ${EXIT_OK}
1076         fi
1077
1078         if ! hook_exists ${type} ${what}; then
1079                 error "Hook of type '${type}' and name '${what}' could not be found."
1080                 exit "${EXIT_ERROR}"
1081         fi
1082
1083         hook_exec ${type} ${what} help
1084 }
1085
1086 function cli_dns_server() {
1087         if cli_help_requested $@; then
1088                 cli_show_man network-dns-server
1089                 exit ${EXIT_OK}
1090         fi
1091
1092         # Get the command.
1093         local cmd=${1}; shift
1094         if [ -z "${cmd}" ]; then
1095                 cli_show_man network-dns-server
1096                 exit ${EXIT_ERROR}
1097         fi
1098
1099         # Get the new server to process (if any).
1100         local server=${1}
1101         local priority=${2}
1102
1103         case "${cmd}" in
1104                 list)
1105                         dns_server_list
1106                         exit ${EXIT_OK}
1107                         ;;
1108                 add)
1109                         if dns_server_exists ${server}; then
1110                                 error "DNS server '${server}' already exists!"
1111                                 exit ${EXIT_ERROR}
1112                         fi
1113
1114                         log INFO "Adding new DNS server: ${server}"
1115                         dns_server_add ${server} ${priority}
1116                         ;;
1117                 remove)
1118                         if ! dns_server_exists ${server}; then
1119                                 error "DNS server '${server}' does not exist!"
1120                                 exit ${EXIT_ERROR}
1121                         fi
1122
1123                         log INFO "Removing DNS server: ${server}"
1124                         dns_server_remove ${server} ${priority}
1125                         ;;
1126                 update)
1127                         # Just run the update afterwards.
1128                         ;;
1129                 *)
1130                         error "No such command: ${cmd}"
1131                         exit ${EXIT_ERROR}
1132         esac
1133
1134         # Update the local DNS configuration after changes have been made.
1135         dns_generate_resolvconf
1136         radvd_update
1137
1138         exit ${EXIT_OK}
1139 }
1140
1141 # Process the given action
1142 case "${action}" in
1143         init)
1144                 init_run
1145                 ;;
1146
1147         settings|hostname|port|device|zone|start|stop|restart|status|reset|route)
1148                 cli_${action} $@
1149                 ;;
1150
1151         # DHCP server configuration (automatically detects which protocol to use).
1152         dhcpv6|dhcpv4)
1153                 cli_dhcpd ${action/dhcp/ip} $@
1154                 ;;
1155
1156         # DNS server configuration.
1157         dns-server)
1158                 cli_dns_server $@
1159                 ;;
1160
1161         ""|help|--help|-h)
1162                 cli_help $@
1163                 ;;
1164
1165         *)
1166                 error "Invalid command given: ${action}"
1167                 cli_usage "network help"
1168                 exit ${EXIT_CONF_ERROR}
1169                 ;;
1170 esac
1171
1172 exit ${EXIT_OK}