#
# Copyright (c) 1998 Sun Microsystems, Inc.
# 
# This software is provided by Sun ``AS IS'' and any express or implied
# warranties, including, but not limited to, the implied warranties of
# merchantability and fitness for a particular purpose are disclaimed.
# In no event shall Sun Microsystems be liable for any direct, indirect,
# incidental, special, exemplary, or consequential damages.
# 
# This software is not a product, and is provided for evaluation purposes
# only.
# 
# This software may not be resold without the express permission of
# Sun Microsystems.
# 
# Feedback on this tool may be sent to packet-shell-owner@sunroof.eng.sun.com.
# 
# 
# ident "@(#)basic_server	1.3	98/02/23 SMI"
#

#
# Given a dhcp module "chaddr" form, convert to ethernet notation.
#
proc chaddr_to_ether { chaddr } {
	regsub -all 0x $chaddr "" temp
	regsub -all " " $temp ":" ether

	return $ether
}

#
# Make ARP entry
#
proc make_arp_entry { chaddr ip } {
	set ether [chaddr_to_ether $chaddr]
	exec arp -s $ip $ether temp
}

#
# Delete ARP entry
proc delete_arp_entry { ip } {
	exec arp -d $ip 
}

#
# Basic client sanity check. Does the client get it right when not thrown any
# curves? We can be called with a bogus IP address to try and throw the client. 
#
# returns:
#	0	-	INIT, INIT-REBOOT, RENEW/REBIND success
#	1	-	protocol problem with client.
#	3	-	DECLINE success
#	4	-	RELEASE success
#
proc dhcp_server_basic { ip_to_use } {

	global bootpc bootps lease_time client_timeout ep clntmsg
	global INADDR_ANY INADDR_BROADCAST

	set INADDR_ANY		0.0.0.0;
	set INADDR_BROADCAST	255.255.255.255;
	set xid			0;
	set bootpc		68;	# Bootp client port
	set bootps		67;	# Bootp server port
	set error		0
	set clients		0

	# open the socket. We don't set options unless the client asks us to
	# by using the broadcast flag.
	popen socket ep inet dgram udp

	foreach i { dontroute broadcast reuseaddr } {
		ep.setsockopt SOL_SOCKET $i 1
	}

	# Bind to bootp server port.
	ep.bind 0.0.0.0/$bootps

	pinit bootps clntmsg - - raw

	while { [ep.recvfrom clntmsg 0 $client_timeout] != "timeout" } {

		# It better be a BOOTREQUEST.
		set optype [lindex [pget clntmsg bootps op] 1]
		if { $optype != "(BOOTREQUEST)" } {
			puts "BOOTP op: $optype != BOOTREQUEST"
			pfree clntmsg
			pclose socket ep
			return 1
		}

		# A message. What kind?
		set msgtype [lindex  \
		    [pget clntmsg bootps opt "dhcp message type"] 1]

		switch -exact -- $msgtype {
			(discover) {
		    		set error [doValidateDiscover clntmsg]
			}
			(request) {
				set error [doValidateRequest clntmsg]
			}
			(inform) {
				set error [doValidateInform clntmsg]
			}
			(decline) {
				set error [doValidateDecline clntmsg]
			}
			(release) {
				set error [doValidateRelease clntmsg]
			}
			default	{
				set error_msg "Unrecognized message: $msgtype"
				pfree clntmsg
				pclose socket ep
				return 1
			}
		}

		if { $error != 0 } {
			pfree clntmsg
			pclose socket ep
			return 1
		}

		# Respond to messages we handle... We don't remember
		# previous ones...
		switch -exact -- $msgtype {
			(discover) {
				set error [doHandleDiscover clntmsg $ip_to_use]
			}
			(request) {
				set error [doHandleRequest clntmsg $ip_to_use]
			}
			(inform) {
				set error [doHandleInform clntmsg]
			}
			(decline) {
				set chaddr [pget clntmsg bootps chaddr]
				set message [pget clntmsg bootps opt message]
				puts "DECLINE received from $chaddr"
				puts "client message: $message"
				set error 3; # decline
			}
			(release) {
				set chaddr [pget clntmsg bootps chaddr]
				set clnt_ip [pget clntmsg bootps ciaddr]
				puts "RELEASE of $clnt_ip received from $chaddr"
				set error 4; # release
			}
		}

		pfree clntmsg
		pinit bootps clntmsg - - raw
	}
	puts "No client requests received in $client_timeout msec, exiting"

	pfree clntmsg
	pclose socket ep

	return $error
}
