#!/bin/ksh -p
#
# ident "@(#)utdhcpnet.ksh	1.9 04/09/29 SMI"
#
# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#


#
# getIntfbySubnet() - gets the interface name for the specified subnet.
# The interface is printed on the stdout.
#
# Parameters:
#	$1 subnet number
#
getIntfbySubnet() {

	typeset SUBNET=${1:-}
	typeset T_SUB

	while read T_INTF T_ADDR T_MASK
	do
		T_SUB=`IP_Mask2Net $T_ADDR $T_MASK`
		if [ $? -ne 0 ]; then
			# error found while translating the address
			# we just skip it
			continue
		fi
		if [ $SUBNET == $T_SUB ]; then
			# it's local subnet, no dummy subnet needed
			print $T_INTF
			break
		fi
	done < ${DHCPALLINTF}
}

ProcessAdd() {
	BEGAN=false
	while read input
	do
		case $input in
		(begin*)
			COMMAND=${input#begin }
			if [[ $COMMAND != "subnet" && $COMMAND != "interface" ]]; then
				print -u2 "Error: Invalid block type.  Please try again."
				return 1
			else
				BEGAN=true
			fi		
			;;
		(end)
			break
			;;
		(*)
			if ! $BEGAN ; then
				print -u2 "Error: Input must start with a \"begin\" statement"
				return 1
			else
				ConvertKeyToLower "$input"
                        	input="$_RETURN_VAL"
				if [[ $COMMAND = "subnet" ]]; then
					case $input in
					(network*)
						SUBNET=${input#network=}
						SUBNETFILE=$SUBNETFILEPATHPREFIX$SUBNET
						if [[ -f $SUBNETFILE ]] &&
						   ! isDummySubnet $SUBNETFILE; then
                					print -u2 "DHCP subnet configuration for $SUBNET already exists."
                					return 1
        					fi
						checkDummySubnet=$SUBNET
					esac
					SourceSubnet "$input"
				elif [[ $COMMAND = "interface" ]]; then
					case $input in
					(interface*)
						INT=${input#interface=}
						# strip imbedded quotes if any
                                                INT=${INT#\"}
                                                INT=${INT%\"}
						INTFILE=$INTFILEPATHPREFIX$INT	
						if [[ -f $INTFILE ]]; then
							print -u2 "DHCP interface configuration for $INT already exists."
                                                        return 1
					        fi
					esac
					SourceInterface "$input"
				fi
			fi
			;;
		esac
	done
	if [[ $COMMAND = "subnet" ]]; then
		ValidateSubnetBlock
		if [[ $? != 0 ]]; then
			print -u2 "Error: Invalid subnet block"
			return 1
		else
			InsertSystemFiles subnet 
		fi
	elif [[ $COMMAND = "interface" ]]; then
		ValidateInterfaceBlock
		if [[ $? != 0 ]]; then
			print -u2 "Error: Invalid interface block"
			return 1
                else
			InsertSystemFiles interface
                fi
	fi
	return 0
}

#
# InsertSystemFiles() - inserts the DHCP info into system files
#	(e.g. /etc/dhcpd.conf and /etc/sysconfig/dhcpd.
#
# Parameters:
#	$1 defines either subnet or interface operation
#	$2 dummy flag.  Used to generate dummy subnet blocks
#
InsertSystemFiles() {

	typeset mode=$1
	typeset Dummy=${2:-}
	typeset INTF=""
	typeset SUBNET=""
	typeset OUTPUTFILE=""
	typeset INCFILE=""

	case $mode in
	(subnet)
		SUBNET=$UT_DHCP_NETWORK
		INTF=`getIntfbySubnet $SUBNET`
		OUTPUTFILE=$SUBNETFILEPATHPREFIX$SUBNET
		GenerateSubnetDHCPDConf $Dummy > $OUTPUTFILE
		if $CHROOTED; then
			ETCOUTPUTFILE="/etc/$SUBNETFILEPREFIX$SUBNET"
		fi
		;;
	(interface)
		INTF=$UT_DHCP_INTERFACE
		OUTPUTFILE=$INTFILEPATHPREFIX$INTF
		GenerateInterfaceDHCPDConf > $OUTPUTFILE
		if $CHROOTED; then
			ETCOUTPUTFILE="/etc/$INTFILEPREFIX$INTF"
		fi
		;;
	esac

	if $CHROOTED; then
		cp $OUTPUTFILE $ETCOUTPUTFILE 2>/dev/null
		INCFILE=$ETCOUTPUTFILE
	else
		INCFILE=$OUTPUTFILE
	fi
	ls $DHCPDCONF | xargs grep "$INCFILE" >/dev/null
	if [[ $? != 0 ]]; then
		InsertGenericInclude "$INCFILE" "$INTF"
	fi
}

#
# GenerateSubnetDHCPDConf() - generates the DHCP file for the subnetwork.
# 
# Parameters:
#	$0 Dummy Subnet flag. When specified, only "not authoritative" option
#	   will be set in the block instead of range and lease information.
#	   Also, no need to generate the option block.
#
GenerateSubnetDHCPDConf() {
	typeset Dummy=${1:-}
	if [ -n "$Dummy" ]; then
		print "$DUMMY_SUBNET_COMMENT"
	fi
	print "subnet $UT_DHCP_NETWORK netmask $UT_DHCP_NETMASK {"
	if [[ -n $UT_DHCP_BROADCAST ]]; then
		print "	option broadcast-address $UT_DHCP_BROADCAST;"
	fi
	if [[ -n $UT_DHCP_SUBNETMASK ]]; then
		print "	option subnet-mask $UT_DHCP_SUBNETMASK;"
	fi
	if [[ -n $UT_DHCP_MTU ]]; then
		print "	option interface-mtu $UT_DHCP_MTU;"
	fi
	if [[ -n $UT_DHCP_ROUTERS ]]; then
		print "	option routers $UT_DHCP_ROUTERS;"
	fi
	if [ -z "$Dummy" ];then
		print "	vendor-option-space NewT;"
	fi
	if [[ -n $UT_DHCP_AUTHSRVR ]]; then
		print "	option NewT.AuthSrvr $UT_DHCP_AUTHSRVR;"
	fi
	if [[ -n $UT_DHCP_ALTAUTHLIST ]]; then
		UT_DHCP_ALTAUTHLIST=`print -- $UT_DHCP_ALTAUTHLIST | 
		sed -e 's/ /,/g'`
		print "	option NewT.AltAuth $UT_DHCP_ALTAUTHLIST;"
	fi
	if [[ -n $UT_DHCP_FIRMWARESRVR ]]; then
		print "	option NewT.FWSrvr $UT_DHCP_FIRMWARESRVR;"
	fi
	if [ -n "$Dummy" ]; then
		print "	not authoritative;"
	else
		GenerateOptionsDHCPDBlock
		if [[ -n $UT_DHCP_RANGE ]]; then
			print "	range $UT_DHCP_RANGE;"
			print "	not authoritative;"
		else
			print "	authoritative;"
		fi
		print "	max-lease-time 86400;"
		print "	default-lease-time 86400;"
	fi
	print "}"
}

GenerateInterfaceDHCPDConf() {
cat <<!
subnet $UT_DHCP_NETWORK netmask $UT_DHCP_NETMASK {
	option dhcp-server-identifier $UT_DHCP_IP;
	#option hostname $UT_DHCP_HOSTNAME;
!
        if [[ -n $UT_DHCP_BROADCAST ]]; then
		print "	option broadcast-address $UT_DHCP_BROADCAST;"
	fi
	if [[ -n $UT_DHCP_SUBNETMASK ]]; then
		print "	option subnet-mask $UT_DHCP_SUBNETMASK;"
	fi
        if [[ -n $UT_DHCP_MTU ]]; then
		print "	option interface-mtu $UT_DHCP_MTU;"
	fi
        if [[ -n $UT_DHCP_ROUTERS ]]; then
		print "	option routers $UT_DHCP_ROUTERS;"
	fi
	print "	vendor-option-space NewT;"
	print "	option NewT.Intf \"$UT_DHCP_INTERFACE\";"
	if [[ -n $UT_DHCP_AUTHSRVR ]]; then
		print "	option NewT.AuthSrvr $UT_DHCP_AUTHSRVR;"
	fi
        if [[ -n $UT_DHCP_ALTAUTHLIST ]]; then
		UT_DHCP_ALTAUTHLIST=`print -- $UT_DHCP_ALTAUTHLIST |
		sed -e 's/ /,/g'`
		print "	option NewT.AltAuth $UT_DHCP_ALTAUTHLIST;"
	fi
        if [[ -n $UT_DHCP_FIRMWARESRVR ]]; then
		print "	option NewT.FWSrvr $UT_DHCP_FIRMWARESRVR;"
	fi
        GenerateOptionsDHCPDBlock
	if [[ -n $UT_DHCP_RANGE ]]; then
		print "	range $UT_DHCP_RANGE;"
		print "	not authoritative;"
	else
		print "	authoritative;"
	fi
	print "	max-lease-time 86400;"
	print "	default-lease-time 86400;"
	print "}"
}

ProcessDelete() {
	BEGAN=false
	INTSET=false
	NETSET=false

	while read input
	do
          	case $input in
		(begin*)
                        COMMAND=${input#begin }
                        if [[ $COMMAND != "subnet" && $COMMAND != "interface" ]]; then
                                print -u2 "Error: Invalid block type.  Please try again."
				return 1
			else
				BEGAN=true
                        fi
                        ;;
		(interface*)
			if $BEGAN; then
				if [[ $COMMAND = "subnet" ]]; then
					print -u2 "Error: Invalid block."
					return 1
				elif [[ $COMMAND = "interface" ]]; then
					INT=${input#interface=}
					# strip imbedded quotes if any
                                        INT=${INT#\"}
                                        INT=${INT%\"}
					INTFILE=$INTFILEPATHPREFIX$INT	
					if [[ ! -f $INTFILE ]]; then
                                        	print -u2 "Configuration for DHCP interface $INT does not exist."
                                        	return 1
					else
						INTSET=true
					fi
				fi
			else
				print -u2 "Error: Input must start with a \"begin\" statement"
				return 1
			fi
			;;	
		(network*)
			if $BEGAN; then
				if [[ $COMMAND = "interface" ]]; then
					print -u2 "Error: Invalid block."
                                        return 1
				elif [[ $COMMAND = "subnet" ]]; then
					SUBNET=${input#network=}
					SUBNETFILE=$SUBNETFILEPATHPREFIX$SUBNET
					if [[ ! -f $SUBNETFILE ]] ||
					   isDummySubnet $SUBNETFILE; then
                				print -u2 "Configuration for DHCP subnet $SUBNET does not exist."
						return 1
					else
						NETSET=true
					fi
					checkDummySubnet=$SUBNET
				fi
			else
				print -u2 "Error: Input must start with a \"begin\" statement"	
				return 1
			fi
                        ;;
		(end)
			if $NETSET; then
				typeset Intf=`getIntfbySubnet $SUBNET`
				DeleteGenericInclude "subnet-$SUBNET" $Intf
				rm -f $SUBNETFILE 2>/dev/null
				if $CHROOTED; then
					rm -f "/etc/$SUBNETFILEPREFIX$SUBNET" 2>/dev/null
				fi
			elif $INTSET; then
				DeleteGenericInclude "interface-$INT" $Intf
				rm -f $INTFILE 2>/dev/null
				if $CHROOTED; then
					rm -f "/etc/$INTFILEPREFIX$INT" 2>/dev/null
				fi
			else
				return 0
			fi
			;;
		(*)
			if $BEGAN; then
				print -u2 "Error: Invalid argument."
				return 1
			else
				print -u2 "Error: Must specify a valid block."
				return 1
			fi
			;;
		esac
	done
	return 0
}

ProcessChange() {
	STARTED=false
	INTSET=false
	NETSET=false

        while read input
        do
                case $input in
		(begin*)
                        COMMAND=${input#begin }
                        if [[ $COMMAND != "subnet" && $COMMAND != "interface" ]]; then
                                print -u2 "Error: Invalid block type.  Please try again."
				return 1
			else
				STARTED=true
                        fi
                        ;;
		(interface*)
			if $STARTED; then
				if [[ $COMMAND = "subnet" ]]; then
					print -u2 "Error: Invalid block."
                                        return 1
				fi						
				INT=${input#interface=}
				# strip imbedded quotes if any
                                INT=${INT#\"}
                                INT=${INT%\"}
				INTFILE=$INTFILEPATHPREFIX$INT
				if [[ ! -f $INTFILE ]]; then
                                        print -u2 "Configuration for DHCP interface $INT does not exist."
                                        return 1
                                else
					INTSET=true
					TranslateDHCPDConf
					InitDHCPBlockParser $UTDHCPFILE
					while GetNextDHCPBlock interface
					do
						if [[ $UT_DHCP_INTERFACE = $INT ]]; then
							break
						fi
					done
				fi
			else
				print -u2 "Error: Input must start with a \"begin\" statement"
                                return 1
			fi
			;;
                (network*)
			if $STARTED; then
				if [[ $COMMAND = "interface" ]]; then
					if ! $INTSET; then
                                		print -u2 "Error: Missing Interface definition"
						return 1
                        		else
                                		ConvertKeyToLower "$input"
                                		input="$_RETURN_VAL"
                                		SourceInterface "$input" 
                        		fi
				elif [[ $COMMAND = "subnet" ]]; then
                        		SUBNET=${input#network=}
                        		SUBNETFILE=$SUBNETFILEPATHPREFIX$SUBNET
                        		if [[ ! -f $SUBNETFILE ]] ||
					   isDummySubnet $SUBNETFILE; then
                                		print -u2 "Configuration for DHCP subnet $SUBNET does not exist."
						return 1
					else
						NETSET=true
						TranslateDHCPDConf
        					InitDHCPBlockParser $UTDHCPFILE
						while GetNextDHCPBlock subnet
						do
							if [[ $UT_DHCP_NETWORK = $SUBNET ]]; then
							break
							fi
						done
					fi
				fi
			else
				print -u2 "Error: Input must start with a \"begin\" statement"
                                return 1
                        fi
                        ;;
                (end)
                        break
                        ;;
                (*)
			if $INTSET; then
				ConvertKeyToLower "$input"
				input="$_RETURN_VAL"
				SourceInterface "$input"
			elif $NETSET; then
				ConvertKeyToLower "$input"
                        	input="$_RETURN_VAL"
                        	SourceSubnet "$input" 
			else
				print -u2 "Error: Missing DHCP block definition"
                                return 1
			fi
                        ;;
                esac
        done
	DestroyDHCPBlockParser
        rm -f $UTDHCPFILE 2>/dev/null
	if $INTSET; then
		ValidateInterfaceBlock
		if [[ $? != 0 ]]; then
                	print -u2 "Error: Invalid interface block"
                	return 1
		else
			GenerateInterfaceDHCPDConf >> $INTFILE.$$
			rm -f $INTFILE 2>/dev/null
			mv $INTFILE.$$ $INTFILE
			if $CHROOTED; then
                                ETCINTFILE="/etc/$INTFILEPREFIX$UT_DHCP_INTERFACE"
                                cp $INTFILE $ETCINTFILE
			fi
		fi
	elif $NETSET; then
		ValidateSubnetBlock
        	if [[ $? != 0 ]]; then
                	print -u2 "Error: Invalid subnet block"
                	return 1
        	else
			GenerateSubnetDHCPDConf >> $SUBNETFILE.$$
			rm -f $SUBNETFILE 2>/dev/null
			mv $SUBNETFILE.$$ $SUBNETFILE
			if $CHROOTED; then
                                ETCSUBNETFILE="/etc/$SUBNETFILEPREFIX$UT_DHCP_NETWORK"
                                cp $SUBNETFILE $ETCSUBNETFILE 2>/dev/null
			fi
		fi
	else
		return 1
        fi
        return 0
}

ListSubnetBlocks() {
	TranslateDHCPDConf
	if [[ $? != 0 ]]; then
                return 0
        fi
	InitDHCPBlockParser $UTDHCPFILE
	if [[ $? != 0 ]]; then
                return 0
        fi
	while GetNextDHCPBlock subnet
        do
                CreateDHCPBlock subnet
	done	
	DestroyDHCPBlockParser
	rm -f $UTDHCPFILE 2>/dev/null
}

ListInterfaceBlocks() {
	TranslateDHCPDConf
	if [[ $? != 0 ]]; then
		return 0
	fi
	InitDHCPBlockParser $UTDHCPFILE
	if [[ $? != 0 ]]; then
		return 0
	fi
	while GetNextDHCPBlock interface
	do
          	CreateDHCPBlock interface
	done
	DestroyDHCPBlockParser
	rm -f $UTDHCPFILE 2>/dev/null
}

#
# addDummySubnets() - adds the dummy local subnets so that DHCP can listen
# on the local intefaces.
#
addDummySubnets() {
	typeset T_INTF;
	typeset T_ADDR;
	typeset T_MASK;
	typeset T_SUB;
	typeset T_SUBFILE;
	typeset T_INTFILE;

	while read T_INTF T_ADDR T_MASK
	do
		T_INTFILE=$INTFILEPATHPREFIX$T_INTF
		if [ -f $T_INTFILE ]; then
			continue
		fi
		T_SUB=`IP_Mask2Net $T_ADDR $T_MASK`
		if [ $? -ne 0 ]; then
			# error found while translating the address
			# we just skip it
			continue
		fi
		T_SUBFILE=$SUBNETFILEPATHPREFIX$T_SUB
		if [ -f $T_SUBFILE ]; then
			continue
		fi
		InitSubnetBlock
		UT_DHCP_NETWORK=$T_SUB
		UT_DHCP_NETMASK=$T_MASK
		InsertSystemFiles subnet Dummy
	done < ${DHCPALLINTF}
}

#
# delDummySubnets() - delete all dummy local subnets.
#
delDummySubnets() {

	typeset T_INTF;
	typeset T_SUBFILE;

	typeset SUBNETFILES=`ls -1 ${SUBNETFILEPATHPREFIX}* 2>/dev/null`
	if [ -z "$SUBNETFILES" ]; then
		# no subnet definition
		return
	fi
	for T_SUBFILE in $SUBNETFILES
	do
		if ! isDummySubnet $T_SUBFILE; then
			continue
		fi
		if ! loadDHCPSubnetBlock $T_SUBFILE; then
			continue
		fi

		T_INTF=`getIntfbySubnet $UT_DHCP_NETWORK`
		if [ -z "$T_INTF" ]; then
			# this means that this is in the local subnet but not
			# attached to any specific interface.  This should not
			# happen.  We just skip this file for now.
			continue
		fi
		# remove the dummy subnet
		DeleteGenericInclude "subnet-$UT_DHCP_NETWORK" $T_INTF
		rm -f $T_SUBFILE
	done
}

#
# loadDHCPSubnetBlock() - loads the specified Subnet block file into the
# global variables UT_DHCP_*.
#
# Parameters:
#	$1 Subnet block file
#
# Returns:
#	0 if successful
#	1 otherwise
#
loadDHCPSubnetBlock() {
	typeset DHCPFile=${1:-}
	typeset status=0

	if [ ! -f $DHCPFile ]; then
		return 1
	fi
	InitSubnetBlock
	TranslateSubnet $DHCPFile
	InitDHCPBlockParser $UTDHCPFILE
	if [[ $? != 0 ]]; then
		return 1
	fi
	if ! GetNextDHCPBlock subnet; then
		status=1
	fi
	DestroyDHCPBlockParser
	rm -f $UTDHCPFILE 2>/dev/null
	return $status
}

#
# checkDummySubnet() - checks to make sure that dummy subnet files are created
# or removed if configuring or un-configuring a remote subnet.  The subnet number
# that was just added/deleted is stored in the checkDummySubnet variable.
#
# Parameters:
#	$1 the operation on the subnet
#
# Returns:
#	0 if successful
#	1 otherwise
#
checkDummySubnet() {

	typeset OPER=$1
	typeset MOD_SUBNET=${checkDummySubnet:-}
	typeset T_INTF;
	typeset T_SUBFILE;

	if [ -z "${MOD_SUBNET}" ]; then
		# nothing to check
		return 0
	fi

	case $OPER in
	(add)

		# check if it's a remote subnet
		T_INTF=`getIntfbySubnet $MOD_SUBNET`
		if [ -z "$T_INTF" ]; then
			addDummySubnets
		fi
		;;
	(delete)
		# first, we need to check to see if any remote subnet is left
		# after the deletion
		typeset SUBNETFILES=`ls -1 ${SUBNETFILEPATHPREFIX}* 2>/dev/null`
		typeset Remote_Subnet_Found=false

		if [ -n "$SUBNETFILES" ]; then
			for T_SUBFILE in $SUBNETFILES
			do
				if isDummySubnet $T_SUBFILE; then
					continue
				fi
				if ! loadDHCPSubnetBlock $T_SUBFILE; then
					continue
				fi

				T_INTF=`getIntfbySubnet $UT_DHCP_NETWORK`
				if [ -z "$T_INTF" ]; then
					# remote subnet found
					Remote_Subnet_Found=true
				fi
			done
			if $Remote_Subnet_Found; then
				addDummySubnets
			else
				delDummySubnets
			fi
		fi
		;;
	esac

}

# main

ETC_OPT_UT=/etc/opt/SUNWut
UT_DHCP_BASEDIR=`(cd ${ETC_OPT_UT}/dhcp ; /bin/pwd)`
DHCPALLINTF=/var/opt/SUNWut/tmp/dhcp_all_interface.$$
STATUS=0

. ${UT_DHCP_BASEDIR}/../../support_lib/dhcp_config
. ${UT_DHCP_BASEDIR}/dhcp_config_linux

TMPDELETEFILE=/var/opt/SUNWut/tmp/$$.delete

# Contains the subnet that was just added/deleted.
checkDummySubnet=""

CheckChrooted

if [[ $# = 0 ]]; then
	ListSubnetBlocks
	ListInterfaceBlocks
	exit 0
fi	

# generate the list of available interfaces on the system.
# it includes the inteface name, IP address, and netmasks
ifconfig -a | awk '
	BEGIN	{ list=0 }
	/Link encap:/		{ if ($3 == "encap:Ethernet") {
				    list=1;
				    intf=$1;
				  } else {
				    list=0;
				  }
				}
	/inet addr:/		{ z = split($2, addr, ":");
				  z = split($4, mask, ":");
				}
	/UP BROADCAST RUNNING/	{ if (list == 1) {
				     printf ("%s %s %s\n", intf, addr[2], mask[2]);
				   }
				}
	' > ${DHCPALLINTF}

case $1 in
(add)
	if ! ProcessAdd; then
		STATUS=1
	else
		checkDummySubnet add
		STATUS=$?
	fi
	;;
(change)
	ProcessChange 
	STATUS=$?
	;;
(delete)
	if ! ProcessDelete; then
		STATUS=1
	else
		checkDummySubnet delete
		STATUS=$?
	fi
	;;
(list)
	if [[ -z $2 ]]; then
		ListSubnetBlocks
		ListInterfaceBlocks
	elif [[ $2 = "subnet" ]]; then
		ListSubnetBlocks
	elif [[ $2 = "interface" ]]; then
		ListInterfaceBlocks
	else
		print -u2 "Must specify either interface or subnet."
		STATUS=1
	fi
	;;
(*)
	STATUS=1
esac

rm -f ${DHCPALLINTF}
exit $STATUS
