ipf_include.sh revision 8823:000507e9108d
1#!/sbin/sh
2#
3# CDDL HEADER START
4#
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.
8#
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.
13#
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]
19#
20# CDDL HEADER END
21#
22#
23# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25#
26
27ETC_IPF_DIR=/etc/ipf
28IP6FILCONF=$ETC_IPF_DIR/ipf6.conf
29IPNATCONF=$ETC_IPF_DIR/ipnat.conf
30IPPOOLCONF=$ETC_IPF_DIR/ippool.conf
31VAR_IPF_DIR=/var/tmp/ipf
32IPFILCONF=$VAR_IPF_DIR/ipf.conf
33IPFILOVRCONF=$VAR_IPF_DIR/ipf_ovr.conf
34IPF_LOCK=/var/run/ipflock
35CONF_FILES=""
36NAT_FILES=""
37IPF_SUFFIX=".ipf"
38NAT_SUFFIX=".nat"
39
40# version for configuration upgrades
41CURRENT_VERSION=1
42
43IPF_FMRI="svc:/network/ipfilter:default"
44INETDFMRI="svc:/network/inetd:default"
45RPCBINDFMRI="svc:/network/rpc/bind:default"
46
47SMF_ONLINE="online"
48SMF_MAINT="maintenance"
49SMF_NONE="none"
50
51FW_CONTEXT_PG="firewall_context"
52METHOD_PROP="ipf_method"
53
54FW_CONFIG_PG="firewall_config"
55POLICY_PROP="policy"
56APPLY2_PROP="apply_to"
57EXCEPTIONS_PROP="exceptions"
58
59FW_CONFIG_DEF_PG="firewall_config_default"
60FW_CONFIG_OVR_PG="firewall_config_override"
61CUSTOM_FILE_PROP="custom_policy_file"
62OPEN_PORTS_PROP="open_ports"
63
64PREFIX_HOST="host:"
65PREFIX_NET="network:"
66PREFIX_POOL="pool:"
67PREFIX_IF="if:"
68
69SERVINFO=/usr/lib/servinfo
70
71#
72# Given a service, gets its config pg name 
73#
74get_config_pg()
75{
76	if [ "$1" = "$IPF_FMRI" ]; then
77		echo "$FW_CONFIG_DEF_PG"
78	else
79		echo "$FW_CONFIG_PG"
80	fi
81	return 0
82}
83
84#
85# Given a service, gets its firewall policy
86#
87get_policy()
88{
89	config_pg=`get_config_pg $1`
90	svcprop -p $config_pg/${POLICY_PROP} $1 2>/dev/null
91}
92
93get_global_def_policy()
94{
95	svcprop -p ${FW_CONFIG_DEF_PG}/${POLICY_PROP} $IPF_FMRI 2>/dev/null
96}
97
98#
99# Given a service, gets its firewall policy
100#
101get_exceptions()
102{
103	config_pg=`get_config_pg $1`
104	svcprop -p $config_pg/${EXCEPTIONS_PROP} $1 2>/dev/null
105}
106
107#
108# Given a service, gets its firewall policy
109#
110get_apply2_list()
111{
112	config_pg=`get_config_pg $1`
113	svcprop -p $config_pg/${APPLY2_PROP} $1 2>/dev/null
114}
115
116check_ipf_dir()
117{
118	[ -d $VAR_IPF_DIR ] && return 0
119	mkdir $VAR_IPF_DIR >/dev/null 2>&1 || return 1
120}
121
122#
123# fmri_to_file fmri suffix
124#
125fmri_to_file()
126{
127	check_ipf_dir || return 1
128	fprefix="${VAR_IPF_DIR}/`echo $1 | tr -s '/:' '__'`"
129	echo "${fprefix}${2}"
130}
131
132#
133# Return service's enabled property
134#
135service_is_enabled()
136{
137	#
138	# Temporary enabled state overrides the persistent state
139	# so check it first.
140	#
141	enabled_ovr=`svcprop -c -p general_ovr/enabled $1 2>/dev/null`
142	if [ -n "$enabled_ovr" ]; then
143		[ "$enabled_ovr" = "true" ] && return 0 || return 1
144	fi
145
146	enabled=`svcprop -c -p general/enabled $1 2>/dev/null`
147	[ -n "$enabled" -a "$enabled" = "true" ] && return 0 || return 1
148}
149
150#
151# Return whether service is desired state
152#
153# Args: fmri state
154# Return:
155#  0 - desired state is service's current state  
156#  1 - desired state is not service's current state  
157#
158service_check_state()
159{
160	#
161	# Make sure we're done with ongoing state transition
162	#
163	while [ "`svcprop -p restarter/next_state $1`" != "$SMF_NONE" ]; do
164		sleep 1
165	done
166	
167	[ "`svcprop -p restarter/state $1`" = "$2" ] && return 0 || return 1
168}
169
170#
171# Deny/Allow list stores values in the form "host:addr", "network:addr/netmask",
172# "pool:number", and "if:interface". This function returns the
173# IP(addr or addr/netmask) value or a pool number.
174#
175get_IP()
176{
177	value_is_interface $1 && return 1
178	echo "$1" | sed -n -e 's,^pool:\(.*\),pool/\1,p' \
179	    -e 's,^host:\(.*\),\1,p' \
180	    -e 's,^network:\(.*\),\1,p'
181}
182
183get_interface()
184{
185	value_is_interface $1 || return 1
186	scratch=`echo "$1" | sed -e 's/^if://'`
187
188	ifconfig $scratch >/dev/null 2>&1 || return 1
189	echo $scratch | sed -e 's/:.*//'
190}
191
192#
193#
194#
195value_is_interface()
196{
197	[ -z "$1" ] && return 1
198	echo $1 | grep "^if:" >/dev/null 2>&1
199}
200
201#
202# Remove rules in given file from active list without restarting ipfilter
203#
204remove_rules()
205{
206	[ -f "$1" ] && ipf -r -f $1 >/dev/null 2>&1
207}
208
209remove_nat_rules()
210{
211	[ -f "$1" ] && ipnat -r -f $1 >/dev/null 2>&1
212}
213
214check_ipf_syntax()
215{
216	ipf -n -f $1 >/dev/null 2>&1
217}
218
219check_nat_syntax()
220{
221	ipnat -n -f $1 >/dev/null 2>&1
222}
223
224file_get_ports()
225{
226	ipf -n -v -f $1 2>/dev/null | sed -n -e \
227	    's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \
228	    awk '{if (length($0) > 1) {printf("%s ", $1)}}'
229}
230
231get_active_ports()
232{
233	ipfstat -io 2>/dev/null | sed -n -e \
234	    's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \
235	    awk '{if (length($0) > 1) {printf("%s ",$1)}}'
236}
237
238#
239# Given two list of ports, return failure if there's a duplicate.
240#
241sets_check_duplicate()
242{
243	#
244	# If either list is empty, there isn't any conflict.
245	#
246	[ -z "$1" -o -z "$2" ] && return 0
247
248	for p in $1; do
249		for ap in $2; do
250			[ "$p" = "$ap" ] && return 1
251		done
252	done
253
254	return 0
255}
256
257#
258# Given a file containing ipf rules, check the syntax and verify
259# the rules don't conflict, use same port number, with active
260# rules (ipfstat -io output).
261#
262update_check_ipf_rules()
263{
264	check_ipf_syntax $1 || return 1
265
266	lports=`file_get_ports $1`
267	lactive_ports=`get_active_ports`
268
269	sets_check_duplicate "$lports" "$lactive_ports" || return 1
270}
271
272server_port_list=""
273
274#
275# Given a file containing ipf rules, check the syntax and verify
276# the rules don't conflict with already processed services.
277#
278# The list of processed services' ports are maintained in the global
279# variable 'server_port_list'.
280#
281check_ipf_rules()
282{
283	check_ipf_syntax $1 || return 1
284
285	lports=`file_get_ports $1`
286	sets_check_duplicate "$lports" "$server_port_list" || return 1
287	server_port_list="$server_port_list $lports"
288	return 0
289}
290
291prepend_new_rules()
292{
293	check_ipf_syntax $1 && tail -r $1 | sed -e 's/^[a-z]/@0 &/' | \
294	    ipf -f - >/dev/null 2>&1
295}
296
297append_new_rules()
298{
299	check_ipf_syntax $1 && ipf -f $1 >/dev/null 2>&1
300}
301
302append_new_nat_rules()
303{
304	check_nat_syntax $1 && ipnat -f $1 >/dev/null 2>&1
305}
306
307#
308# get port information from string of the form "proto:{port | port-port}"
309#
310tuple_get_port()
311{
312	port_str=`echo "$1" | sed -e 's/ //g; s/.*://' 2>/dev/null`
313	[ -z "$port_str" ] && return 1
314
315	echo $port_str | grep "-" >/dev/null
316	if  [ $? -eq  0 ]; then
317		echo $port_str | grep '^[0-9]\{1,5\}-[0-9]\{1,5\}$' >/dev/null || \
318		    return 1
319		ports=`echo $port_str | ( IFS=- read a b ; \
320		    [ $a \-le $b ] && echo $a $b || echo $b $a )`
321
322		for p in $ports; do
323			[ $p -gt 65535 ] && return 1
324		done
325		echo "$ports"
326	else
327		#
328		# port_str is a single port, verify and return it.
329		#
330		echo "$port_str" | grep '^[0-9]\{1,5\}$' >/dev/null || return 1
331		[ $port_str -gt 65535 ] && return 1
332		echo "$port_str"
333	fi
334}
335
336#
337# get proto info from string of the form "{tcp | udp}:port"
338#
339tuple_get_proto()
340{
341	proto=`echo "$1" | sed -e 's/ //g; s/:.*//' 2>/dev/null`
342	[ -z "$proto" ] && return 0
343
344	[ "$proto" = "tcp" -o "$proto" = "udp" ] && echo $proto || return 1
345	return 0
346}
347
348ipf_get_lock()
349{
350	newpid=$$
351
352	if [ -f "$IPF_LOCK/pid" ]; then
353		curpid=`cat $IPF_LOCK/pid 2>/dev/null`
354		[ "$curpid" = "$newpid" ] && return 0
355
356		#
357		# Clear lock if the owning process is no longer around.
358		#
359		ps -p $curpid >/dev/null 2>&1 || rm -r $IPF_LOCK >/dev/null 2>&1
360	fi
361
362	#
363	# Grab the lock
364	#
365	while :; do
366		mkdir $IPF_LOCK 2>/dev/null && break;
367		sleep 1
368	done
369	echo $newpid > $IPF_LOCK/pid
370}
371
372#
373# Remove lock if it's ours
374#
375ipf_remove_lock()
376{
377	if [ -f "$IPF_LOCK/pid" ]; then
378		[ "`cat $IPF_LOCK/pid`" = "$$" ] && rm -r $IPF_LOCK
379	fi
380	return 0
381}
382
383#
384# Make IPFILCONF, /var/tmp/ipf/ipf.conf, a symlink to the input file argument.
385#
386custom_set_symlink()
387{
388	#
389	# Nothing to do if the input file doesn't exist or
390	# if the input file is the "/etc/ipf/ipf.conf" file.
391	#
392	[ ! -f "$1" ] && return 0
393
394	rm $IPFILCONF >/dev/null 2>&1
395	ln -s $1 $IPFILCONF >/dev/null 2>&1
396}
397
398#
399# New file replaces original file if they have different content
400#
401replace_file()
402{
403	orig=$1	
404	new=$2	
405
406	#
407	# IPFILCONF may be a symlink, remove it if that's the case
408	#
409	if [ -L "$orig" ]; then
410		rm $orig
411		touch $orig
412	fi
413
414	mv $new $orig && return 0 || return 1
415}
416
417#
418# Given a service, gets the following details for ipf rule:
419# - policy
420# - protocol
421# - port(IANA port obtained by running servinfo)
422#
423process_server_svc()
424{
425	service=$1
426	ip="any"
427        policy=`get_policy ${service}`
428
429	#
430	# Empties service's rules file so callers won't use existing rule if
431	# we fail here.
432	#
433	file=`fmri_to_file $service $IPF_SUFFIX`
434	[ -z "$file" ] && return 1
435	echo "# $service" >${file}
436
437	#
438	# Nothing to do if policy is "use_global"
439	#
440	[ "$policy" = "use_global" ] && return 0
441
442	restarter=`svcprop -p general/restarter $service 2>/dev/null`
443	if [ "$restarter" = "$INETDFMRI" ]; then
444		iana_name=`svcprop -p inetd/name $service 2>/dev/null`
445		isrpc=`svcprop -p inetd/isrpc $service 2>/dev/null`
446	else
447		iana_name=`svcprop -p $FW_CONTEXT_PG/name $service 2>/dev/null`
448		isrpc=`svcprop -p $FW_CONTEXT_PG/isrpc $service 2>/dev/null`
449	fi
450
451	#
452	# Bail if iana_name isn't defined. Services with static rules
453	# like nis/client don't need to generate rules using
454	# iana name and protocol information.
455	# 
456	[ -z "$iana_name" ] && return 1
457
458	#
459	# RPC services
460	#
461	if [ "$isrpc" = "true" ]; then
462		tports=`$SERVINFO -R -p -t -s $iana_name 2>/dev/null`
463		if [ -n "$tports" ]; then
464			for tport in $tports; do 
465				generate_rules $service $policy "tcp" \
466				    $ip $tport $file
467			done
468		fi
469
470		uports=`$SERVINFO -R -p -u -s $iana_name 2>/dev/null`
471		if [ -n "$uports" ]; then
472			for uport in $uports; do 
473				generate_rules $service $policy "udp" \
474				    $ip $uport $file
475			done
476		fi
477
478		return 0
479	fi
480
481	#
482	# Get the IANA port and supported protocols(tcp and udp)
483	# No support for IPv6 at this point.
484	#
485	tport=`$SERVINFO -p -t -s $iana_name 2>&1`
486	if [ $? -eq 0 -a -n "$tport" ]; then
487		generate_rules $service $policy "tcp" $ip $tport $file
488	fi
489
490	uport=`$SERVINFO -p -u -s $iana_name 2>&1`
491	if [ $? -eq 0 -a -n "$uport" ]; then
492		generate_rules $service $policy "udp" $ip $uport $file
493	fi
494
495	return 0
496}
497
498#
499# Given a service's name, policy, protocol and port, generate ipf rules
500# - list of host/network/interface to apply policy
501#
502# A 'use_global' policy inherits the system-wided Global Default policy
503# from network/ipfilter. For {deny | allow} policies, the rules are
504# ordered as:
505#
506# - make exceptions to policy for those in "exceptions" list
507# - apply policy to those specified in "apply_to" list 
508# - policy rule
509#
510generate_rules()
511{
512	service=$1
513	mypolicy=$2
514	proto=$3
515	ip=$4
516	port=$5
517	out=$6
518
519	#
520	# Default mode is to inherit from global's policy
521	#
522	[ "$mypolicy" = "use_global" ] && return 0
523
524	tcp_opts=""
525	[ "$proto" = "tcp" ] && tcp_opts="flags S keep state keep frags"
526
527	#
528	# Allow all if policy is 'none'
529	#
530	if [ "$mypolicy" = "none" ]; then
531		echo "pass in log quick proto ${proto} from any to ${ip}" \
532		    "port = ${port} ${tcp_opts}" >>${out}
533		return 0
534	fi
535
536	#
537	# For now, let's concern only with incoming traffic.
538	#
539	[ "$mypolicy" = "deny" ] && { ecmd="pass"; acmd="block"; }
540	[ "$mypolicy" = "allow" ] && { ecmd="block"; acmd="pass"; }
541
542	for name in `get_exceptions $service`; do
543		[ -z "$name" -o "$name" = '""' ] && continue
544
545		ifc=`get_interface $name`
546		if [ $? -eq 0 -a -n "$ifc" ]; then
547			echo "${ecmd} in log quick on ${ifc} from any to" \
548			    "${ip} port = ${port}" >>${out}
549			continue
550		fi
551
552		addr=`get_IP ${name}`
553		if [ $? -eq 0 -a -n "$addr" ]; then
554			echo "${ecmd} in log quick proto ${proto} from ${addr}" \
555			    "to ${ip} port = ${port} ${tcp_opts}" >>${out}
556		fi
557	done
558
559	for name in `get_apply2_list $service`; do
560		[ -z "$name" -o "$name" = '""' ] && continue
561
562		ifc=`get_interface $name`
563		if [ $? -eq 0 -a -n "$ifc" ]; then
564			echo "${acmd} in log quick on ${ifc} from any to" \
565			    "${ip} port = ${port}" >>${out}
566			continue
567		fi
568
569		addr=`get_IP ${name}`
570		if [ $? -eq 0 -a -n "$addr" ]; then
571			echo "${acmd} in log quick proto ${proto} from ${addr}" \
572			    "to ${ip} port = ${port} ${tcp_opts}" >>${out}
573		fi
574	done
575
576	echo "${ecmd} in log quick proto ${proto} from any to ${ip}" \
577	    "port = ${port} ${tcp_opts}" >>${out}
578
579	return 0
580}
581
582#
583# Service has either IANA ports and proto or its own firewall method to
584# generate the rules.
585#
586# - if service has a custom method, use it to populate its rules
587# - if service has a firewall_config pg, use process_server_svc
588#
589# Argument - fmri
590#
591process_service()
592{
593	#
594	# Don't process network/ipfilter
595	#
596	[ "$1" = "$IPF_FMRI" ] && return 0
597
598	service_check_state $1 $SMF_MAINT && return 1
599
600	method=`svcprop -p $FW_CONTEXT_PG/$METHOD_PROP $1 2>/dev/null | \
601	    sed 's/\\\//g'`
602	if [ -n "$method" -a "$method" != '""' ]; then
603		( exec $method $1 >/dev/null )
604	else
605		svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1 || return 1
606		process_server_svc $1 || return 1
607	fi
608	return 0
609}
610
611#
612# Generate rules for protocol/port defined in firewall_config_default/open_ports
613# property. These are non-service programs whose network resource info are
614# defined as "{tcp | upd}:{PORT | PORT-PORT}". Essentially, these programs need
615# some specific local ports to be opened. For example, BitTorrent clients need to
616# have 6881-6889 opened.
617#
618process_nonsvc_progs()
619{
620	out=$1
621	echo "# Non-service programs rules" >>${out}
622	progs=`svcprop -p ${FW_CONFIG_DEF_PG}/${OPEN_PORTS_PROP} \
623	    $SMF_FMRI 2>/dev/null`
624
625	for prog in $progs; do
626		[ -z "$prog" -o "$prog" = '""' ] && continue
627
628		port=`tuple_get_port $prog`
629		[ $? -eq 1 -o -z "$port" ] && continue
630
631		proto=`tuple_get_proto $prog`
632		[ $? -eq 1 ] && continue
633
634		set -- $port
635		if  [ $# -gt 1 ]; then
636			if [ -z "$proto" ]; then
637				echo "pass in log quick from any to any" \
638				    "port ${1} >< ${2}" >>${out}
639			else
640				echo "pass in log quick proto ${proto} from any" \
641				    "to any port ${1} >< ${2}" >>${out}
642			fi
643		else
644			if [ -z "$proto" ]; then
645				echo "pass in log quick from any to any" \
646				    "port = ${1}" >>${out}
647			else
648				echo "pass in log quick proto ${proto} from any" \
649				    "to any port = ${1}" >>${out}
650			fi
651		fi
652	done
653
654	return 0
655}
656
657#
658# Generate a new /etc/ipf/ipf.conf. If firewall policy is 'none',
659# ipf.conf is empty .
660#
661create_global_rules()
662{
663	policy=`get_global_def_policy`
664
665	if [ "$policy" = "custom" ]; then
666		file=`svcprop -p ${FW_CONFIG_DEF_PG}/${CUSTOM_FILE_PROP} $SMF_FMRI`
667
668		[ -n "$file" ] && custom_set_symlink $file
669		return 0
670	fi
671
672	TEMP=`mktemp /var/run/ipf.conf.pid$$.XXXXXX`
673	process_nonsvc_progs $TEMP
674
675	echo "# Global Default rules" >>${TEMP}
676	if [ "$policy" != "none" ]; then
677		echo "pass out log quick all keep state" >>${TEMP}
678	fi
679
680	case "$policy" in
681	'none')
682		# No rules
683		replace_file ${IPFILCONF} ${TEMP}
684		return $?
685		;;
686	
687	'deny')
688		ecmd="pass"
689		acmd="block"
690		;;
691
692	'allow')
693		ecmd="block"
694		acmd="pass"
695		;;
696	*)
697		return 1;
698		;;
699	esac
700
701	for name in `get_exceptions $SMF_FMRI`; do
702		[ -z "$name" -o "$name" = '""' ] && continue
703
704		ifc=`get_interface $name`
705		if [ $? -eq 0 -a -n "$ifc" ]; then
706			echo "${ecmd} in log quick on ${ifc} all" >>${TEMP} 
707			continue
708		fi
709
710		addr=`get_IP ${name}`
711		if [ $? -eq 0 -a -n "$addr" ]; then
712			echo "${ecmd} in log quick from ${addr} to any" >>${TEMP}
713		fi
714
715	done
716
717	for name in `get_apply2_list $SMF_FMRI`; do
718		[ -z "$name" -o "$name" = '""' ] && continue
719
720		ifc=`get_interface $name`
721		if [ $? -eq 0 -a -n "$ifc" ]; then
722			echo "${acmd} in log quick on ${ifc} all" >>${TEMP}
723			continue
724		fi
725
726		addr=`get_IP ${name}`
727		if [ $? -eq 0 -a -n "$addr" ]; then
728			echo "${acmd} in log quick from ${addr} to any" >>${TEMP}
729		fi
730	done
731
732	if [ "$policy" = "allow" ]; then
733		#
734		# Allow DHCP traffic if running as a DHCP client 
735		#
736		/sbin/netstrategy | grep dhcp >/dev/null 2>&1
737		if [ $? -eq 0 ]; then
738			echo "pass out log quick from any port = 68" \
739			    "keep state" >>${TEMP}
740			echo "pass out log quick from any port = 546" \
741			    "keep state" >>${TEMP}
742			echo "pass in log quick from any to any port = 68" >>${TEMP}
743			echo "pass in log quick from any to any port = 546" >>${TEMP}
744		fi
745		echo "block in log all" >>${TEMP}
746	fi
747
748	replace_file ${IPFILCONF} ${TEMP}
749	return $?
750}
751
752#
753# Generate a new /etc/ipf/ipf_ovr.conf, the override system-wide policy. It's
754# a simplified policy that doesn't support 'exceptions' entities.
755#
756# If firewall policy is "none", no rules are generated.
757#
758# Note that "pass" rules don't have "quick" as we don't want
759# them to override services' block rules.
760#
761create_global_ovr_rules()
762{
763	#
764	# Simply empty override file if global policy is 'custom'
765	#
766	if [ "`get_global_def_policy`" = "custom" ]; then 
767		echo "# 'custom' global policy" >$IPFILOVRCONF
768		return 0
769	fi
770
771	#
772	# Get and process override policy
773	#
774	ovr_policy=`svcprop -p ${FW_CONFIG_OVR_PG}/${POLICY_PROP} $IPF_FMRI`
775	TEMP=`mktemp /var/run/ipf_ovr.conf.pid$$.XXXXXX`
776
777	[ "$ovr_policy" = "deny" ] && acmd="block in log quick"
778	[ "$ovr_policy" = "allow" ] && acmd="pass in log"
779
780	apply2_list=`svcprop -p $FW_CONFIG_OVR_PG/$APPLY2_PROP $IPF_FMRI`
781	for name in $apply2_list; do
782		[ -z "$name" -o "$name" = '""' ] && continue
783
784		ifc=`get_interface $name`
785		if [ $? -eq 0 -a -n "$ifc" ]; then
786			echo "${acmd} on ${ifc} all" >>${TEMP}
787			continue
788		fi
789
790		addr=`get_IP ${name}`
791		if [ $? -eq 0 -a -n "$addr" ]; then
792			echo "${acmd} from ${addr} to any" >>${TEMP}
793		fi
794	done
795
796	replace_file ${IPFILOVRCONF} ${TEMP}
797	return $?
798}
799
800#
801# Service is put into maintenance state due to its invalid firewall
802# definition and/or policy.
803#
804svc_mark_maintenance()
805{
806	svcadm mark maintenance $1 >/dev/null 2>&1
807
808	date=`date`
809	echo "[ $date ${0}: $1 has invalid ipf configuration. ]"
810	echo "[ $date ${0}: placing $1 in maintenance. ]"
811
812	#
813	# Move service's rule files to another location since
814	# they're most likely invalid.
815	#
816	ipfile=`fmri_to_file $1 $IPF_SUFFIX`
817	[ -f "$ipfile" ] && mv $ipfile "$ipfile.bak"
818
819	natfile=`fmri_to_file $1 $NAT_SUFFIX`
820	[ -f "$natfile" ] && mv $natfile "$natfile.bak"
821
822	return 0
823}
824
825svc_is_server()
826{
827	svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1
828}
829
830#
831# Create rules for enabled firewalling and client services.
832# - obtain the list of enabled services and process them
833# - save the list of rules file for later use
834#
835create_services_rules()
836{
837	#
838	# Do nothing if global policy is 'custom'
839	#
840	global_policy=`get_global_def_policy`
841	[ "$global_policy" = "custom" ] && return 0
842
843	ipf_get_lock
844
845	#
846	# Get all enabled services
847	#
848	allsvcs=`svcprop -cf -p general/enabled -p general_ovr/enabled '*' \
849	    2>/dev/null | sed -n 's,^\(svc:.*\)/:properties/.* true$,\1,p' | sort -u`
850
851	#
852	# Process enabled services 
853	#
854	for s in $allsvcs; do
855		service_is_enabled $s || continue
856		process_service $s || continue
857
858		ipfile=`fmri_to_file $s $IPF_SUFFIX`
859		if [ -n "$ipfile" -a -r "$ipfile" ]; then
860			check_ipf_syntax $ipfile
861			if [ $? -ne 0 ]; then
862				svc_mark_maintenance $s
863				continue
864			fi
865
866			svc_is_server $s
867			if [ $? -eq 0 ]; then
868				check_ipf_rules $ipfile
869				if [ $? -ne 0 ]; then
870					svc_mark_maintenance $s
871					continue
872				fi
873			fi
874			CONF_FILES="$CONF_FILES $ipfile"
875		fi
876
877		natfile=`fmri_to_file $s $NAT_SUFFIX`
878		if [ -n "$natfile" -a -r "$natfile" ]; then
879			check_nat_syntax $natfile
880			if [ $? -ne 0 ]; then
881				svc_mark_maintenance $s
882				continue
883			fi
884
885			NAT_FILES="$NAT_FILES $natfile"
886		fi
887	done
888
889	ipf_remove_lock
890	return 0
891}
892
893#
894# We update a services ipf ruleset in the following manners:
895# - service is disabled, tear down its rules.
896# - service is disable or refreshed(online), setup or update its rules.
897#
898service_update_rules()
899{
900	#
901	# If ipfilter isn't online or global policy is 'custom',
902	# nothing should be done.
903	#
904	service_check_state $SMF_FMRI $SMF_ONLINE || return 0
905	[ "`get_global_def_policy`" = "custom" ] && return 0
906
907	svc=$1
908
909	ipfile=`fmri_to_file $svc $IPF_SUFFIX`
910	[ -z "$ipfile" ] && return 0
911
912	remove_rules $ipfile
913
914	natfile=`fmri_to_file $svc $NAT_SUFFIX`
915	[ -n "$natfile" ] && remove_nat_rules $natfile
916
917	#
918	# Don't go further if service is disabled or in maintenance.
919	#
920	service_is_enabled $svc || return 0
921	service_check_state $1 $SMF_MAINT && return 0
922
923	process_service $svc || return 1
924	if [ -f "$ipfile" ]; then
925		check_ipf_syntax $ipfile
926		if [ $? -ne 0 ]; then
927			svc_mark_maintenance $svc
928			return 1
929		fi
930	fi
931
932	if [ -f "$natfile" ]; then
933		check_nat_syntax $natfile
934		if [ $? -ne 0 ]; then
935			svc_mark_maintenance $svc
936			return 1
937		fi
938	fi
939
940	if [ -f "$ipfile" ]; then
941		svc_is_server $svc
942		if [ $? -eq 0 ]; then
943			update_check_ipf_rules $ipfile
944			if [ $? -ne 0 ]; then
945				svc_mark_maintenance $svc
946				return 1
947			fi
948		fi
949
950		prepend_new_rules $ipfile
951
952		#
953		# reload Global Override rules to
954		# maintain correct ordering.
955		#
956		remove_rules $IPFILOVRCONF
957		prepend_new_rules $IPFILOVRCONF
958	fi
959
960	[ -f "$natfile" ] && append_new_nat_rules $natfile
961
962	return 0
963}
964
965#
966# Call the service_update_rules with appropriate svc fmri.
967#
968# This is called from '/lib/svc/method/ipfilter fw_update' whenever
969# a service is disabled/enabled/refreshed.
970#
971service_update()
972{
973	svc=$1
974	ret=0
975
976	ipf_get_lock
977	service_update_rules $svc || ret=1
978
979	ipf_remove_lock
980	return $ret
981}
982