;----------------------------------------------------------------------------
;File name:	MASQUE.S			Revision date:	2000.08.01
;Creator:	Ulf Ronald Andersson		Creation date:	1997.08.10
;(c)1997 by:	Ulf Ronald Andersson		All rights reserved
;Feedback to:	dlanor@oden.se			Released as FREEWARE
;----------------------------------------------------------------------------
	include	RA_TOS.I
	include	RA_JAR.I
;
	include	sting\PORT.I
	include	sting\LAYER.I
	include	sting\TRANSPRT.I
	include	sting\TCP.I
	include	sting\UDP.I
	include	sting\DOMAIN.I
;----------------------------------------------------------------------------
	output	.TOS
;----------------------------------------------------------------------------
M_YEAR	=	2000
M_MONTH	=	08
M_DAY	=	01
;
M_TITLE	MACRO
	dc.b	'Masquerade'
	ENDM	;M_TITLE
M_VERSION	MACRO	
	dc.b	'01.16'
	ENDM	;M_VERSION
M_AUTHOR	MACRO
	dc.b	'Ronald Andersson'
	ENDM	;M_AUTHOR
;----------------------------------------------------------------------------
MASQ_INFO_IP	=	$0A00FF49
;----------------------------------------------------------------------------
	struct	masq_port
	d_s	masq_port_main,sizeof_prt_des	;as defined in PORT.I
	struc_p	masq_port_ptr			;-> port to be masked
	uint32	masq_port_ip			;ip number for masking it
	uint32	masq_port_ISP_ip		;masked ip number
	uint32	masq_port_info_ip		;masq info server
	uint32	masq_port_master_ip		;masq master host
	d_end	masq_port
;----------------------------------------------------------------------------
	struct	msq_entry
	struc_p	msq_next
	d_alias	msq_proto_port
	uint16	msq_protocol
	uint16	msq_local_port
	uint32	msq_local_ip
	uint16	msq_remote_port	;word = remote port of connection
	uint32	msq_remote_ip	;long = remote IP of connection
	uint16	msq_index
	uint16	msq_mask_port	;word = port code masking the local port
	d_end	msq_entry
;----------------------------------------------------------------------------
MASQ_POWER	=	12		;bits in masq index (port offsets)
MASQ_TOTAL	=	1<<MASQ_POWER	;same as LINUX (but different method)
MASQ_START	=	61000		;same as LINUX
MASQ_LIMIT	=	MASQ_START+MASQ_TOTAL
;----------------------------------------------------------------------------
	struct	ICMP_HD
	uint8		ICMP_HD_type
	uint8		ICMP_HD_code
	uint16		ICMP_HD_checksum
	d_alias		ICMP_HD_id
	uint8		ICMP_HD_extra_1
	uint8		ICMP_HD_extra_2
	d_alias		ICMP_HD_seq
	uint8		ICMP_HD_extra_3
	uint8		ICMP_HD_extra_4
	d_alias		ICMP_data
	d_end	ICMP_HD
;----------------------------------------------------------------------------
;Start of:	STX program
;----------------------------------------------------------------------------
	SECTION	TEXT
;----------------------------------------------------------------------------
text_start:
;----------------------------------------------------------------------------
start:
	bra	start_1
;----------------------------------------------------------------------------
;Start of:	Resident STX data
;----------------------------------------------------------------------------
my_port:
	dc.l	port_name_s	;prt_des_name
	dc.w	L_MASQUE	;prt_des_type
	dc.w	0		;prt_des_active		Activation flag
	dc.l	0		;prt_des_flags
	dc.l	$0A00FF01	;prt_des_ip_addr	IP_number
	dc.l	-1		;prt_des_sub_mask
	dc.w	1500		;prt_des_mtu
	dc.w	8192		;prt_des_max_mtu
	dc.l	0		;prt_des_stat_sd_data
	dc.l	0		;prt_des_send		->Tx queue
	dc.l	0		;prt_des_stat_rcv_data
	dc.l	0		;prt_des_receive	->Rx queue
	dc.w	0		;prt_des_stat_dropped
	dc.l	my_driver	;prt_des_driver		->driver struct
	dc.l	0		;prt_des_next		->next port
;----------------------------------------------------------------------------
;the port data is extended with 'Masque' specific data below
;----------------------------------------------------------------------------
masq_port_p:	dc.l	0		;masq_port_ptr		;port to be masked
dummy_IP:	dc.l	$0A00FF00	;masq_port_ip		;ip number for masking
real_ISP_IP:	dc.l	0		;masq_port_ISP_ip	;the masked ISP IP
masq_info_IP:	dc.l	MASQ_INFO_IP	;masq_port_info_ip	;masq info server (New: 1999.05.15)
masq_master_IP:	dc.l	$0A00FF01	;masq_port_master_ip	;masq master host (New: 1999.05.15)
;----------------------------------------------------------------------------
my_driver:
	dc.l	my_set_state	;drv_des_set_state
	dc.l	my_cntrl	;drv_des_cntrl
	dc.l	my_send		;drv_des_send
	dc.l	my_receive	;drv_des_receive
	dc.l	driver_name_s	;drv_des_name
	dc.l	version_s	;drv_des_version
	dc.w	((M_YEAR-1980)<<9)|(M_MONTH<<5)|M_DAY
	dc.l	author_s	;drv_des_author
	dc.l	0		;drv_des_next		->next driver
basepage_p:
	dc.l	start-$100	;drv_des_basepage	->basepage of self
;----------------------------------------------------------------------------
port_name_s:
driver_name_s:
	M_TITLE
	dc.b	NUL
	even
;
version_s:
	M_VERSION
	dc.b	NUL
	even
;
author_s:
	M_AUTHOR
	dc.b	NUL
	even
;
MASQ_PORT_vn_s:
	dc.b	'MASQ_PORT',NUL
	even
;
masq_port_name_p:		;char *masq_port_name_p;
	dc.l	0
;
masq_root_p:			;struct msq_entry *masq_root_p;
	dc.l	0
masq_count:			;uint16	masq_count;
	dc.w	0
masq_time:			;uint32 masq_time;
	dc.l	0
;
masq_temp_2L:
masq_temp_0:
	dc.l	0
masq_temp_1:
	dc.l	0
;
active_f:			;uint16	active_f;
	dc.w	0
;
sting_drivers:	ds.l	1	;DRV_LIST	*sting_drivers;
tpl:		ds.l	1	;TPL		*tpl;
stx:		ds.l	1	;STX		*stx;
;----------------------------------------------------------------------------
;End of:	Resident STX data
;----------------------------------------------------------------------------
;Start of:	Resident functions and subroutines
;----------------------------------------------------------------------------
my_cntrl:
	link	a6,#0
	movem.l	d3-d5/a2-a5,-(sp)
	moveq	#E_PARAMETER,d0			;prep E_PARAMETER error for bad port arg
	lea	my_port(pc),a5			;a5 -> my_port
	cmpa.l	8(a6),a5			;port argument correct ?
	bne.s	.exit				;exit if port argument incorrect
	moveq	#E_FNAVAIL,d0			;prep E_FNAVAIL error code for unsupported opcodes
	move	16(a6),d2			;d2 = cntrl_port opcode
	lea	my_CTL_func_t(pc),a0		;a0 -> CTL function table
.seek_CTL_loop:				;loop start for seeking opcode in table
	move	(a0)+,d1			;d1 = CTL opcode from table
	ble.s	.exit				;exit with E_FNAVAIL if opcode unsupported
	move.l	(a0)+,a1			;a0 = CTL routine ptr from table
	cmp	d2,d1				;correct opcode now ?
	bne.s	.seek_CTL_loop			;loop back to seek correct opcode
.found_CTL:
	move.l	12(a6),d0			;d0 = long argument
	move.l	d0,a0				;a0 = long argument
	jsr	(a1)				;call a CTL subroutine
.exit:
	movem.l	(sp)+,d3-d5/a2-a5
	unlk	a6
	rts
;
def_CTL	MACRO	opcode,routine
	dc.w	\1
	dc.l	\2
	ENDM
;
my_CTL_func_t:
	def_CTL	CTL_GENERIC_SET_IP,my_SET_IP
	def_CTL	CTL_GENERIC_GET_IP,my_GET_IP
	def_CTL	CTL_GENERIC_SET_MTU,my_SET_MTU
	def_CTL	CTL_GENERIC_GET_MTU,my_GET_MTU
	def_CTL	CTL_GENERIC_GET_MMTU,my_GET_MMTU
	def_CTL	CTL_GENERIC_GET_TYPE,my_GET_TYPE
	def_CTL	CTL_GENERIC_GET_STAT,my_GET_STAT
	def_CTL	CTL_GENERIC_CLR_STAT,my_CLR_STAT
	def_CTL	CTL_MASQUE_SET_PORT,my_SET_PORT
	def_CTL	CTL_MASQUE_GET_PORT,my_GET_PORT
	def_CTL	CTL_MASQUE_SET_MASKIP,my_SET_MASKIP
	def_CTL	CTL_MASQUE_GET_MASKIP,my_GET_MASKIP
	def_CTL	CTL_MASQUE_GET_REALIP,my_GET_REALIP
	dc.w	0
;
;end of my_cntrl
;----------------------------------------------------------------------------
;Start of:	CTL subroutines
;----------------------------------------------------------------------------
my_SET_IP:
	move.l	masq_port_master_ip(a5),d1
	cmp.l	prt_des_ip_addr(a5),d1
	bne.s	.keep_master
	move.l	d0,masq_port_master_ip(a5)
.keep_master:
	move.l	d0,prt_des_ip_addr(a5)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_IP:
	move.l	prt_des_ip_addr(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_SET_MTU:
	clr.l	d1
	move	prt_des_max_mtu(a5),d1
	cmp.l	d1,d0
	bhi.s	E_PARAMETER_rts
	cmp	#48,d0
	bhs.s	accept_set_mtu
E_PARAMETER_rts:
	moveq	#E_PARAMETER,d0
	rts
;
accept_set_mtu:
	move	d0,prt_des_mtu(a5)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_MTU:
	move	prt_des_mtu(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_MMTU:
	move	prt_des_max_mtu(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_TYPE:
	move	prt_des_type(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_STAT:
	clr.l	d0
	move	prt_des_stat_dropped(a5),d0
	move.l	d0,(a0)+
	move.l	prt_des_stat_sd_data(a5),(a0)+
	move.l	prt_des_stat_rcv_data(a5),(a0)+
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_CLR_STAT:
	clr	prt_des_stat_dropped
	clr.l	prt_des_stat_sd_data
	clr.l	prt_des_stat_rcv_data
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_SET_PORT:
	move.l	d0,masq_port_ptr(a5)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_PORT:
	move.l	masq_port_ptr(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_SET_MASKIP:
	move.l	d0,masq_port_ip(a5)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_MASKIP:
	move.l	masq_port_ip(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_REALIP:
	moveq	#E_UNREACHABLE,d0
	tst	active_f
	beq.s	.exit
	move.l	masq_port_ISP_ip(a5),(a0)
	moveq	#E_NORMAL,d0
.exit:
	rts
;----------------------------------------------------------------------------
;End of:	CTL subroutines
;----------------------------------------------------------------------------
my_send:
	link	a6,#0
	movem.l	d3-d5/a2-a5,-(sp)
	lea	my_port(pc),a5			;a5 -> my_port
	cmpa.l	8(a6),a5			;argument correct
	bne	.exit				;exit if argument incorrect
	tst	active_f			;are we active ?
	beq	.exit				;exit if we're not active
	move.l	masq_port_p(pc),a4		;a4 -> masq_port
	move.l	a4,d0				;is this valid
	ble	.exit				;exit if pointer not valid
	move.l	dummy_IP(PC),d4			;d4 = dummy_IP  (for masq_port)
	cmp.l	prt_des_ip_addr(a4),d4		;is port IP correctly masked
	beq.s	.port_IP_ok			;go keep masked port IP
	move.l	prt_des_ip_addr(a4),real_ISP_IP	;store connection IP number
	move.l	d4,prt_des_ip_addr(a4)		;mask IP number of masq_port
	move.l	prt_des_ip_addr(a5),d0		;d0 = IP of Masquerade port
	move.l	d0,masq_port_master_ip(a5)	;set Masquerade IP as master
.port_IP_ok:
	move.l	masq_port_master_ip(a5),d4	;d4 = masq master machine IP
	TIMER_now				;find work start time
	move.l	d0,masq_time			;store start time for future
	move.l	real_ISP_IP(pc),d5		;d5 = real_ISP_IP  (masqueing)
;
;The loop below is the main loop inside which all masking and unmasking
;of datagrams is made. The detail work is performed by some subroutines.
;
.send_loop:
	move.l	prt_des_send(a5),d3		;Tx queue empty ?
	ble	.exit				;if empty, just exit
;
	TIMER_elapsed	masq_time(pc)		;how long have we worked ?
	cmp.l	#50,d0				;50 ms or more ?
	bhs.s	.exit				;if so, exit
;
;The TIMER calls above ensures that we will not lock up the rest of
;the system even when traffic is intense on some high speed channel.
;
	move.l	d3,a3				;a3 -> Tx dgram of my_port
	move.l	IPDG_next(a3),prt_des_send(a5)	;unlink it from the chain
	clr.l	IPDG_next(a3)			;and cut link from it
	check_dgram_ttl	(a3)
	tst	d0
	bpl.s	.send_it
	addq	#1,prt_des_stat_dropped(a5)
	bra.s	.send_loop
;
.send_it:
	cmp.l	IPDG_hdr+IPHD_ip_dest(a3),d5	;packet from Internet ?  (addressed to real_ISP_IP)
	bne.s	.not_masked			;if not, go mask it and send
;-------
;We come here for each incoming packet from Internet
;-------
	bsr	unmask_datagram			;unmask the datagram
	move.l	a0,d0				;dgram ok or dropped
	ble.s	.send_loop			;try another if this dropped
.mask_bounce:
	lea	prt_des_receive(a5),a2		;a2 -> Rx queue of my_port
	bra.s	.recvgram_test			;go pass it to receiver
;
;The inner loop below adds datagram (a0) to reception queue of my_port
;
.recvgram_loop:
	lea	IPDG_next(a1),a2		;(a2) -> next dgram or is NULL
.recvgram_test:
	move.l	(a2),a1				;a1 -> old dgram or is NULL
	move.l	a1,d0				;at end of reception queue ?
	bne.s	.recvgram_loop			;loop back to pass old stuff
	move.l	a0,(a2)				;store new dgram at end of queue
	clr.l	d0
	move	IPDG_pkt_length(a0),d0
	add.l	d0,prt_des_stat_rcv_data(a5)	;count sent data
	bra.s	.send_loop			;loop back for more new stuff
;-------------------------------------
;We come here for each outgoing packet to the Internet
;
.not_masked:
	bsr	mask_datagram
	move.l	a0,d0				;dgram ok or dropped
	beq.s	.send_loop			;try another after drop
	move.l	a3,a0				;a0 -> dgram
	blt.s	.mask_bounce			;deal with bounce (masq info request)
	lea	prt_des_send(a4),a2		;a2 -> Tx queue of masq_port
	bra.s	.sendgram_test
;
;The inner loop below adds datagram (a0) to transmission queue of masq_port
;
.sendgram_loop:
	lea	IPDG_next(a1),a2		;(a2) -> next dgram or is NULL
.sendgram_test:
	move.l	(a2),a1				;a1 -> old dgram or is NULL
	move.l	a1,d0
	bne.s	.sendgram_loop			;loop back to pass old stuff
	move.l	a0,(a2)				;store new dgram at end of chain
	clr.l	d0
	move	IPDG_pkt_length(a0),d0
	add.l	d0,prt_des_stat_sd_data(a5)	;count sent data
	bra	.send_loop			;loop back for more new stuff
;
.exit:
	movem.l	(sp)+,d3-d5/a2-a5
	unlk	a6
	rts
;
;end of my_send
;----------------------------------------------------------------------------
my_receive:
	rts
;
;end of my_receive  (That was easy...  :-)
;----------------------------------------------------------------------------
my_set_state:
	link	a6,#0
	movem.l	a2-a3,-(sp)
	clr.l	d0
	lea	my_port(pc),a0
	cmpa.l	8(a6),a0
	bne	.exit
	move	12(a6),d0		;d0 = new state
	beq.s	.passivate
.activate:
	tst	active_f
	bne	.done
	move.l	masq_port_p(pc),d0
	bgt.s	.have_port
	bsr	find_masq_port
	move.l	a0,masq_port_p
	ble	.done
	move.l	a0,d0
.have_port:
	move.l	d0,a0
	cmp	#L_INTERNAL,prt_des_type(a0)	;Internal port
	beq.s	.illegal_port
	cmp	#L_MASQUE,prt_des_type(a0)	;Masquerade port
	bne.s	.legal_port
.illegal_port:
	clr.l	d0
	bra.s	.exit
;
.legal_port:
	move.l	dummy_IP(pc),d0
	cmp.l	prt_des_ip_addr(a0),d0
	beq.s	.port_IP_ok
	move.l	prt_des_ip_addr(a0),real_ISP_IP
	move.l	d0,prt_des_ip_addr(a0)
.port_IP_ok:
	st	active_f			;flag activation success
	bra.s	.done
;
.passivate:
	tst	active_f
	beq.s	.done
	move.l	masq_port_p(pc),a0
	move.l	a0,d0
	ble.s	.clear_masqing
	move.l	real_ISP_IP(pc),prt_des_ip_addr(a0)
.clear_masqing:
	clr	active_f
	clr	masq_count
	lea	masq_root_p(pc),a0
	move.l	(a0),d0			;d0 -> masq entry or is NULL
	ble.s	.done
	clr.l	(a0)
	move.l	d0,a3			;a3 -> first entry
.loop:					;loop start to release masq RAM
	move.l	msq_next(a3),d0		;d0 -> next_entry_or_is_NULL
	exg	d0,a3			;d0->this_entry  a3->next_entry_or_is_NULL
	KRfree.i	d0		;release masq entry RAM
	move.l	a3,d0			;d0 -> next_entry_or_is_NULL
	bgt.s	.loop			;loop back to release all entries
.state_set
	move.l	my_port+prt_des_ip_addr(pc),d0	;d0 = IP of Masquerade port
	move.l	d0,my_port+masq_port_master_ip	;set Masquerade IP as master
.done:
	moveq	#1,d0
.exit:
	movem.l	(sp)+,a2-a3
	unlk	a6
	rts
;
;end of my_set_state
;----------------------------------------------------------------------------
;IP_DGRAM *mask_datagram(IP_DGRAM dgram);	/* returns NULL if dropped */
;						/* but E_ERROR for bounce  */
;NB: non-standard register assumptions:
;
;a5 -> my_port  a4 -> masq_port  a3 -> dgram  d5 = masking_IP
;
;This code is called for each outgoing packet (normally to Internet)
;
mask_datagram:
	move.l	a3,a0				;prep to return same dgram if ok
	cmp.l	IPDG_hdr+IPHD_ip_src(a3),d5	;already masked ?
	beq	mask_exit			;branch if insane ;-)
	move.l	IPDG_pkt_data(a3),a2		;a2 -> protocol packet
	clr	d3				;preclear bits 8-15 of d3
	move.b	IPDG_hdr+IPHD_protocol(a3),d3	;d3 = protocol
	cmp	#P_ICMP,d3
	beq	mask_ICMP
	cmp	#P_TCP,d3
	beq.s	mask_TCP
	cmp	#P_UDP,d3
	beq.s	mask_UDP
mask_failed:
mask_ICMP_errmsg:	;PATCH move label to implement outgoing error messages
	IP_discard	(a3),#1
	addq		#1,prt_des_stat_dropped(a5)
	suba.l		a0,a0
	bra		mask_exit
;
mask_TCP:
	swap	d3				;d3 = protocol<<16
	move	tcph_src_port(a2),d3		;d3 = protocol.src_port
	move	tcph_dest_port(a2),d0		;d0 = remote port
	bsr	mask_common			;try to mask the packet
	bmi.s	mask_failed			;exit with failure on error
	move	d0,tcph_src_port(a2)		;mask TCP source port
post_fix_TCP:
	move	IPDG_pkt_length(a3),d2		;arg d2 =  TCP packet length
	move.l	a2,a0				;arg a0 -> TCP packet header
	move.l	IPDG_hdr+IPHD_ip_dest(a3),d1	;arg d1 =  dest IP
	move.l	IPDG_hdr+IPHD_ip_src(a3),d0	;arg d0 =  src  IP
	bsr	make_TCP_checksum		;calculate TCP checksum
	move	d0,tcph_chksum(a2)		;patch TCP checksum
	bra.s	post_mask_IP			;go calc & patch IP checksum
;
mask_UDP:
	move.l	IPDG_hdr+IPHD_ip_dest(a0),d0	;d0 = dest IP
	cmp.l	masq_port_info_ip(a5),d0	;masq info server dest IP ?
	bne.s	not_info_request
	cmp	#'MM',UDP_hdr_source_port(a2)	;masq master source port ?
	bne.s	not_info_request
	cmp	#'MI',UDP_hdr_dest_port(a2)	;masq info server dest port ?
	bne.s	not_info_request
	cmp	#8+8,UDP_hdr_length(a2)		;sufficient data length ?
	blt.s	not_info_request
	move.l	IPDG_hdr+IPHD_ip_src(a0),d0	;d0 = source IP
	cmp.l	sizeof_UDP_hdr(a2),d0		;data long confirms master IP ?
	bne.s	keep_set_master
have_info_request:
	move.l	d0,masq_port_master_ip(a5)	;set master IP = d0
keep_set_master:
	move.l	d0,IPDG_hdr+IPHD_ip_dest(a0)	;new packet dest = d0 (back to sender)
	move.l	masq_port_info_ip(a5),d0	;d0 = info server IP
	move.l	d0,IPDG_hdr+IPHD_ip_src(a0)	;new packet src = d0 (from info server)
	move	#'MM',UDP_hdr_dest_port(a2)	;new dest port = masq master port
	move	#'MI',UDP_hdr_source_port(a2)	;new source port = info server port
	move.l	d5,sizeof_UDP_hdr(a2)		;send back real_ISP_IP info
	move.l	masq_port_master_ip(a5),d0	;d0 = masq master IP
	move.l	d0,sizeof_UDP_hdr+4(a2)		;also send back MASTER_IP info
	bsr.s	post_mask_UDP			;patch checksums
	moveq	#E_ERROR,d0			;d0 = E_ERROR
	move.l	d0,a0				;a0 = E_ERROR for bounced info packet
	rts
;
not_info_request:
	swap	d3				;d3 = protocol<<16
	move	UDP_hdr_source_port(a2),d3	;d3 = protocol.src_port
	move	UDP_hdr_dest_port(a2),d0	;d0 = remote port
	bsr	mask_common			;try to mask the packet
	bmi	mask_failed			;exit with failure on error
	move	d0,UDP_hdr_source_port(a2)	;mask UDP source port
post_mask_UDP:
	move.l	a2,a0				;arg a0 -> UDP packet header
	move.l	IPDG_hdr+IPHD_ip_dest(a3),d1	;arg d1 =  dest IP
	move.l	IPDG_hdr+IPHD_ip_src(a3),d0	;arg d0 =  src  IP
	bsr	make_UDP_checksum		;calculate UDP checksum
	move	d0,UDP_hdr_checksum(a2)		;patch UDP checksum
post_mask_IP:
	move.l	a3,a0				;arg a0 -> masked datagram
	bsr	make_IP_check			;calculate IP checksum
	move	d0,IPDG_hdr+IPHD_hdr_chksum(a3)	;patch IP checksum
	rts
;
mask_ICMP:
	clr	d0				;preclear high bits
	move.b	ICMP_HD_type(a2),d0		;d0 = ICMP message type
	cmp	#max_ICMP_type,d0		;Unrecognized ?
	bhi	mask_failed			;strange type => error exit
	add	d0,d0				;make it a word index
	move	icmp_mask_t(pc,d0.w),d0		;get offset to function code
	jmp	icmp_mask_t(pc,d0.w)		;go mask dependent on ICMP type
;
icmp_mask_t:
	dc.w	mask_ICMP_reply-icmp_mask_t	;ICMP type  0
	dc.w	mask_failed-icmp_mask_t		;ICMP type  1
	dc.w	mask_failed-icmp_mask_t		;ICMP type  2
	dc.w	mask_ICMP_errmsg-icmp_mask_t	;ICMP type  3
;
	dc.w	mask_ICMP_errmsg-icmp_mask_t	;ICMP type  4
	dc.w	mask_ICMP_errmsg-icmp_mask_t	;ICMP type  5
	dc.w	mask_failed-icmp_mask_t		;ICMP type  6
	dc.w	mask_failed-icmp_mask_t		;ICMP type  7
;
	dc.w	mask_ICMP_request-icmp_mask_t	;ICMP type  8
	dc.w	mask_failed-icmp_mask_t		;ICMP type  9
	dc.w	mask_failed-icmp_mask_t		;ICMP type 10
	dc.w	mask_ICMP_errmsg-icmp_mask_t	;ICMP type 11
;
	dc.w	mask_ICMP_errmsg-icmp_mask_t	;ICMP type 12
	dc.w	mask_ICMP_request-icmp_mask_t	;ICMP type 13
	dc.w	mask_ICMP_reply-icmp_mask_t	;ICMP type 14
	dc.w	mask_ICMP_request-icmp_mask_t	;ICMP type 15
;
	dc.w	mask_ICMP_reply-icmp_mask_t	;ICMP type 16
	dc.w	mask_ICMP_request-icmp_mask_t	;ICMP type 17
	dc.w	mask_ICMP_reply-icmp_mask_t	;ICMP type 18
icmp_mask_t_end:
max_ICMP_type	=	((icmp_mask_t_end-icmp_mask_t)/2)-1
;
mask_ICMP_reply:
mask_ICMP_request:
	tst.b	ICMP_HD_code(a2)		;Code non_zero ?  (abnormal)
	bne	mask_failed			;weird code => error exit
	swap	d3				;d3 = protocol<<16
	move	ICMP_HD_id(a2),d3		;d3 = protocol.local_idfier
	clr	d0				;d0 = dummy remote id_fier
	bsr	mask_common			;try to mask the packet
	bmi	mask_failed			;exit with failure on error
	move	d0,ICMP_HD_id(a2)		;mask ICMP identifier
	move.l	a2,a0				;a0 -> protocol packet
	move	IPDG_pkt_length(a3),d0		;d0 -> protocol packet length
	bsr	make_ICMP_checksum		;calculate ICMP checksum
	move	d0,ICMP_HD_checksum(a2)		;patch ICMP checksum
	bra.s	post_mask_IP			;go calc & patch IP checksum
;
mask_exit:
	rts
;
;end of mask_datagram
;----------------------------------------------------------------------------
;IP_DGRAM *unmask_datagram(IP_DGRAM dgram);	/* returns NULL if dropped */
;
;NB: non-standard register assumptions:
;
;a5 -> my_port  a4 -> masq_port  a3 -> dgram  d5 = masking_IP
;
;This code is called for each incoming packet (normally from Internet)
;
unmask_datagram:
	move.l	IPDG_pkt_data(a3),a2		;a2 -> protocol packet
	clr	d3				;preclear bits 8-15 of d3
	move.b	IPDG_hdr+IPHD_protocol(a3),d3	;d3 = protocol
	cmp	#P_ICMP,d3
	beq	unmask_ICMP
	cmp	#P_TCP,d3
	beq.s	unmask_TCP
	cmp	#P_UDP,d3
	beq.s	unmask_UDP
unmask_failed:
	IP_discard	(a3),#1
	addq		#1,prt_des_stat_dropped(a5)
	suba.l		a0,a0			;null a0 to flag error
	bra		unmask_exit		;go return NULL
;
unmask_TCP:
	swap	d3				;d3 = protocol<<16
	move	tcph_dest_port(a2),d3		;d3 = protocol.dest_port
	move.l	IPDG_hdr+IPHD_ip_src(a3),d1	;d1 = remote_ip
	move	tcph_src_port(a2),d0		;d0 = remote port
	bsr	unmask_common
	bmi.s	unmask_failed			;exit with failure on error
	move	d0,tcph_dest_port(a2)		;unmask TCP destination port
	move	IPDG_pkt_length(a3),d2		;arg d2 =  TCP packet length
	move.l	a2,a0				;arg a0 -> TCP packet header
	move.l	IPDG_hdr+IPHD_ip_dest(a3),d1	;arg d1 =  dest IP
	move.l	IPDG_hdr+IPHD_ip_src(a3),d0	;arg d0 =  src  IP
	bsr	make_TCP_checksum		;calculate TCP checksum
	move	d0,tcph_chksum(a2)		;patch TCP checksum
	bra.s	post_unmask_IP			;go calc & patch IP checksum
;
unmask_UDP:
	swap	d3				;d3 = protocol<<16
	move	UDP_hdr_dest_port(a2),d3	;d3 = protocol.dest_port
	move.l	IPDG_hdr+IPHD_ip_src(a3),d1	;d1 = remote_ip
	move	UDP_hdr_source_port(a2),d0	;d0 = remote port
	bsr	unmask_common
	bmi.s	unmask_failed			;exit with failure on error
	move	d0,UDP_hdr_dest_port(a2)	;unmask UDP destination port
	move.l	a2,a0				;arg a0 -> UDP packet header
	move.l	IPDG_hdr+IPHD_ip_dest(a3),d1	;arg d1 =  dest IP
	move.l	IPDG_hdr+IPHD_ip_src(a3),d0	;arg d0 =  src  IP
	bsr	make_UDP_checksum		;calculate UDP checksum
	move	d0,UDP_hdr_checksum(a2)		;patch UDP checksum
post_unmask_IP:
	move.l	a3,a0				;arg a0 -> masked datagram
	bsr	make_IP_check			;calculate IP checksum
	move	d0,IPDG_hdr+IPHD_hdr_chksum(a0)	;patch IP checksum
	rts
;
unmask_ICMP:
	clr	d0				;preclear high bits
	move.b	ICMP_HD_type(a2),d0		;d0 = ICMP message type
	cmp	#max_ICMP_type,d0		;Unrecognized ?
	bhi	unmask_failed			;strange type => error exit
	add	d0,d0				;make it a word index
	move	icmp_unmask_t(pc,d0.w),d0	;get offset to function code
	jmp	icmp_unmask_t(pc,d0.w)		;go unmask dependent on ICMP type
;
icmp_unmask_t:
	dc.w	unmask_ICMP_reply-icmp_unmask_t	;ICMP type  0
	dc.w	unmask_failed-icmp_unmask_t		;ICMP type  1
	dc.w	unmask_failed-icmp_unmask_t		;ICMP type  2
	dc.w	unmask_ICMP_errmsg-icmp_unmask_t	;ICMP type  3
;
	dc.w	unmask_ICMP_errmsg-icmp_unmask_t	;ICMP type  4
	dc.w	unmask_ICMP_errmsg-icmp_unmask_t	;ICMP type  5
	dc.w	unmask_failed-icmp_unmask_t		;ICMP type  6
	dc.w	unmask_failed-icmp_unmask_t		;ICMP type  7
;
	dc.w	unmask_ICMP_request-icmp_unmask_t	;ICMP type  8
	dc.w	unmask_failed-icmp_unmask_t		;ICMP type  9
	dc.w	unmask_failed-icmp_unmask_t		;ICMP type 10
	dc.w	unmask_ICMP_errmsg-icmp_unmask_t	;ICMP type 11
;
	dc.w	unmask_ICMP_errmsg-icmp_unmask_t	;ICMP type 12
	dc.w	unmask_ICMP_request-icmp_unmask_t	;ICMP type 13
	dc.w	unmask_ICMP_reply-icmp_unmask_t		;ICMP type 14
	dc.w	unmask_ICMP_request-icmp_unmask_t	;ICMP type 15
;
	dc.w	unmask_ICMP_reply-icmp_unmask_t		;ICMP type 16
	dc.w	unmask_ICMP_request-icmp_unmask_t	;ICMP type 17
	dc.w	unmask_ICMP_reply-icmp_unmask_t		;ICMP type 18
icmp_unmask_t_end:
tst_max_ICMP_type	=	((icmp_unmask_t_end-icmp_unmask_t)/2)-1
;
	IFNE	(tst_max_ICMP_type-max_ICMP_type)
	FAIL	ICMP mask & unmask tables differ in size !!!
	ENDC
;
unmask_ICMP_errmsg:
	lea	ICMP_data(a2),a1	;a1-> IP header of erring packet
	move.b	IPHD_verlen_f(a1),d0	;d0 = IP header byte with header length
	and	#amask_IPHD_f_hd_len,d0	;d0 = header length in longwords
	asl	#2,d0			;d0 = header length in bytes
	lea	(a1,d0.w),a0		;a0-> high level header of erring packet
	move.b	IPHD_protocol(a1),d3	;d3 = protocol of erring packet
	cmp	#P_TCP,d3
	beq.s	unmask_TCP_errmsg
	cmp	#P_UDP,d3
	beq.s	unmask_UDP_errmsg
	bra	unmask_failed		;return with error on strange protocols
;
unmask_TCP_errmsg:
	swap	d3			;d3 = protocol<<16
	move	tcph_src_port(a0),d3	;d3 = protocol.local_port
	move.l	IPHD_ip_dest(a1),d1	;d1 = remote_ip of TCP connection
	move	tcph_dest_port(a0),d0	;d0 = remote port
	bsr	unmask_common		;try to unmask the packet
	bmi	unmask_failed		;exit with failure on error
	lea	ICMP_data(a2),a1	;a1-> IP header of erring packet
	move.b	IPHD_verlen_f(a1),d1	;d1 = IP header byte with header length
	and	#amask_IPHD_f_hd_len,d1	;d1 = header length in longwords
	asl	#2,d1			;d1 = header length in bytes
	lea	(a1,d1.w),a0		;a0-> high level header of erring packet
	move	d0,tcph_src_port(a0)	;unmask local port of erring packet
	bra.s	post_unmask_errmsg	;fix err IP, ICMP, main IP checksums
;
unmask_UDP_errmsg:
	swap	d3				;d3 = protocol<<16
	move	UDP_hdr_source_port(a0),d3	;d3 = protocol.local_port
	move.l	IPHD_ip_dest(a1),d1		;d1 = remote_ip of UDP packet
	move	UDP_hdr_dest_port(a0),d0	;d0 = remote port
	bsr	unmask_common			;try to unmask the packet
	bmi	unmask_failed		;exit with failure on error
	lea	ICMP_data(a2),a1	;a1-> IP header of erring packet
	move.b	IPHD_verlen_f(a1),d1	;d1 = IP header byte with header length
	and	#amask_IPHD_f_hd_len,d1	;d1 = header length in longwords
	asl	#2,d1			;d1 = header length in bytes
	lea	(a1,d1.w),a0		;a0-> high level header of erring packet
	move	d0,UDP_hdr_source_port(a0)	;unmask local port of erring pkt
	clr	UDP_hdr_checksum(a0)	;invalidate UDP checksum
post_unmask_errmsg:
	move.l	IPDG_hdr+IPHD_ip_dest(a3),IPHD_ip_src(a1)	;loc IP in msg
	move.l	a1,a0			;a0-> IP header
	clr.l	d0			;d0=0 as start_value
	bsr	calc_checksum		;calc checksum of erring IP header
	move	d0,IPHD_hdr_chksum(a0)	;patch IP checksum of erring packet
;
	move.l	a2,a0			;a0 -> protocol packet
	move	IPDG_pkt_length(a3),d0	;d0 -> protocol packet length
	bsr	make_ICMP_checksum	;calculate ICMP checksum
	move	d0,ICMP_HD_checksum(a2)	;patch ICMP checksum
;
	bra	post_unmask_IP		;go calc & patch main IP checksum
;
unmask_ICMP_request:
unmask_ICMP_reply:
	tst.b	ICMP_HD_code(a2)		;Code non_zero ?  (abnormal)
	bne	mask_failed			;weird code => error exit
	swap	d3				;d3 = protocol<<16
	move	ICMP_HD_id(a2),d3		;d3 = protocol.masked_idfier
	move.l	IPDG_hdr+IPHD_ip_src(a3),d1	;d1 = remote_ip
	clr	d0				;d0 = dummy remote_idfier
	bsr	unmask_common			;try to unmask the packet
	bmi	unmask_failed			;exit with failure on error
	move	d0,ICMP_HD_id(a2)		;unmask ICMP identifier
	move.l	a2,a0				;a0 -> protocol packet
	move	IPDG_pkt_length(a3),d0		;d0 -> protocol packet length
	bsr	make_ICMP_checksum		;calculate ICMP checksum
	move	d0,ICMP_HD_checksum(a2)		;patch ICMP checksum
	bra	post_unmask_IP			;go calc & patch IP checksum
;
unmask_exit:
	rts					;return result to caller
;
;end of unmask_datagram
;----------------------------------------------------------------------------
;uint16	mask_common();
;
;NB: non-standard register assumptions:
;
;a5 -> my_port  a4 -> masq_port  a3 -> dgram
;d5 = real_ISP_IP  d4 = master_IP  d3 = protocol.source_port  d0 = remote_port
;
;Returns:	a0 -> masking struct  d0 = masked local port (-1 if not found)
;-------------------------------------
mask_common:
	lv_init		sp,vars			;use sp for local variables
	lv_var.l	remote_ip
	lv_var.w	remote_port
;-------
	move		d0,remote_port(sp)			;init var remote_port
	move.l		IPDG_hdr+IPHD_ip_dest(a3),remote_ip(sp)	;init var remote_ip
;-------
	tst		masq_count
	beq.s		mask_allocate
	move.l		IPDG_hdr+IPHD_ip_src(a3),d2	;d2 =  src  IP
	move.l		masq_root_p(pc),a0		;a0 -> root
	lea		masq_root_p-msq_next(pc),a1	;trick !
	bra.s		mask_search
;-------------------------------------
mask_search_loop:
	move.l		msq_next(a0),d0
	beq.s		mask_unfound
	move.l		a0,a1			;a1 -> tested entry
	move.l		d0,a0			;a0 -> next entry
mask_search:
	cmp.l		msq_proto_port(a0),d3		;correct local port & protocol ?
	bne.s		mask_search_loop		;if not, loop on
	cmp.l		msq_local_ip(a0),d2		;correct local IP ?
	bne.s		mask_search_loop		;if wrong local IP, loop on
	move.l		remote_ip(sp),d0		;d0 = remote IP
	cmp.l		msq_remote_ip(a0),d0		;same as found ?
	bne.s		mask_search_loop		;if wrong remote IP, loop on
	move		remote_port(sp),d0		;d0 = remote port
	cmp		msq_remote_port(a0),d0		;same as found ?
	bne.s		mask_search_loop		;if wrong remote port, loop on
mask_found:
	move.l		msq_next(a0),msq_next(a1)	;unlink entry from queue
	move.l		masq_root_p(pc),msq_next(a0)	;link it to queue root
	move.l		a0,masq_root_p			;make this entry new root
	move		msq_mask_port(a0),d0		;d0 = masking port number
mask_ok:
	move.l		d5,IPDG_hdr+IPHD_ip_src(a3)	;mask src IP
	and.l		#$FFFF,d0		;return positive on success
	bra		mask_common_exit
;-------------------------------------
mask_unfound:
	movem.l		a0-a1,masq_temp_2L	;save regs from search end
	cmp		#(1<<MASQ_POWER),masq_count	;room for more entries ?
	blo.s		mask_allocate		;if room, go allocate new entry
mask_reallocate:
	clr.l		msq_next(a1)		;unlink oldest entry from queue
	bra.s		mask_linkup		;go reuse it for new entry
;-------------------------------------
mask_allocate:
	KRmalloc	#sizeof_msq_entry	;allocate masq entry
	tst.l		d0
	ble.s		mask_emergency		;emergency on allocation failure
	move.l		d0,a0			;a0 -> new entry
	move		masq_count(pc),msq_index(a0)
	addq		#1,masq_count
mask_linkup:
	lea		masq_root_p(pc),a1	;(a1) -> recent entry or is NULL
	move.l		(a1),msq_next(a0)	;link new entry as root of chain
	move.l		a0,(a1)			;store new entry as root
;-------
	move.l		IPDG_hdr+IPHD_ip_src(a3),d2		;d2 = local IP
	move.l		d2,msq_local_ip(a0)			;init msq_local_ip
	move.l		d3,msq_proto_port(a0)			;init msq_protocol & msq_local_port
	move.l		remote_ip(sp),msq_remote_ip(a0)		;init msq_remote_ip
	move		remote_port(sp),msq_remote_port(a0)	;init msq_remote_port
;-------
	cmp.l		d2,d4			;master machine ?
	bne.s		mask_interLAN		;else mask interLAN machine
	cmp		#MASQ_START,d3		;port below mask range ?
	blo.s		mask_master		;then go mask as master (keep port)
	cmp		#MASQ_LIMIT,d3		;port above mask range ?
	bhs.s		mask_master		;then go mask as master (keep port)
mask_interLAN:
	move		msq_index(a0),d0	;d0 = unique index
	add		#MASQ_START,d0		;d0 = masked source port
	move		d0,msq_mask_port(a0)	;store as mask_port in struct
	bra		mask_ok			;go mask IP and exit positive
;-------------------------------------
mask_master:
	move		d3,d0			;d0 = local port number
	move		d0,msq_mask_port(a0)	;store as mask_port in struct
	bra		mask_ok			;go mask IP and exit positive
;-------------------------------------
mask_emergency:					;reached at allocation error
	cmp		#1,masq_count		;has it worked some times ?
	blo.s		mask_error		;abort if it just doesn't work
	movem.l		masq_temp_2L(pc),a0-a1	;restore regs from search end
	bra		mask_reallocate
;-------------------------------------
mask_error:
	moveq		#E_ERROR,d0		;return negative on failure
mask_common_exit:
	lv_exit		sp,vars			;end scope of local variables
	rts
;-------------------------------------
;end of mask_common
;----------------------------------------------------------------------------
;uint16	unmask_common();
;
;NB: non-standard register assumptions:
;
;a5 -> my_port  a4 -> masq_port  a3 -> dgram
;d5 = masking_IP  d4 = master_IP  d3 = protocol.dest_port  d0 = remote_port
;
unmask_common:
	lv_init		sp,vars			;use sp for local variables
	lv_var.l	remote_ip
	lv_var.w	remote_port
;-------
	move		d0,remote_port(sp)	;init var remote_port
	move.l		d1,remote_ip(sp)	;init var remote_ip
;-------
	tst		masq_count
	beq.s		unmask_allocate
	move.l		remote_ip(sp),d2		;d2 = remote IP
	move.l		masq_root_p(pc),a0		;a0 -> root
	lea		masq_root_p-msq_next(pc),a1	;trick !
	bra.s		unmask_search
;-------------------------------------
unmask_search_loop:
	move.l		msq_next(a0),d0		;d0 -> current entry or is NULL
	beq.s		unmask_unfound		;break search at queue end
	move.l		a0,a1			;a1 -> previous entry
	move.l		d0,a0			;a0 -> current entry
unmask_search:
	cmp.l		msq_remote_ip(a0),d2		;correct remote IP ?
	bne.s		unmask_search_loop		;if wrong remote IP, loop on
	move		remote_port(sp),d0		;d0 = remote port
	cmp		msq_remote_port(a0),d0		;same as found ?
	bne.s		unmask_search_loop		;if wrong remote port, loop on
	cmp		msq_mask_port(a0),d3		;found masking port
	bne.s		unmask_search_loop		;if wrong masking port, loop on
	move.l		d3,d0				;d0 = protocol.mask_port
	swap		d0				;d0 = mask_port.protocol
	cmp		msq_protocol(a0),d0		;correct protocol ?
	bne.s		unmask_search_loop		;if wrong protocol, loop on
unmask_found:
	move.l		msq_next(a0),msq_next(a1)	;unlink entry from queue
	move.l		masq_root_p(pc),msq_next(a0)	;link it to queue root
	move.l		a0,masq_root_p			;make this entry new root
unmask_ok:
	move.l		msq_local_ip(a0),IPDG_hdr+IPHD_ip_dest(a3)	;unmask dest IP
	move		msq_local_port(a0),d0
	and.l		#$FFFF,d0			;return positive on success
	bra.s		unmask_common_exit
;-------------------------------------
unmask_unfound:
	movem.l		a0-a1,masq_temp_2L		;save regs from search end
	cmp		#(1<<MASQ_POWER),masq_count	;room for more entries ?
	blo.s		unmask_allocate			;if room, go allocate new entry
unmask_reallocate:
	clr.l		msq_next(a1)			;unlink oldest entry from queue
	bra.s		unmask_linkup			;go reuse it for new entry
;-------------------------------------
unmask_allocate:
	KRmalloc	#sizeof_msq_entry		;allocate masq entry
	tst.l		d0				;success ?
	ble.s		unmask_emergency		;emergency on allocation failure
	move.l		d0,a0				;a0 -> new entry
	move		masq_count(pc),msq_index(a0)	;init msq_index
	addq		#1,masq_count			;increment entry count
unmask_linkup:
	lea		masq_root_p(pc),a1	;(a1) -> recent entry or is NULL
	move.l		(a1),msq_next(a0)	;link new entry as root of chain
	move.l		a0,(a1)			;store new entry as root
;-------
	move.l		remote_ip(sp),msq_remote_ip(a0)		;init msq_remote_ip
	move		remote_port(sp),msq_remote_port(a0)	;init msq_remote_port
	move.l		d4,msq_local_ip(a0)			;init msq_local_ip = master_IP
	move.l		d3,msq_proto_port(a0)			;init msq_protocol & msq_local_port
;-------
	move		d3,d0				;d0 = port number
	move		d0,msq_mask_port(a0)		;store as mask_port in struct
	bra.s		unmask_ok			;go mask IP and exit positive
;-------------------------------------
unmask_emergency:				;reached at allocation error
	cmp		#1,masq_count		;has it worked some times ?
	blo.s		unmask_error		;abort if it just doesn't work
	movem.l		masq_temp_2L(pc),a0-a1	;restore regs from search end
	bra.s		unmask_reallocate
;-------------------------------------
unmask_error:
	moveq		#E_ERROR,d0		;return negative on failure
unmask_common_exit:
	lv_exit		sp,vars			;end scope of local variables
	rts
;-------------------------------------
;end of unmask_common
;----------------------------------------------------------------------------
;Here follows eight checksum functions that share most of their code.
;All of these smash d1/d2/a0 and preserve all other regs except d0 (result).
;
;uint16	make_TCP_checksum (uint32 src, uint32 dest, uint16 *blk, uint16 len);
;uint16 make_UDP_checksum (uint32 src, uint32 dest, UDP_HDR *udp_packet);
;uint16	make_ICMP_checksum (uint16 *blk, uint16 len);
;	make_*_checksum calls preclear original checksum before calculation
;
;uint16	calc_TCP_checksum (uint32 src, uint32 dest, uint16 *blk, uint16 len);
;uint16 calc_UDP_checksum (uint32 src, uint32 dest, UDP_HDR *udp_packet);
;uint16	calc_ICMP_checksum (uint16 *blk, uint16 len);
;	calc_*_checksum calls do the same calculations without preclearing
;
;uint16	calc_proto_checksum (uint32 src, uint32 dest, uint16 *blk, uint32 proto_len);
;	calc_proto_checksum is used for UDP and TCP in the above functions
;
;uint16	calc_checksum(uint16 start_value, uint16 *blk, uint16 len);
;	calc_checksum calculates a block checksum regardless of protocol
;	it is used for the data block summing of the other seven functions
;
make_ICMP_checksum:
	clr	ICMP_HD_checksum(a0)	;clear checksum in block
calc_ICMP_checksum:
	move	d0,d1			;d1 = length	(for top bits)
	clr.l	d0			;preclear checksum accumulator
	bra.s	calc_checksum		;add this checksum to block checksum
;
make_UDP_checksum:
	clr	UDP_hdr_checksum(a0)	;clear checksum in packet
calc_UDP_checksum:
	move.l	#P_UDP<<16,d2		;d2 = protocol.zero
	move	UDP_hdr_length(a0),d2	;d2 = protocol.length
	bra.s	calc_proto_checksum
;
make_TCP_checksum:
	clr	tcph_chksum(a0)	;clear checksum in block
calc_TCP_checksum:
	swap	d2		;d2 = length.garbage
	move	#P_TCP,d2	;d2 = length.protocol
	swap	d2		;d2 = protocol.length
calc_proto_checksum:
	add	d0,d1		;X<<16+d1.lo = sum of low IP words
	clr	d0		;d0 = src_ip & 0xFFFF0000
	swap	d0		;d0 = src_ip.hi
	addx	d1,d0		;X<<16+d0.lo = sum of src_IP and dest_IP.lo
	clr	d1		;d1 = dest_ip & 0xFFFF0000
	swap	d1		;d1.lo = dest_ip.hi, d1.hi = 0
	addx.l	d1,d0		;d0 = checksum of both IPs
;
	clr.l	d1		;preclear top bits of d1
	move	d2,d1		;d1 = length
	add.l	d1,d0		;add length word to checksum
	clr	d2		;d2 = protocol.zero
	swap	d2		;d2 = protocol
	add.l	d2,d0		;add protocol word to checksum
;
calc_checksum:
	clr.l	d2			;clear top bits of temp reg
	and.l	#$FFFF,d1		;clear top bits of length count
	ror.l	#1,d1			;d1 = length/2 + (length & 1)<<31
	bra.s	.next_full_word
;
.loop:
	move	(a0)+,d2		;d2.lo = block word, d2.hi remains 0
	add.l	d2,d0			;add block word to checksum
.next_full_word:
	dbra	d1,.loop		;loop back to add all full words
.summed_words:
	tst.l	d1			;odd length ?
	bpl.s	.summed_all		;skip if no byte remains to be summed
	move.b	(a0)+,d2		;d2 = final byte
	asl	#8,d2			;d0 = final byte << 8
	add.l	d2,d0			;add padded final byte to checksum
.summed_all:
;
	move	d0,d2			;d2 = prel_checksum.lo
	clr	d0
	swap	d0			;d0 = prel_checksum.hi
	add	d2,d0			;X+d0.lo = checksum
	clr	d2
	addx	d2,d0			;d0 = final checksum folded to 16 bits
	not	d0			;invert these bits for return value
	rts				;return to caller
;
;end of calc_checksum and seven other related checksum functions
;----------------------------------------------------------------------------
;uint16 make_IP_check (IP_DGRAM *datagram);
;uint16 calc_IP_check (IP_DGRAM *datagram);
;
;make_IP_check clears the checksum in the IP header before calculation
;calc_IP_check just calculates a checksum including the old one in IP header
;	neither stores the new sum anywhere, they just return it to caller.
;	both smash d1/d2/a1 and preserve all other regs except d0 (result).
;
make_IP_check:
	clr	IPDG_hdr+IPHD_hdr_chksum(a0)
calc_IP_check:
	lea	IPDG_hdr(a0),a1			;a1 -> header
	move.l	(a1)+,d0			;check header bytes  0-3
	move.l	(a1)+,d1			;fetch header bytes  4-7
	add.l	d1,d0				;check header bytes  4-7
	move.l	(a1)+,d1			;fetch header bytes  8-11
	addx.l	d1,d0				;check header bytes  8-11
	move.l	(a1)+,d1			;fetch header bytes 12-15
	addx.l	d1,d0				;check header bytes 12-15
	move.l	(a1)+,d1			;fetch header bytes 16-19
	addx.l	d1,d0				;check header bytes 16-19
;
	move	IPDG_opt_length(a0),d2		;d2 = option bytes
	beq.s	.options_checked		;skip options if absent
	roxr	#1,d1				;save X bit in d1
	addq	#3,d2				;prep to round d2
	lsr	#2,d2				;d2 = option longs
	subq	#1,d2				;d2 = fixed for dbra
	roxl	#1,d1				;restore X bit from d1
	movea.l	IPDG_options(a0),a1		;a1 -> options
.option_loop:					;loop start
	move.l	(a1)+,d1				;fetch a long	
	addx.l	d1,d0					;check a long
	dbra	d2,.option_loop			;loop back for all option longs
;
.options_checked:
	move	d0,d1				;d1.lo = sum & 0xFFFF
	clr	d0				;d0.lo = 0  (X bit unaffected)
	swap	d0				;d0.hi = 0,  d0.lo = sum >> 16
	addx	d1,d0				;fold sum to 16 bits
	clr	d1				;d1.lo = 0  (X bit unaffected)
	addx	d1,d0				;add X bit (can give carry)
	addx	d1,d0				;do it again (no carry possible)
	not	d0				;invert the 16 bit checksum
	rts
;
;end of make_IP_check & calc_IP_check
;----------------------------------------------------------------------------
;char *find_masq_port();
;
find_masq_port:
	getvstr		MASQ_PORT_vn_s(pc)
	move.l		d0,a0
	move.l		d0,masq_port_name_p
	ble.s		.exit
	link		a6,#-4
	query_chains	-4(a6),0.w,0.w
	move.l		-4(a6),a0		;a0->ports
	unlk		a6
	bra.s		.test_port
;
.test_name:
	move.l		prt_des_name(a0),a1
	move.l		masq_port_name_p(pc),a2
	str_comp	a1,a2
	beq.s		.exit
	move.l		prt_des_next(a0),a0
.test_port:
	move.l		a0,d0
	bgt.s		.test_name
.exit:
	tst.l		d0
	rts
;
;end of find_masq_port
;----------------------------------------------------------------------------
;End of:	Resident functions and subroutines
;----------------------------------------------------------------------------
;	Resident library function code will be expanded here
;
	make	JAR_links
	make	DOMAIN_links
;----------------------------------------------------------------------------
resident_end:	;all beyond this point will be released in going resident
resident_size	=	resident_end-text_start+$100
;----------------------------------------------------------------------------
;Start of:	STX Non-resident initialization code with tests
;----------------------------------------------------------------------------
start_1:
	move.l		a0,d0
	sne		d7
	bne.s		.have_basepage
	move.l		4(sp),d0
.have_basepage:
	move.l		d0,a5
	lea		mystack(pc),sp
	move.l		a5,basepage_p
	tst.b		d7
	bne		.ACC_launch
	gemdos		Mshrink,#0,(a5),#initial_size
	lea		bp_arglen(a5),a0
	lea		STinG_Load_s(pc),a1
	str_comp	a0,a1
	bne		.bad_launch
;
	gemdos		Super,0.w
	move.l		d0,d4
	eval_cookie	#"STiK"
	move.l		d0,d3				;d3 = d0 -> DRV_LIST structure
	gemdos		Super|_ind,d4
;
	move.l		d3,sting_drivers		;sting_drivers -> DRV_LIST structure
	ble		.STiK_not_found
	move.l		d3,a3				;a3 -> DRV_LIST structure
	lea		DRV_LIST_magic(a3),a0
	lea		STiKmagic_s(pc),a1
	moveq		#10-1,d0
.strcmp_loop:					;loop to test STiKmagic of DRV_LIST
	cmpm.b		(a0)+,(a1)+
	dbne		d0,.strcmp_loop
	bne		.STiK_not_valid
;
	move.l		DRV_LIST_get_dftab(a3),a0	;a0 -> get_dftab function
	pea		TRANSPORT_DRIVER_s		;-(sp) = "TRANSPORT_TCPIP"
	jsr		(a0)				;call get_dftab
	addq		#4,sp
	move.l		d0,tpl				;store pointer in 'tpl'
	ble		.driver_not_valid
;
	move.l		DRV_LIST_get_dftab(a3),a0	;a0 -> get_dftab function
	pea		MODULE_DRIVER_s			;-(sp) = "MODULE_LAYER"
	jsr		(a0)				;call get_dftab
	addq		#4,sp
	move.l		d0,stx				;store pointer in 'tpl'
	ble		.layer_not_valid
.install:
	link		a6,#-8
	query_chains	-8(a6),-4(a6),0.w
	movem.l		-8(a6),a3/a4			;a3->ports  a4->drivers
	unlk		a6
;
	move.l		a3,d0
	beq.s		.bad_port_list
.port_loop:
	move.l		d0,a3
	move.l		prt_des_next(a3),d0
	bne.s		.port_loop
;
	move.l		a4,d0
	beq.s		.bad_driver_list
.driver_loop:
	move.l		d0,a4
	move.l		drv_des_next(a4),d0
	bne.s		.driver_loop
;
.final_install:
	move.l		#my_port,prt_des_next(a3)
	move.l		#my_driver,drv_des_next(a4)
;
	gemdos		Ptermres,#resident_size,#0
;-------------------------------------
.ACC_launch:
	lea		ACC_launch_s(pc),a0
	bsr.s		report_error
.loop:
	bra.s		.loop
;-------------------------------------
.bad_launch:
	lea		bad_launch_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
.bad_port_list:
	lea		bad_port_list_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
.bad_driver_list:
	lea		bad_driver_list_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
.STiK_not_found:
	lea		STiK_not_found_s,a0
	bra.s		.error_exit
;-------------------------------------
.STiK_not_valid:
	lea		STiK_not_valid_s,a0
	bra.s		.error_exit
;-------------------------------------
.driver_not_valid:
	lea		driver_not_valid_s,a0
	bra.s		.error_exit
;-------------------------------------
.layer_not_valid:
	lea		layer_not_valid_s(pc),a0
.error_exit:
	bsr.s		report_error
	gemdos		Pterm,#E_ERROR
;-------------------------------------
report_error:
	move.l		a0,-(sp)
	lea		error_title_s(pc),a0
	bsr.s		Cconws_sub
	move.l		(sp)+,a0
	bsr.s		Cconws_sub
	lea		error_tail_s(pc),a0
Cconws_sub:
	gemdos		Cconws,(a0)
	rts
;----------------------------------------------------------------------------
;End of:	STX Non-resident initialization code with tests
;----------------------------------------------------------------------------
;	Non-resident library function code will be expanded here
;
	make	JAR_links
	make	DOMAIN_links
;----------------------------------------------------------------------------
text_limit:
text_size	= text_limit-text_start
	SECTION	DATA
data_start:
;----------------------------------------------------------------------------
STinG_Load_s:
	dc.b	10,'STinG_Load',NUL
STiKmagic_s:
	dc.b	'STiKmagic',NUL
TRANSPORT_DRIVER_s:
	dc.b	'TRANSPORT_TCPIP',NUL
MODULE_DRIVER_s:
	dc.b	'MODULE_LAYER',NUL
;
ACC_launch_s:
	dc.b	'This non-ACC, was launched as an ACC,',CR,LF
	dc.b	'so now you must reset the computer !',CR,LF
	dc.b	'I am looping forever to avoid damage',CR,LF
	dc.b	'that could occur if I try to exit !',CR,LF
	dc.b	NUL
;
bad_launch_s:	
	dc.b	'This STX should only be launched by',CR,LF
	dc.b	'STinG, or another TCP/IP stack with',CR,LF
	dc.b	'a compatible module interface !',CR,LF
	dc.b	NUL
;
bad_port_list_s:
	dc.b	'The list chain of STinG ports was',CR,LF
	dc.b	'not found...!',CR,LF
	dc.b	NUL
;
bad_driver_list_s:
	dc.b	'The list chain of STinG drivers was',CR,LF
	dc.b	'not found...!',CR,LF
	dc.b	NUL
;
STiK_not_found_s:
	dc.b	'There is no STiK cookie in the jar !',CR,LF
	dc.b	NUL
;
STiK_not_valid_s:
	dc.b	'The STiK cookie data is corrupt !',CR,LF
	dc.b	NUL
;
driver_not_valid_s:
	dc.b	'The main STinG driver is not valid !',CR,LF
	dc.b	NUL
;
layer_not_valid_s:
	dc.b	'The STinG module layer is not valid !',CR,LF
	dc.b	NUL
;
error_title_s:
	dc.b	BEL,CR,LF
	dc.b	'------------'
	dc.b	' '
	M_TITLE
	dc.b	' '
	M_VERSION
	dc.b	'------------',CR,LF
	dc.b	NUL
;
error_tail_s:
	dc.b	BEL,CR,LF,NUL
;----------------------------------------------------------------------------
data_limit:
data_size	=	data_limit-data_start
	SECTION	BSS
bss_start:
;----------------------------------------------------------------------------
		ds.l	200		;subroutine stack >= 100 longs
mystack:	ds.l	1		;top of subroutine stack
;----------------------------------------------------------------------------
bss_limit:
bss_size	=	bss_limit-bss_start
;----------------------------------------------------------------------------
initial_size	=	text_size+data_size+bss_size+$100
;----------------------------------------------------------------------------
		END
;----------------------------------------------------------------------------
;End of file:	MASQUE.S
;----------------------------------------------------------------------------
