8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / svc / shell / ipf_include.sh
blob5082f2a9e00c8432ca29e89caf24d5f6e436e2fc
1 #!/sbin/sh
3 # CDDL HEADER START
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
20 # CDDL HEADER END
22 # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 # Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
26 IPFILTER_FMRI="svc:/network/ipfilter:default"
27 ETC_IPF_DIR=/etc/ipf
28 IPNATCONF=`/usr/bin/svcprop -p config/ipnat_config_file $IPFILTER_FMRI \
29 2>/dev/null`
30 if [ $? -eq 1 ]; then
31 IPNATCONF=$ETC_IPF_DIR/ipnat.conf
33 IPPOOLCONF=`/usr/bin/svcprop -p config/ippool_config_file $IPFILTER_FMRI \
34 2>/dev/null`
35 if [ $? -eq 1 ]; then
36 IPPOOLCONF=$ETC_IPF_DIR/ippool.conf
38 VAR_IPF_DIR=/var/run/ipf
39 IPFILCONF=$VAR_IPF_DIR/ipf.conf
40 IP6FILCONF=$VAR_IPF_DIR/ipf6.conf
41 IPFILOVRCONF=$VAR_IPF_DIR/ipf_ovr.conf
42 IP6FILOVRCONF=$VAR_IPF_DIR/ipf6_ovr.conf
43 IPF_LOCK=/var/run/ipflock
44 CONF_FILES=""
45 CONF6_FILES=""
46 NAT_FILES=""
47 IPF_SUFFIX=".ipf"
48 IPF6_SUFFIX=".ipf6"
49 NAT_SUFFIX=".nat"
51 # version for configuration upgrades
52 CURRENT_VERSION=1
54 IPF_FMRI="svc:/network/ipfilter:default"
55 INETDFMRI="svc:/network/inetd:default"
56 RPCBINDFMRI="svc:/network/rpc/bind:default"
58 SMF_ONLINE="online"
59 SMF_MAINT="maintenance"
60 SMF_NONE="none"
62 FW_CONTEXT_PG="firewall_context"
63 METHOD_PROP="ipf_method"
65 FW_CONFIG_PG="firewall_config"
66 POLICY_PROP="policy"
67 APPLY2_PROP="apply_to"
68 APPLY2_6_PROP="apply_to_6"
69 EXCEPTIONS_PROP="exceptions"
70 EXCEPTIONS_6_PROP="exceptions_6"
71 TARGET_PROP="target"
72 TARGET_6_PROP="target_6"
73 BLOCKPOL_PROP="block_policy"
75 FW_CONFIG_DEF_PG="firewall_config_default"
76 FW_CONFIG_OVR_PG="firewall_config_override"
77 CUSTOM_FILE_PROP="custom_policy_file"
78 CUSTOM_FILE_6_PROP="custom_policy_file_6"
79 OPEN_PORTS_PROP="open_ports"
81 PREFIX_HOST="host:"
82 PREFIX_NET="network:"
83 PREFIX_POOL="pool:"
84 PREFIX_IF="if:"
86 GLOBAL_CONFIG=""
87 GLOBAL_POLICY=""
88 GLOBAL_BLOCK_POLICY=""
90 SERVINFO=/usr/lib/servinfo
93 # Get value(s) for given property from either firewall_config_default or
94 # firewall_config_override property groups.
96 # global_get_prop_value pg_name propname
97 # pg_name - FW_CONFIG_DEF_PG or FW_CONFIG_OVR_PG
98 # propname - property name
100 global_get_prop_value()
102 target_pg=$1
103 prop=$2
105 [ "$1" != $FW_CONFIG_OVR_PG -a "$1" != $FW_CONFIG_DEF_PG ] && return
107 [ "$1" == $FW_CONFIG_DEF_PG ] && extra_pg=$FW_CONFIG_OVR_PG || \
108 extra_pg=$FW_CONFIG_DEF_PG
110 value=`echo $GLOBAL_CONFIG | awk '{
111 found=0
112 for (i=1; i<=NF; i++) {
113 if (found == 1) {
114 if (index($i, target_pg) == 1 || index($i, extra_pg) == 1)
115 break;
117 print $i;
120 if (split($i, values, "/") < 2)
121 continue;
123 if (values[1] == target_pg && values[2] == prop)
124 found=1;
126 }' target_pg=$target_pg prop=$prop extra_pg=$extra_pg`
128 # Return
129 echo "$value"
133 # Initialize and cache network/ipfilter configuration, global configuration.
135 # Since an SMF service configuration may get updated during the execution of the
136 # service method, it's best to read all relevant configuration via one svcprop
137 # invocation and cache it for later use.
139 # This function reads and stores relevant configuration into GLOBAL_CONFIG and
140 # initializes the GLOBAL_POLICY and GLOBAL_BLOCK_POLICY variables. GLOBAL_CONFIG
141 # is a string containing pg/prop and their corresponding values (i.e. svcprop -p
142 # pg fmri output). To get values for a certain pg/prop, use
143 # global_get_prop_value().
145 global_init()
147 GLOBAL_CONFIG=`svcprop -p ${FW_CONFIG_OVR_PG} -p ${FW_CONFIG_DEF_PG} \
148 $IPF_FMRI 2>/dev/null | awk '{$2=" "; print $0}'`
150 GLOBAL_POLICY=`global_get_prop_value $FW_CONFIG_DEF_PG $POLICY_PROP`
151 GLOBAL_BLOCK_POLICY=`global_get_prop_value $FW_CONFIG_DEF_PG \
152 $BLOCKPOL_PROP`
156 # Given a service, gets its config pg name
158 get_config_pg()
160 if [ "$1" = "$IPF_FMRI" ]; then
161 echo "$FW_CONFIG_DEF_PG"
162 else
163 echo "$FW_CONFIG_PG"
165 return 0
169 # Given a service, gets its firewall policy
171 get_policy()
173 config_pg=`get_config_pg $1`
174 svcprop -p $config_pg/${POLICY_PROP} $1 2>/dev/null
178 # block policy can be set to "return", which will expand into
179 # separate block rules for tcp (block return-rst ...) and all other
180 # protocols (block return-icmp-as-dest ...)
182 get_block_policy()
184 config_pg=`get_config_pg $1`
185 svcprop -p $config_pg/${BLOCKPOL_PROP} $1 2>/dev/null
189 # Given a service, gets its source address exceptions for IPv4
191 get_exceptions()
193 config_pg=`get_config_pg $1`
194 exceptions=`svcprop -p $config_pg/${EXCEPTIONS_PROP} $1 2>/dev/null`
195 echo $exceptions | sed -e 's/\\//g'
199 # Given a service, gets its source address exceptions for IPv6
201 get_exceptions_6()
203 config_pg=`get_config_pg $1`
204 exceptions6=`svcprop -p $config_pg/${EXCEPTIONS_6_PROP} $1 2>/dev/null`
205 echo $exceptions6 | sed -e 's/\\//g'
209 # Given a service, gets its firewalled source addresses for IPv4
211 get_apply2_list()
213 config_pg=`get_config_pg $1`
214 apply2=`svcprop -p $config_pg/${APPLY2_PROP} $1 2>/dev/null`
215 echo $apply2 | sed -e 's/\\//g'
219 # Given a service, gets its firewalled source addresses for IPv6
221 get_apply2_6_list()
223 config_pg=`get_config_pg $1`
224 apply2_6=`svcprop -p $config_pg/${APPLY2_6_PROP} $1 2>/dev/null`
225 echo $apply2_6 | sed -e 's/\\//g'
229 # Given a service, gets its firewalled target addresses for IPv4
231 get_target_list()
233 config_pg=`get_config_pg $1`
234 target=`svcprop -p $config_pg/${TARGET_PROP} $1 2>/dev/null`
235 [ -z "$target" -o "$target" = '""' ] && target=any
236 echo $target | sed -e 's/\\//g'
240 # Given a service, gets its firewalled target addresses for IPv6
242 get_target_6_list()
244 config_pg=`get_config_pg $1`
245 target6=`svcprop -p $config_pg/${TARGET_6_PROP} $1 2>/dev/null`
246 [ -z "$target6" -o "$target6" = '""' ] && target6=any
247 echo $target6 | sed -e 's/\\//g'
250 check_ipf_dir()
252 [ -d $VAR_IPF_DIR ] && return 0
253 mkdir $VAR_IPF_DIR >/dev/null 2>&1 || return 1
257 # fmri_to_file fmri suffix
259 fmri_to_file()
261 check_ipf_dir || return 1
262 fprefix="${VAR_IPF_DIR}/`echo $1 | tr -s '/:' '__'`"
263 echo "${fprefix}${2}"
267 # Return service's enabled property
269 service_is_enabled()
272 # Temporary enabled state overrides the persistent state
273 # so check it first.
275 enabled_ovr=`svcprop -c -p general_ovr/enabled $1 2>/dev/null`
276 if [ -n "$enabled_ovr" ]; then
277 [ "$enabled_ovr" = "true" ] && return 0 || return 1
280 enabled=`svcprop -c -p general/enabled $1 2>/dev/null`
281 [ -n "$enabled" -a "$enabled" = "true" ] && return 0 || return 1
285 # Return whether service is desired state
287 # Args: fmri state
288 # Return:
289 # 0 - desired state is service's current state
290 # 1 - desired state is not service's current state
292 service_check_state()
295 # Make sure we're done with ongoing state transition
297 while [ "`svcprop -p restarter/next_state $1`" != "$SMF_NONE" ]; do
298 sleep 1
299 done
301 [ "`svcprop -p restarter/state $1`" = "$2" ] && return 0 || return 1
305 # Deny/Allow list stores values in the form "host:addr", "network:addr/netmask",
306 # "pool:number", and "if:interface". This function returns the
307 # IP(addr or addr/netmask) value or a pool number.
309 get_IP()
311 value_is_interface $1 && return 1
312 echo "$1" | sed -n -e "s,^${PREFIX_POOL}\(.*\),pool/\1,p" \
313 -e "s,^${PREFIX_HOST}\(.*\),\1,p" \
314 -e "s,^${PREFIX_NET}\(.*\),\1,p" \
315 -e "s,^any,any,p"
318 get_interface()
320 value_is_interface $1 || return 1
321 scratch=`echo "$1" | sed -e "s/^${PREFIX_IF}//"`
323 ifconfig $scratch >/dev/null 2>&1 || return 1
324 echo $scratch | sed -e 's/:.*//'
330 value_is_interface()
332 [ -z "$1" ] && return 1
333 echo $1 | grep "^${PREFIX_IF}" >/dev/null 2>&1
337 # Remove rules in given file from active list without restarting ipfilter
339 remove_rules()
341 [ -f "$1" ] && ipf $2 -r -f $1 >/dev/null 2>&1
344 remove_nat_rules()
346 [ -f "$1" ] && ipnat -r -f $1 >/dev/null 2>&1
349 check_ipf_syntax()
351 ipf $2 -n -f $1 >/dev/null 2>&1
354 check_nat_syntax()
356 ipnat -n -f $1 >/dev/null 2>&1
359 unique_ports()
361 echo $* | xargs -n 1 echo | sort -u
364 file_get_ports()
366 ipf $2 -n -v -f $1 2>/dev/null | sed -n -e \
367 's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \
368 awk '{if (length($0) > 1) {printf("%s ", $1)}}'
371 get_active_ports()
373 ipfstat $1 -io 2>/dev/null | sed -n -e \
374 's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \
375 awk '{if (length($0) > 1) {printf("%s ",$1)}}'
379 # Given two list of ports, return failure if there's a duplicate.
381 sets_check_duplicate()
384 # If either list is empty, there isn't any conflict.
386 [ -z "$1" -o -z "$2" ] && return 0
388 for p in $1; do
389 for ap in $2; do
390 [ "$p" = "$ap" ] && return 1
391 done
392 done
394 return 0
398 # Given a file containing ipf rules, check the syntax and verify
399 # the rules don't conflict, use same port number, with active
400 # rules (ipfstat -io output).
402 update_check_ipf_rules()
404 check_ipf_syntax $1 $2 || return 1
406 lports=`file_get_ports $1 $2`
407 lactive_ports=`get_active_ports $2`
409 sets_check_duplicate "$lports" "$lactive_ports" || return 1
412 server_port_list=""
413 server_port_list_6=""
416 # Given a file containing ipf rules, check the syntax and verify
417 # the rules don't conflict with already processed services.
419 # The list of processed services' ports are maintained in the global
420 # variables 'server_port_list' and 'server_port_list_6'.
422 check_ipf_rules()
425 check_ipf_syntax $1 $2 || return 1
427 lports=`file_get_ports $1 $2`
429 if [ "$2" = "-6" ]; then
430 sets_check_duplicate "$lports" "$server_port_list_6" || return 1
431 server_port_list_6="$server_port_list_6 $lports"
432 else
433 sets_check_duplicate "$lports" "$server_port_list" || return 1
434 server_port_list="$server_port_list $lports"
437 return 0
440 prepend_new_rules()
442 check_ipf_syntax $1 $2 && tail -r $1 | sed -e 's/^[a-z]/@0 &/' | \
443 ipf $2 -f - >/dev/null 2>&1
446 append_new_rules()
448 check_ipf_syntax $1 $2 && ipf $2 -f $1 >/dev/null 2>&1
451 append_new_nat_rules()
453 check_nat_syntax $1 && ipnat -f $1 >/dev/null 2>&1
457 # get port information from string of the form "proto:{port | port-port}"
459 tuple_get_port()
461 port_str=`echo "$1" | sed -e 's/ //g; s/\\\//g; s/.*://' 2>/dev/null`
462 [ -z "$port_str" ] && return 1
464 echo $port_str | grep "-" >/dev/null
465 if [ $? -eq 0 ]; then
466 echo $port_str | grep '^[0-9]\{1,5\}-[0-9]\{1,5\}$' >/dev/null || \
467 return 1
468 ports=`echo $port_str | ( IFS=- read a b ; \
469 [ $a \-le $b ] && echo $a $b || echo $b $a )`
471 for p in $ports; do
472 [ $p -gt 65535 ] && return 1
473 done
474 echo "$ports"
475 else
477 # port_str is a single port, verify and return it.
479 echo "$port_str" | grep '^[0-9]\{1,5\}$' >/dev/null || return 1
480 [ $port_str -gt 65535 ] && return 1
481 echo "$port_str"
486 # get proto info from string of the form "{tcp | udp}:port"
488 tuple_get_proto()
490 proto=`echo "$1" | sed -e 's/ //g; s/:.*//' 2>/dev/null`
491 [ -z "$proto" ] && return 0
493 [ "$proto" = "tcp" -o "$proto" = "udp" ] && echo $proto || return 1
494 return 0
497 ipf_get_lock()
499 newpid=$$
501 if [ -f "$IPF_LOCK/pid" ]; then
502 curpid=`cat $IPF_LOCK/pid 2>/dev/null`
503 [ "$curpid" = "$newpid" ] && return 0
506 # Clear lock if the owning process is no longer around.
508 ps -p $curpid >/dev/null 2>&1 || rm -r $IPF_LOCK >/dev/null 2>&1
512 # Grab the lock
514 while :; do
515 mkdir $IPF_LOCK 2>/dev/null && break;
516 sleep 1
517 done
518 echo $newpid > $IPF_LOCK/pid
522 # Remove lock if it's ours
524 ipf_remove_lock()
526 if [ -f "$IPF_LOCK/pid" ]; then
527 [ "`cat $IPF_LOCK/pid`" = "$$" ] && rm -r $IPF_LOCK
529 return 0
533 # Make IPFILCONF, /var/tmp/ipf/ipf.conf, a symlink to the input file argument.
535 custom_set_symlink()
538 # Nothing to do if the input file doesn't exist.
540 [ ! -f "$1" ] && return 0
542 check_ipf_dir || return 1
544 rm $IPFILCONF >/dev/null 2>&1
545 ln -s $1 $IPFILCONF >/dev/null 2>&1
549 # Make IP6FILCONF, /var/tmp/ipf/ipf6.conf, a symlink to the input file argument.
551 custom_set_symlink_6()
554 # Nothing to do if the input file doesn't exist.
556 [ ! -f "$1" ] && return 0
558 check_ipf_dir || return 1
560 rm $IP6FILCONF >/dev/null 2>&1
561 ln -s $1 $IP6FILCONF >/dev/null 2>&1
565 # New file replaces original file if they have different content
567 replace_file()
569 orig=$1
570 new=$2
573 # IPFILCONF may be a symlink, remove it if that's the case
575 if [ -L "$orig" ]; then
576 rm $orig
577 touch $orig
580 check_ipf_dir || return 1
581 mv $new $orig && return 0 || return 1
585 # Given a service, gets the following details for ipf rule:
586 # - policy
587 # - protocol
588 # - port(IANA port obtained by running servinfo)
590 process_server_svc()
592 service=$1
593 policy=`get_policy ${service}`
596 # Empties service's rules file so callers won't use existing rule if
597 # we fail here.
599 file=`fmri_to_file $service $IPF_SUFFIX`
600 file6=`fmri_to_file $service $IPF6_SUFFIX`
601 [ -z "$file" ] && return 1
602 echo "# $service" >${file}
603 echo "# $service" >${file6}
606 # Nothing to do if policy is "use_global"
608 [ "$policy" = "use_global" ] && return 0
610 restarter=`svcprop -p general/restarter $service 2>/dev/null`
611 if [ "$restarter" = "$INETDFMRI" ]; then
612 iana_name=`svcprop -p inetd/name $service 2>/dev/null`
613 isrpc=`svcprop -p inetd/isrpc $service 2>/dev/null`
614 else
615 iana_name=`svcprop -p $FW_CONTEXT_PG/name $service 2>/dev/null`
616 isrpc=`svcprop -p $FW_CONTEXT_PG/isrpc $service 2>/dev/null`
620 # Bail if iana_name isn't defined. Services with static rules
621 # like nis/client don't need to generate rules using
622 # iana name and protocol information.
624 [ -z "$iana_name" ] && return 1
627 # RPC services
629 if [ "$isrpc" = "true" ]; then
630 # The ports used for IPv6 are usually also reachable
631 # through IPv4, so generate IPv4 rules for them, too.
632 tports=`$SERVINFO -R -p -t -s $iana_name 2>/dev/null`
633 tports6=`$SERVINFO -R -p -t6 -s $iana_name 2>/dev/null`
634 if [ -n "$tports" -o -n "$tports6" ]; then
635 tports=`unique_ports $tports $tports6`
636 for tport in $tports; do
637 generate_rules $service $policy "tcp" \
638 $tport $file
639 done
642 if [ -n "$tports6" ]; then
643 for tport6 in $tports6; do
644 generate_rules $service $policy "tcp" \
645 $tport6 $file6 _6
646 done
649 uports=`$SERVINFO -R -p -u -s $iana_name 2>/dev/null`
650 uports6=`$SERVINFO -R -p -u6 -s $iana_name 2>/dev/null`
651 if [ -n "$uports" ]; then
652 uports=`unique_ports $uports $uports6`
653 for uport in $uports; do
654 generate_rules $service $policy "udp" \
655 $uport $file
656 done
659 if [ -n "$uports6" ]; then
660 for uport6 in $uports6; do
661 generate_rules $service $policy "udp" \
662 $uport6 $file6 _6
663 done
666 return 0
670 # Get the IANA port and supported protocols(tcp and udp)
672 tport=`$SERVINFO -p -t -s $iana_name 2>&1`
673 if [ $? -eq 0 -a -n "$tport" ]; then
674 generate_rules $service $policy "tcp" $tport $file
677 tport6=`$SERVINFO -p -t6 -s $iana_name 2>&1`
678 if [ $? -eq 0 -a -n "$tport6" ]; then
679 generate_rules $service $policy "tcp" $tport6 $file6 _6
682 uport=`$SERVINFO -p -u -s $iana_name 2>&1`
683 if [ $? -eq 0 -a -n "$uport" ]; then
684 generate_rules $service $policy "udp" $uport $file
687 uport6=`$SERVINFO -p -u6 -s $iana_name 2>&1`
688 if [ $? -eq 0 -a -n "$uport6" ]; then
689 generate_rules $service $policy "udp" $uport6 $file6 _6
692 return 0
696 # Given a service's name, policy, protocol and port, generate ipf rules
697 # - list of host/network/interface to apply policy
699 # A 'use_global' policy inherits the system-wided Global Default policy
700 # from network/ipfilter. For {deny | allow} policies, the rules are
701 # ordered as:
703 # - make exceptions to policy for those in "exceptions" list
704 # - apply policy to those specified in "apply_to" list
705 # - policy rule
707 generate_rules()
709 service=$1
710 mypolicy=$2
711 proto=$3
712 port=$4
713 out=$5
714 _6=$6
717 # Default mode is to inherit from global's policy
719 [ "$mypolicy" = "use_global" ] && return 0
721 tcp_opts=""
722 [ "$proto" = "tcp" ] && tcp_opts="flags S keep state keep frags"
724 block_policy=`get_block_policy $1`
725 if [ "$block_policy" = "use_global" ]; then
726 block_policy=${GLOBAL_BLOCK_POLICY}
729 if [ "$block_policy" = "return" ]; then
730 [ "$proto" = "tcp" ] && block_policy="return-rst"
731 [ "$proto" != "tcp" ] && block_policy="return-icmp-as-dest"
732 else
733 block_policy=""
736 iplist=`get_target${_6}_list $service`
739 # Allow all if policy is 'none'
741 if [ "$mypolicy" = "none" ]; then
742 for ip in $iplist; do
743 daddr=`get_IP ${ip}`
744 [ -z "$daddr" -o "$daddr" = '""' ] && continue
745 echo "pass in log quick proto ${proto} from any to ${daddr}" \
746 "port = ${port} ${tcp_opts}" >>${out}
747 done
748 return 0
752 # For now, let's concern ourselves only with incoming traffic.
754 [ "$mypolicy" = "deny" ] && { ecmd="pass"; acmd="block ${block_policy}"; }
755 [ "$mypolicy" = "allow" ] && { ecmd="block ${block_policy}"; acmd="pass"; }
757 for name in `get_exceptions${_6} $service`; do
758 [ -z "$name" -o "$name" = '""' ] && continue
760 ifc=`get_interface $name`
761 if [ $? -eq 0 -a -n "$ifc" ]; then
762 for ip in $iplist; do
763 daddr=`get_IP ${ip}`
764 [ -z "$daddr" -o "$daddr" = '""' ] && continue
765 echo "${ecmd} in log quick on ${ifc} from any to" \
766 "${daddr} port = ${port}" >>${out}
767 done
768 continue
771 saddr=`get_IP ${name}`
772 if [ $? -eq 0 -a -n "$saddr" ]; then
773 for ip in $iplist; do
774 daddr=`get_IP ${ip}`
775 [ -z "$daddr" -o "$daddr" = '""' ] && continue
776 echo "${ecmd} in log quick proto ${proto} from ${saddr}" \
777 "to ${daddr} port = ${port} ${tcp_opts}" >>${out}
778 done
780 done
782 for name in `get_apply2${_6}_list $service`; do
783 [ -z "$name" -o "$name" = '""' ] && continue
785 ifc=`get_interface $name`
786 if [ $? -eq 0 -a -n "$ifc" ]; then
787 for ip in $iplist; do
788 daddr=`get_IP ${ip}`
789 [ -z "$daddr" -o "$daddr" = '""' ] && continue
790 echo "${acmd} in log quick on ${ifc} from any to" \
791 "${daddr} port = ${port}" >>${out}
792 done
793 continue
796 saddr=`get_IP ${name}`
797 if [ $? -eq 0 -a -n "$saddr" ]; then
798 for ip in $iplist; do
799 daddr=`get_IP ${ip}`
800 [ -z "$daddr" -o "$daddr" = '""' ] && continue
801 echo "${acmd} in log quick proto ${proto} from ${saddr}" \
802 "to ${daddr} port = ${port} ${tcp_opts}" >>${out}
803 done
805 done
807 for ip in $iplist; do
808 daddr=`get_IP ${ip}`
809 [ -z "$daddr" -o "$daddr" = '""' ] && continue
810 echo "${ecmd} in log quick proto ${proto} from any to ${daddr}" \
811 "port = ${port} ${tcp_opts}" >>${out}
812 done
814 return 0
818 # Service has either IANA ports and proto or its own firewall method to
819 # generate the rules.
821 # - if service has a custom method, use it to populate its rules
822 # - if service has a firewall_config pg, use process_server_svc
824 # Argument - fmri
826 process_service()
829 # Don't process network/ipfilter
831 [ "$1" = "$IPF_FMRI" ] && return 0
833 service_check_state $1 $SMF_MAINT && return 1
835 method=`svcprop -p $FW_CONTEXT_PG/$METHOD_PROP $1 2>/dev/null | \
836 sed 's/\\\//g'`
837 if [ -n "$method" -a "$method" != '""' ]; then
838 ( exec $method $1 >/dev/null )
839 else
840 svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1 || return 1
841 process_server_svc $1 || return 1
843 return 0
847 # Generate rules for protocol/port defined in firewall_config_default/open_ports
848 # property. These are non-service programs whose network resource info are
849 # defined as "{tcp | upd}:{PORT | PORT-PORT}". Essentially, these programs need
850 # some specific local ports to be opened. For example, BitTorrent clients need to
851 # have 6881-6889 opened.
853 process_nonsvc_progs()
855 out=$1
856 echo "# Non-service programs rules" >>${out}
857 progs=`global_get_prop_value $FW_CONFIG_DEF_PG $OPEN_PORTS_PROP`
859 for prog in $progs; do
860 [ -z "$prog" -o "$prog" = '""' ] && continue
862 port=`tuple_get_port $prog`
863 [ $? -eq 1 -o -z "$port" ] && continue
865 proto=`tuple_get_proto $prog`
866 [ $? -eq 1 ] && continue
868 set -- $port
869 if [ $# -gt 1 ]; then
870 if [ -z "$proto" ]; then
871 echo "pass in log quick from any to any" \
872 "port ${1} >< ${2}" >>${out}
873 else
874 echo "pass in log quick proto ${proto} from any" \
875 "to any port ${1} >< ${2}" >>${out}
877 else
878 if [ -z "$proto" ]; then
879 echo "pass in log quick from any to any" \
880 "port = ${1}" >>${out}
881 else
882 echo "pass in log quick proto ${proto} from any" \
883 "to any port = ${1}" >>${out}
886 done
888 return 0
892 # Generate a new /etc/ipf/ipf.conf. If firewall policy is 'none',
893 # ipf.conf is empty .
895 create_global_rules()
897 if [ "$GLOBAL_POLICY" = "custom" ]; then
898 file=`global_get_prop_value $FW_CONFIG_DEF_PG $CUSTOM_FILE_PROP`
899 file6=`global_get_prop_value $FW_CONFIG_DEF_PG $CUSTOM_FILE_6_PROP`
901 [ -n "$file" ] && custom_set_symlink $file
902 [ -n "$file6" ] && custom_set_symlink_6 $file6
904 return 0
907 TEMP=`mktemp /var/run/ipf.conf.pid$$.XXXXXX`
908 TEMP6=`mktemp /var/run/ipf6.conf.pid$$.XXXXXX`
909 process_nonsvc_progs $TEMP
910 process_nonsvc_progs $TEMP6
912 echo "# Global Default rules" >>${TEMP}
913 echo "# Global Default rules" >>${TEMP6}
914 if [ "$GLOBAL_POLICY" != "none" ]; then
915 echo "pass out log quick all keep state" >>${TEMP}
916 echo "pass out log quick all keep state" >>${TEMP6}
919 case "$GLOBAL_POLICY" in
920 'none')
921 # No rules
922 replace_file ${IPFILCONF} ${TEMP}
923 replace_file ${IP6FILCONF} ${TEMP6}
924 return $?
927 'deny')
928 ecmd="pass"
929 acmd="block"
932 'allow')
933 ecmd="block"
934 acmd="pass"
937 return 1;
939 esac
941 for name in `global_get_prop_value $FW_CONFIG_DEF_PG $EXCEPTIONS_PROP`; do
942 [ -z "$name" -o "$name" = '""' ] && continue
944 ifc=`get_interface $name`
945 if [ $? -eq 0 -a -n "$ifc" ]; then
946 echo "${ecmd} in log quick on ${ifc} all" >>${TEMP}
947 continue
950 addr=`get_IP ${name}`
951 if [ $? -eq 0 -a -n "$addr" ]; then
952 echo "${ecmd} in log quick from ${addr} to any" >>${TEMP}
955 done
957 for name in `global_get_prop_value $FW_CONFIG_DEF_PG $EXCEPTIONS_6_PROP`; do
958 [ -z "$name" -o "$name" = '""' ] && continue
960 ifc=`get_interface $name`
961 if [ $? -eq 0 -a -n "$ifc" ]; then
962 echo "${ecmd} in log quick on ${ifc} all" >>${TEMP6}
963 continue
966 addr=`get_IP ${name}`
967 if [ $? -eq 0 -a -n "$addr" ]; then
968 echo "${ecmd} in log quick from ${addr} to any" >>${TEMP6}
971 done
973 for name in `global_get_prop_value $FW_CONFIG_DEF_PG $APPLY2_PROP`; do
974 [ -z "$name" -o "$name" = '""' ] && continue
976 ifc=`get_interface $name`
977 if [ $? -eq 0 -a -n "$ifc" ]; then
978 echo "${acmd} in log quick on ${ifc} all" >>${TEMP}
979 continue
982 addr=`get_IP ${name}`
983 if [ $? -eq 0 -a -n "$addr" ]; then
984 echo "${acmd} in log quick from ${addr} to any" >>${TEMP}
986 done
988 for name in `global_get_prop_value $FW_CONFIG_DEF_PG $APPLY2_6_PROP`; do
989 [ -z "$name" -o "$name" = '""' ] && continue
991 ifc=`get_interface $name`
992 if [ $? -eq 0 -a -n "$ifc" ]; then
993 echo "${acmd} in log quick on ${ifc} all" >>${TEMP6}
994 continue
997 addr=`get_IP ${name}`
998 if [ $? -eq 0 -a -n "$addr" ]; then
999 echo "${acmd} in log quick from ${addr} to any" >>${TEMP6}
1001 done
1003 if [ "$GLOBAL_POLICY" = "allow" ]; then
1005 # Allow DHCP(v6) traffic if running as a DHCP client
1007 /sbin/netstrategy | grep dhcp >/dev/null 2>&1
1008 if [ $? -eq 0 ]; then
1009 echo "pass out log quick from any port = 68" \
1010 "keep state" >>${TEMP}
1011 echo "pass in log quick from any to any port = 68" >>${TEMP}
1013 echo "pass out log quick from any port = 546" \
1014 "keep state" >>${TEMP6}
1015 echo "pass in log quick from any to any port = 546" >>${TEMP6}
1017 echo "block in log all" >>${TEMP}
1018 echo "block in log all" >>${TEMP6}
1021 replace_file ${IPFILCONF} ${TEMP}
1022 replace_file ${IP6FILCONF} ${TEMP6}
1023 return $?
1027 # Generate a new /etc/ipf/ipf_ovr.conf, the override system-wide policy. It's
1028 # a simplified policy that doesn't support 'exceptions' entities.
1030 # If firewall policy is "none", no rules are generated.
1032 # Note that "pass" rules don't have "quick" as we don't want
1033 # them to override services' block rules.
1035 create_global_ovr_rules()
1038 # Simply empty override file if global policy is 'custom'
1040 if [ "$GLOBAL_POLICY" = "custom" ]; then
1041 echo "# 'custom' global policy" >$IPFILOVRCONF
1042 echo "# 'custom' global policy" >$IP6FILOVRCONF
1043 return 0
1047 # Get and process override policy
1049 ovr_policy=`global_get_prop_value $FW_CONFIG_OVR_PG $POLICY_PROP`
1050 if [ "$ovr_policy" = "none" ]; then
1051 echo "# global override policy is 'none'" >$IPFILOVRCONF
1052 echo "# global override policy is 'none'" >$IP6FILOVRCONF
1053 return 0
1056 TEMP=`mktemp /var/run/ipf_ovr.conf.pid$$.XXXXXX`
1057 [ "$ovr_policy" = "deny" ] && acmd="block in log quick"
1058 [ "$ovr_policy" = "allow" ] && acmd="pass in log"
1060 apply2_list=`global_get_prop_value $FW_CONFIG_OVR_PG $APPLY2_PROP`
1061 for name in $apply2_list; do
1062 [ -z "$name" -o "$name" = '""' ] && continue
1064 ifc=`get_interface $name`
1065 if [ $? -eq 0 -a -n "$ifc" ]; then
1066 echo "${acmd} on ${ifc} all" >>${TEMP}
1067 continue
1070 addr=`get_IP ${name}`
1071 if [ $? -eq 0 -a -n "$addr" ]; then
1072 echo "${acmd} from ${addr} to any" >>${TEMP}
1074 done
1076 apply2_6_list=`global_get_prop_value $FW_CONFIG_OVR_PG $APPLY2_6_PROP`
1077 for name in $apply2_6_list; do
1078 [ -z "$name" -o "$name" = '""' ] && continue
1080 ifc=`get_interface $name`
1081 if [ $? -eq 0 -a -n "$ifc" ]; then
1082 echo "${acmd} on ${ifc} all" >>${TEMP6}
1083 continue
1086 addr=`get_IP ${name}`
1087 if [ $? -eq 0 -a -n "$addr" ]; then
1088 echo "${acmd} from ${addr} to any" >>${TEMP6}
1090 done
1092 replace_file ${IPFILOVRCONF} ${TEMP}
1093 replace_file ${IP6FILOVRCONF} ${TEMP6}
1094 return $?
1098 # Service is put into maintenance state due to its invalid firewall
1099 # definition and/or policy.
1101 svc_mark_maintenance()
1103 svcadm mark maintenance $1 >/dev/null 2>&1
1105 date=`date`
1106 echo "[ $date ${0}: $1 has invalid ipf configuration. ]"
1107 echo "[ $date ${0}: placing $1 in maintenance. ]"
1110 # Move service's rule files to another location since
1111 # they're most likely invalid.
1113 ipfile=`fmri_to_file $1 $IPF_SUFFIX`
1114 [ -f "$ipfile" ] && mv $ipfile "$ipfile.bak"
1115 ip6file=`fmri_to_file $1 $IPF6_SUFFIX`
1116 [ -f "$ip6file" ] && mv $ip6file "$ip6file.bak"
1118 natfile=`fmri_to_file $1 $NAT_SUFFIX`
1119 [ -f "$natfile" ] && mv $natfile "$natfile.bak"
1121 return 0
1124 svc_is_server()
1126 svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1
1130 # Create rules for enabled firewalling and client services.
1131 # - obtain the list of enabled services and process them
1132 # - save the list of rules file for later use
1134 create_services_rules()
1137 # Do nothing if global policy is 'custom'
1139 [ "$GLOBAL_POLICY" = "custom" ] && return 0
1141 ipf_get_lock
1144 # Get all enabled services
1146 allsvcs=`svcprop -cf -p general/enabled -p general_ovr/enabled '*' \
1147 2>/dev/null | sed -n 's,^\(svc:.*\)/:properties/.* true$,\1,p' | sort -u`
1150 # Process enabled services
1152 for s in $allsvcs; do
1153 service_is_enabled $s || continue
1154 process_service $s || continue
1156 ipfile=`fmri_to_file $s $IPF_SUFFIX`
1157 if [ -n "$ipfile" -a -r "$ipfile" ]; then
1158 check_ipf_syntax $ipfile
1159 if [ $? -ne 0 ]; then
1160 svc_mark_maintenance $s
1161 continue
1164 svc_is_server $s
1165 if [ $? -eq 0 ]; then
1166 check_ipf_rules $ipfile
1167 if [ $? -ne 0 ]; then
1168 svc_mark_maintenance $s
1169 continue
1172 CONF_FILES="$CONF_FILES $ipfile"
1175 ip6file=`fmri_to_file $s $IPF6_SUFFIX`
1176 if [ -n "$ip6file" -a -r "$ip6file" ]; then
1177 check_ipf_syntax $ip6file -6
1178 if [ $? -ne 0 ]; then
1179 svc_mark_maintenance $s
1180 continue
1183 svc_is_server $s
1184 if [ $? -eq 0 ]; then
1185 check_ipf_rules $ip6file -6
1186 if [ $? -ne 0 ]; then
1187 svc_mark_maintenance $s
1188 continue
1191 CONF6_FILES="$CONF6_FILES $ip6file"
1194 natfile=`fmri_to_file $s $NAT_SUFFIX`
1195 if [ -n "$natfile" -a -r "$natfile" ]; then
1196 check_nat_syntax $natfile
1197 if [ $? -ne 0 ]; then
1198 svc_mark_maintenance $s
1199 continue
1202 NAT_FILES="$NAT_FILES $natfile"
1204 done
1206 ipf_remove_lock
1207 return 0
1211 # We update a services ipf ruleset in the following manners:
1212 # - service is disabled, tear down its rules.
1213 # - service is disable or refreshed(online), setup or update its rules.
1215 service_update_rules()
1217 svc=$1
1219 ipfile=`fmri_to_file $svc $IPF_SUFFIX`
1220 ip6file=`fmri_to_file $svc $IPF6_SUFFIX`
1221 [ -n "$ipfile" ] && remove_rules $ipfile
1222 [ -n "$ip6file" ] && remove_rules $ip6file -6
1224 [ -z "$ipfile" -a -z "$ip6file" ] && return 0
1226 natfile=`fmri_to_file $svc $NAT_SUFFIX`
1227 [ -n "$natfile" ] && remove_nat_rules $natfile
1230 # Don't go further if service is disabled or in maintenance.
1232 service_is_enabled $svc || return 0
1233 service_check_state $1 $SMF_MAINT && return 0
1235 process_service $svc || return 1
1236 if [ -f "$ipfile" ]; then
1237 check_ipf_syntax $ipfile
1238 if [ $? -ne 0 ]; then
1239 svc_mark_maintenance $svc
1240 return 1
1244 if [ -f "$ip6file" ]; then
1245 check_ipf_syntax $ip6file -6
1246 if [ $? -ne 0 ]; then
1247 svc_mark_maintenance $svc
1248 return 1
1252 if [ -f "$natfile" ]; then
1253 check_nat_syntax $natfile
1254 if [ $? -ne 0 ]; then
1255 svc_mark_maintenance $svc
1256 return 1
1260 if [ -f "$ipfile" ]; then
1261 svc_is_server $svc
1262 if [ $? -eq 0 ]; then
1263 update_check_ipf_rules $ipfile
1264 if [ $? -ne 0 ]; then
1265 svc_mark_maintenance $svc
1266 return 1
1270 prepend_new_rules $ipfile
1273 # reload Global Override rules to
1274 # maintain correct ordering.
1276 remove_rules $IPFILOVRCONF
1277 prepend_new_rules $IPFILOVRCONF
1280 if [ -f "$ip6file" ]; then
1281 svc_is_server $svc
1282 if [ $? -eq 0 ]; then
1283 update_check_ipf_rules $ip6file -6
1284 if [ $? -ne 0 ]; then
1285 svc_mark_maintenance $svc
1286 return 1
1290 prepend_new_rules $ip6file -6
1293 # reload Global Override rules to
1294 # maintain correct ordering.
1296 remove_rules $IP6FILOVRCONF -6
1297 prepend_new_rules $IP6FILOVRCONF -6
1300 [ -f "$natfile" ] && append_new_nat_rules $natfile
1302 return 0
1306 # Call the service_update_rules with appropriate svc fmri.
1308 # This is called from '/lib/svc/method/ipfilter fw_update' whenever
1309 # a service is disabled/enabled/refreshed.
1311 service_update()
1313 svc=$1
1314 ret=0
1317 # If ipfilter isn't online or global policy is 'custom',
1318 # nothing should be done.
1320 [ "$GLOBAL_POLICY" = "custom" ] && return 0
1321 service_check_state $SMF_FMRI $SMF_ONLINE || return 0
1323 ipf_get_lock
1324 service_update_rules $svc || ret=1
1326 ipf_remove_lock
1327 return $ret
1331 # Initialize global configuration
1333 global_init