/*
Copyright 2001-2022 John Wiseman G8BPQ

This file is part of LinBPQ/BPQ32.

LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

LinBPQ/BPQ32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with LinBPQ/BPQ32.  If not, see http://www.gnu.org/licenses
*/	

//
//	INP3 Suport Code for BPQ32 Switch
//

//	All code runs from the BPQ32 Received or Timer Routines under Semaphore.


#define _CRT_SECURE_NO_DEPRECATE 

#pragma data_seg("_BPQDATA")

#include "CHeaders.h"

#include "time.h"
#include "stdio.h"
#include <fcntl.h>					 
//#include "vmm.h"

static VOID SendNetFrame(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Frame)
{
	// INP3 should only ever send over an active link, so just queue the message
	
	if (Route->NEIGHBOUR_LINK)
		C_Q_ADD(&Route->NEIGHBOUR_LINK->TX_Q, Frame);
	else
		ReleaseBuffer(Frame);
}


typedef struct _RTTMSG
{
	UCHAR ID[7];
	UCHAR TXTIME[11];
	UCHAR SMOOTHEDRTT[11];
	UCHAR LASTRTT[11];
	UCHAR POINTER[11];
	UCHAR ALIAS[7];
	UCHAR VERSION[12];
	UCHAR SWVERSION[9];
	UCHAR FLAGS[10];
	UCHAR PADDING[147];

} RTTMSG;

extern int COUNTNODES();

VOID __cdecl Debugprintf(const char * format, ...);

VOID SendINP3RIF(struct ROUTE * Route, UCHAR * Call, UCHAR * Alias, int Hops, int RTT);
VOID SendOurRIF(struct ROUTE * Route);
VOID UpdateNode(struct ROUTE * Route, UCHAR * axcall, UCHAR * alias, int  hops, int rtt);
VOID UpdateRoute(struct DEST_LIST * Dest, struct DEST_ROUTE_ENTRY * ROUTEPTR, int  hops, int rtt);
VOID KillRoute(struct DEST_ROUTE_ENTRY * ROUTEPTR);
VOID AddHere(struct DEST_ROUTE_ENTRY * ROUTEPTR,struct ROUTE * Route , int  hops, int rtt);
VOID SendRIPToNeighbour(struct ROUTE * Route);
VOID DecayNETROMRoutes(struct ROUTE * Route);
VOID DeleteINP3Routes(struct ROUTE * Route);
BOOL L2SETUPCROSSLINKEX(PROUTE ROUTE, int Retries);

//#define NOINP3

struct _RTTMSG RTTMsg = {""};

//struct ROUTE DummyRoute = {"","",""};

int RIPTimerCount = 0;				// 1 sec to 10 sec counter
int PosTimerCount = 0;				// 1 sec to 5 Mins counter

// Timer Runs every 10 Secs

extern int MAXRTT;			// 90 secs
extern int MaxHops;

extern int RTTInterval;			// 4 Minutes
int RTTRetries = 2;
int RTTTimeout = 6;				// 1 Min (Horizon is 1 min)

VOID InitialiseRTT()
{
	UCHAR temp[sizeof(RTTMsg.FLAGS) + 4];

	memset(&RTTMsg, ' ', sizeof(struct _RTTMSG));
	memcpy(RTTMsg.ID, "L3RTT: ", 7);
	memcpy(RTTMsg.VERSION, "LEVEL3_V2.1 ", 12);
	memcpy(RTTMsg.SWVERSION, "BPQ32001 ", 9);
	_snprintf(temp, sizeof(temp), "$M%d $N      ", MAXRTT); // trailing spaces extend to ensure padding if the length of characters for MAXRTT changes.
	memcpy(RTTMsg.FLAGS, temp, 10);                 // But still limit the actual characters copied.
	memcpy(RTTMsg.ALIAS, &MYALIASTEXT, 6);
	RTTMsg.ALIAS[6] = ' ';
}

VOID TellINP3LinkGone(struct ROUTE * Route)
{
	struct DEST_LIST * Dest =  DESTS;
	char call[11]="";

	ConvFromAX25(Route->NEIGHBOUR_CALL, call);
	Debugprintf("BPQ32 L2 Link to Neighbour %s lost", call);

	if (Route->NEIGHBOUR_LINK)
		Debugprintf("BPQ32 Neighbour_Link not cleared");


	if (Route->INP3Node == 0)
		DecayNETROMRoutes(Route);
	else
		DeleteINP3Routes(Route);
}

VOID DeleteINP3Routes(struct ROUTE * Route)
{
	int i;
	struct DEST_LIST * Dest =  DESTS;

	// Delete any NETROM Dest entries via this Route

	Route->SRTT = 0;
	Route->RTT = 0;
	Route->BCTimer = 0;
	Route->Status = 0;
	Route->Timeout = 0;

	Dest--;

	// Delete any Dest entries via this Route

	for (i=0; i < MAXDESTS; i++)
	{
		Dest++;

		if (Dest->DEST_CALL[0] == 0)
			continue;										// Spare Entry

		if (Dest->NRROUTE[0].ROUT_OBSCOUNT >= 128)	 // Not if locked
			continue;

		if (Dest->ROUTE[0].ROUT_NEIGHBOUR == Route)
		{
			//	We are deleting the best route, so need to tell other nodes
			//	If this is the only one, we need to keep the entry with at 60000 rtt so
			//	we can send it. Remove when all gone

			//	How do we indicate is is dead - Maybe the 60000 is enough!
			if (Dest->ROUTE[1].ROUT_NEIGHBOUR == 0)
			{
				// Only entry
				Dest->ROUTE[0].SRTT = 60000;
				Dest->ROUTE[0].Hops = 255;

				continue;
			}

			Dest->ROUTE[1].LastRTT  = Dest->ROUTE[0].SRTT;		// So next scan will check if rtt has increaced
															// enough to need a RIF
				
			memcpy(&Dest->ROUTE[0], &Dest->ROUTE[1], sizeof(struct DEST_ROUTE_ENTRY));
			memcpy(&Dest->ROUTE[1], &Dest->ROUTE[2], sizeof(struct DEST_ROUTE_ENTRY));
			memset(&Dest->ROUTE[2], 0, sizeof(struct DEST_ROUTE_ENTRY));

			continue;
		}

		// If we aren't removing the best, we don't need to tell anyone.
		
		if (Dest->ROUTE[1].ROUT_NEIGHBOUR == Route)
		{
			memcpy(&Dest->ROUTE[1], &Dest->ROUTE[2], sizeof(struct DEST_ROUTE_ENTRY));
			memset(&Dest->ROUTE[2], 0, sizeof(struct DEST_ROUTE_ENTRY));

			continue;
		}

		if (Dest->ROUTE[2].ROUT_NEIGHBOUR == Route)
		{
			memset(&Dest->ROUTE[2], 0, sizeof(struct DEST_ROUTE_ENTRY));
			continue;
		}
	}
}

VOID DecayNETROMRoutes(struct ROUTE * Route)
{
	int i;
	struct DEST_LIST * Dest =  DESTS;

	Dest--;

	// Decay any NETROM Dest entries via this Route. If OBS reaches zero, remove

	// OBSINIT is probably too many retries. Try decrementing by 2.

	for (i=0; i < MAXDESTS; i++)
	{
		Dest++;

		if (Dest->DEST_CALL[0] == 0)
			continue;										// Spare Entry

		if (Dest->NRROUTE[0].ROUT_NEIGHBOUR == Route)
		{
			if (Dest->NRROUTE[0].ROUT_OBSCOUNT && Dest->NRROUTE[0].ROUT_OBSCOUNT < 128)	 // Not if locked
			{
				Dest->NRROUTE[0].ROUT_OBSCOUNT--;
				if (Dest->NRROUTE[0].ROUT_OBSCOUNT)
					Dest->NRROUTE[0].ROUT_OBSCOUNT--;

			}
			if (Dest->NRROUTE[0].ROUT_OBSCOUNT == 0)
			{
				// Route expired

				if (Dest->NRROUTE[1].ROUT_NEIGHBOUR == 0)			// No more Netrom Routes
				{
					if (Dest->ROUTE[0].ROUT_NEIGHBOUR == 0)		// Any INP3 ROutes?
					{
						// No More Routes - ZAP Dest

						REMOVENODE(Dest);			// Clear buffers, Remove from Sorted Nodes chain, and zap entry	
						continue;
					}
					else
					{
						// Still have an INP3 Route - just zap this entry

						memset(&Dest->NRROUTE[0], 0, sizeof(struct NR_DEST_ROUTE_ENTRY));
						continue;

					}
				}

				memcpy(&Dest->NRROUTE[0], &Dest->NRROUTE[1], sizeof(struct NR_DEST_ROUTE_ENTRY));
				memcpy(&Dest->NRROUTE[1], &Dest->NRROUTE[2], sizeof(struct NR_DEST_ROUTE_ENTRY));
				memset(&Dest->NRROUTE[2], 0, sizeof(struct NR_DEST_ROUTE_ENTRY));

				continue;
			}
		}
		
		if (Dest->NRROUTE[1].ROUT_NEIGHBOUR == Route)
		{
			Dest->NRROUTE[1].ROUT_OBSCOUNT--;

			if (Dest->NRROUTE[1].ROUT_OBSCOUNT == 0)
			{
				memcpy(&Dest->NRROUTE[1], &Dest->NRROUTE[2], sizeof(struct NR_DEST_ROUTE_ENTRY));
				memset(&Dest->NRROUTE[2], 0, sizeof(struct NR_DEST_ROUTE_ENTRY));

				continue;
			}
		}

		if (Dest->NRROUTE[2].ROUT_NEIGHBOUR == Route)
		{
			Dest->NRROUTE[2].ROUT_OBSCOUNT--;

			if (Dest->NRROUTE[2].ROUT_OBSCOUNT == 0)
			{
				memset(&Dest->NRROUTE[2], 0, sizeof(struct NR_DEST_ROUTE_ENTRY));
				continue;
			}
		}
	}
}


VOID TellINP3LinkSetupFailed(struct ROUTE * Route)
{
	// Attempt to activate Neighbour failed
	
//	char call[11]="";

//	ConvFromAX25(Route->NEIGHBOUR_CALL, call);
//	Debugprintf("BPQ32 L2 Link to Neighbour %s setup failed", call);


	if (Route->INP3Node == 0)
		DecayNETROMRoutes(Route);
	else
		DeleteINP3Routes(Route);
}

VOID ProcessRTTReply(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff)
{
	int RTT;
	unsigned int OrigTime;

	if ((Route->Status & GotRTTResponse) == 0)
	{
		// Link is just starting

		Route->Status |= GotRTTResponse;

		if (Route->Status & GotRTTRequest)
		{
			Route->Status |= SentOurRIF;	
			SendOurRIF(Route);
			SendRIPToNeighbour(Route);
		}
	}

	Route->Timeout = 0;			// Got Response
	
	sscanf(&Buff->L4DATA[6], "%d", &OrigTime);
	RTT = GetTickCount() - OrigTime;

	if (RTT > 60000)
		return;					// Ignore if more than 60 secs

	Route->RTT = RTT;

	if (Route->SRTT == 0)
		Route->SRTT = RTT;
	else
		Route->SRTT = ((Route->SRTT * 80)/100) + ((RTT * 20)/100);
}

VOID ProcessINP3RIF(struct ROUTE * Route, UCHAR * ptr1, int msglen, int Port)
{
	unsigned char axcall[7];
	int hops;
	unsigned short rtt;
	int len;
	int opcode;
	char alias[6];
	UINT Stamp, HH, MM;


#ifdef NOINP3

	return;

#endif

	if (Route->INP3Node == 0)
		return;						// We don't want to use INP3

	// Update Timestamp on Route

	time((time_t *)&Stamp);

	Stamp = Stamp % 86400;			// Secs into day
	HH = Stamp / 3600;

	Stamp -= HH * 3600;
	MM = Stamp  / 60;

	Route->NEIGHBOUR_TIME = 256 * HH + MM;

	while (msglen > 0)
	{
		memset(alias, ' ', 6);	
		memcpy(axcall, ptr1, 7);

		if (axcall[0] < 0x60 || (axcall[0] & 1))		// Not valid ax25 callsign
			return;					// Corrupt RIF
	
		ptr1+=7;

		hops = *ptr1++;
		rtt = (*ptr1++ << 8);
		rtt += *ptr1++;

		msglen -= 10;

		while (*ptr1 && msglen > 0)
		{
			len = *ptr1;
			opcode = *(ptr1+1);

			if (len < 2 || len > msglen)
				return;				// Duff RIF

			if (opcode == 0)
			{
				if (len > 1 && len < 9)
					memcpy(alias, ptr1+2, len-2);
				else
				{
					Debugprintf("Corrupt INP3 Message");
					return;
				}
			}
			ptr1+=len;
			msglen -=len;
		}

		ptr1++;
		msglen--;		// EOP

		UpdateNode(Route, axcall, alias, hops, rtt);
	}
	
	return;
}

VOID KillRoute(struct DEST_ROUTE_ENTRY * ROUTEPTR)
{
}


VOID UpdateNode(struct ROUTE * Route, UCHAR * axcall, UCHAR * alias, int  hops, int rtt)
{
	struct DEST_LIST * Dest;
	struct DEST_ROUTE_ENTRY * ROUTEPTR;
	int i;
	char call[11]="";

	if (hops > MaxHops && hops < 255)
	{
//		ConvFromAX25(axcall, call);
//		Debugprintf("Node %s Hops %d RTT %d Ignored - Hop Count too high", call, hops, rtt);
		return;
	}

	if (rtt > MAXRTT  && rtt < 60000)
	{
//		ConvFromAX25(axcall, call);
//		Debugprintf("Node %s Hops %d RTT %d Ignored - rtt too high", call, hops, rtt);
		return;
	}

	if (FindDestination(axcall, &Dest))
		goto Found;

	if (Dest == NULL)
		return;			// Tsble Full

	if (rtt >= 60000)
		return;				// No Point addind a new dead route

	memset(Dest, 0, sizeof(struct DEST_LIST));

	memcpy(Dest->DEST_CALL, axcall, 7);
	memcpy(Dest->DEST_ALIAS, alias, 6);

//	Set up First Route

	Dest->ROUTE[0].Hops = hops;
	Dest->ROUTE[0].SRTT = rtt;
	Dest->ROUTE[0].LastRTT = 0;

	Dest->INP3FLAGS = NewNode;

	Dest->ROUTE[0].ROUT_NEIGHBOUR = Route;

	NUMBEROFNODES++;

	ConvFromAX25(Dest->DEST_CALL, call);
	Debugprintf("Adding  Node %s Hops %d RTT %d", call, hops, rtt);

	return;

Found:

	if (Dest->DEST_STATE & 0x80)	// Application Entry
		return;

	// Update ALIAS

	if (alias[0] > ' ')
		memcpy(Dest->DEST_ALIAS, alias, 6);

	// See if we are known to it, it not add

	ROUTEPTR = &Dest->ROUTE[0];

	if (rtt >= 60000)
	{
		i=rtt+1;
	}

	if (ROUTEPTR->ROUT_NEIGHBOUR == Route)
	{
		UpdateRoute(Dest, ROUTEPTR, hops, rtt);
		return;
	}

	ROUTEPTR = &Dest->ROUTE[1];

	if (ROUTEPTR->ROUT_NEIGHBOUR == Route)
	{
		UpdateRoute(Dest, ROUTEPTR, hops, rtt);
		return;
	}

	ROUTEPTR = &Dest->ROUTE[2];

	if (ROUTEPTR->ROUT_NEIGHBOUR == Route)
	{
		UpdateRoute(Dest, ROUTEPTR, hops, rtt);
		return;
	}

	// Not in list. If any spare, add.
	// If full, see if this is better

	if (rtt >= 60000)
		return;				// No Point addind a new dead route

	ROUTEPTR = &Dest->ROUTE[0];

	for (i = 1; i < 4; i++)
	{
		if (ROUTEPTR->ROUT_NEIGHBOUR == NULL)
		{
			// Add here

			Dest->ROUTE[0].Hops = hops;
			Dest->ROUTE[0].SRTT = rtt;
			Dest->ROUTE[0].ROUT_NEIGHBOUR = Route;

			return;
		}
		ROUTEPTR++;
	}

	// Full, see if this is better

	// Note that wont replace any netrom routes with INP3 ones unless we add pseudo rtt values to netrom entries

	if (Dest->ROUTE[0].SRTT > rtt)
	{
		// We are better. Move others down and add on front

		memcpy(&Dest->ROUTE[2], &Dest->ROUTE[1], sizeof(struct DEST_ROUTE_ENTRY));
		memcpy(&Dest->ROUTE[1], &Dest->ROUTE[0], sizeof(struct DEST_ROUTE_ENTRY));

		AddHere(&Dest->ROUTE[0], Route, hops, rtt);
		return;
	}

	if (Dest->ROUTE[1].SRTT > rtt)
	{
		// We are better. Move  2nd down and add

		memcpy(&Dest->ROUTE[2], &Dest->ROUTE[1], sizeof(struct DEST_ROUTE_ENTRY));

		AddHere(&Dest->ROUTE[1], Route, hops, rtt);
		return;
	}

	if (Dest->ROUTE[2].SRTT > rtt)
	{
		// We are better. Add here

		AddHere(&Dest->ROUTE[2], Route, hops, rtt);
		return;
	}

	// Worse than any - ignoee

}

VOID AddHere(struct DEST_ROUTE_ENTRY * ROUTEPTR,struct ROUTE * Route , int  hops, int rtt)
{
	ROUTEPTR->Hops = hops;
	ROUTEPTR->SRTT = rtt;
	ROUTEPTR->LastRTT = 0;
	ROUTEPTR->RTT = 0;
	ROUTEPTR->ROUT_NEIGHBOUR = Route;

	return;
}


/*	LEA	EDI,DEST_CALL[EBX]
	MOV	ECX,7
	REP MOVSB

	MOV	ECX,6			; ADD ALIAS
	MOV	ESI,OFFSET32 TEMPFIELD
	REP MOVSB

	POP	ESI
;
;	GET _NEIGHBOURS FOR THIS DESTINATION
;
	CALL	CONVTOAX25
	JNZ SHORT BADROUTE
;
	CALL	GETVALUE
	MOV	_SAVEPORT,AL		; SET PORT FOR _FINDNEIGHBOUR

	CALL	GETVALUE
	MOV	_ROUTEQUAL,AL
;
	MOV	ESI,OFFSET32 AX25CALL

	PUSH	EBX			; SAVE DEST
	CALL	_FINDNEIGHBOUR
	MOV	EAX,EBX			; ROUTE TO AX
	POP	EBX

	JZ SHORT NOTBADROUTE

	JMP SHORT BADROUTE

NOTBADROUTE:
;
;	UPDATE ROUTE LIST FOR THIS DEST
;
	MOV	ROUT1_NEIGHBOUR[EBX],EAX
	MOV	AL,_ROUTEQUAL
	MOV	ROUT1_QUALITY[EBX],AL
	MOV	ROUT1_OBSCOUNT[EBX],255	; LOCKED
;
	POP	EDI
	POP	EBX
	
	INC	_NUMBEROFNODES

	JMP	SENDOK
*/


VOID SortRoutes(struct DEST_LIST * Dest)
{
	 struct DEST_ROUTE_ENTRY Temp;

	// May now be out of order

	if (Dest->ROUTE[1].ROUT_NEIGHBOUR == 0)
		return;						// Only One, so cant be out of order
	
	if (Dest->ROUTE[2].ROUT_NEIGHBOUR == 0)
	{
		// Only 2

		if (Dest->ROUTE[0].SRTT <= Dest->ROUTE[1].SRTT)
			return;

		// Swap one and two

		memcpy(&Temp, &Dest->ROUTE[0], sizeof(struct DEST_ROUTE_ENTRY));
		memcpy(&Dest->ROUTE[0], &Dest->ROUTE[1], sizeof(struct DEST_ROUTE_ENTRY));
		memcpy(&Dest->ROUTE[1], &Temp, sizeof(struct DEST_ROUTE_ENTRY));

		return;
	}

	// Have 3 Entries
}



VOID UpdateRoute(struct DEST_LIST * Dest, struct DEST_ROUTE_ENTRY * ROUTEPTR, int  hops, int rtt)
{
	if (ROUTEPTR->Hops == 0)
	{
		// This is not a INP3 Route - Convert it

		ROUTEPTR->Hops = hops;
		ROUTEPTR->SRTT = rtt;

		SortRoutes(Dest);
		return;
	}

	if (rtt == 60000)
	{
		ROUTEPTR->SRTT = rtt;
		ROUTEPTR->Hops = hops;

		SortRoutes(Dest);
		return;

	}

	ROUTEPTR->SRTT = rtt;
	ROUTEPTR->Hops = hops;
	
	SortRoutes(Dest);
	return;
}

VOID ProcessRTTMsg(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff, int Len, int Port)
{
	// See if a reply to our message, or a new request

	if (memcmp(Buff->L3SRCE, MYCALL,7) == 0)
	{
		ProcessRTTReply(Route, Buff);
		ReleaseBuffer(Buff);
	}
	else
	{
		int OtherRTT;
		int Dummy;
		
		if (Route->INP3Node == 0)
		{
			ReleaseBuffer(Buff);
			return;						// We don't want to use INP3
		}

		// Extract other end's SRTT

		sscanf(&Buff->L4DATA[6], "%d %d", &Dummy, &OtherRTT);
		Route->NeighbourSRTT = OtherRTT * 10;  // We store in mS

		// Echo Back to sender
	
		SendNetFrame(Route, Buff);

		if ((Route->Status & GotRTTRequest) == 0)
		{
			// Link is just starting

			Route->Status |= GotRTTRequest;
			
			if (Route->Status & GotRTTResponse)
			{
				Route->Status |= SentOurRIF;	
				SendOurRIF(Route);
				SendRIPToNeighbour(Route);

			}
			else
			{
				// We have not yet seen a response (and maybe haven't sent one

				Route->BCTimer = 0;		// So send one
			}
		}
	}
}

VOID SendRTTMsg(struct ROUTE * Route)
{
	struct _L3MESSAGEBUFFER * Msg;
	char Stamp[50];

	Msg = GetBuff();
	if (Msg == 0)
		return;

	Msg->Port = Route->NEIGHBOUR_PORT;
	Msg->L3PID = NRPID;

	memcpy(Msg->L3DEST, L3RTT, 7);
	memcpy(Msg->L3SRCE, MYCALL, 7);
	Msg->L3TTL = 2;
	Msg->L4ID = 0;
	Msg->L4INDEX = 0;
	Msg->L4RXNO = 0;
	Msg->L4TXNO = 0;
	Msg->L4FLAGS = L4INFO;


	sprintf(Stamp, "%10d %10d %10d %10d ", GetTickCount(), Route->SRTT/10, Route->RTT/10, 0);
	memcpy(RTTMsg.TXTIME, Stamp, 44);

	memcpy(Msg->L4DATA, &RTTMsg, 236);

	Msg->LENGTH = 256 + 1 + 7;

	Route->Timeout = RTTTimeout;

	SendNetFrame(Route, Msg);
}

VOID SendKeepAlive(struct ROUTE * Route)
{
	struct _L3MESSAGEBUFFER * Msg = GetBuff();

	if (Msg == 0)
		return;

	Msg->L3PID = NRPID;

	memcpy(Msg->L3DEST, L3KEEP, 7);
	memcpy(Msg->L3SRCE, MYCALL, 7);
	Msg->L3TTL = 1;
	Msg->L4ID = 0;
	Msg->L4INDEX = 0;
	Msg->L4RXNO = 0;
	Msg->L4TXNO = 0;
	Msg->L4FLAGS = L4INFO;

//	Msg->L3MSG.L4DATA[0] = 'K';

	Msg->LENGTH = 20 + MSGHDDRLEN + 1;

	SendNetFrame(Route, Msg);
}

int BuildRIF(UCHAR * RIF, UCHAR * Call, UCHAR * Alias, int Hops, int RTT)
{
	int AliasLen;
	int RIFLen;
	UCHAR AliasCopy[10] = "";
	UCHAR * ptr;


	if (RTT > 60000) RTT = 60000;	// Dont send more than 60000

	memcpy(&RIF[0], Call, 7);
	RIF[7] = Hops;
	RIF[8] = RTT >> 8;
	RIF[9] = RTT & 0xff;

	if (Alias)
	{
		// Need to null-terminate Alias
		
		memcpy(AliasCopy, Alias, 6);
		ptr = strchr(AliasCopy, ' ');

		if (ptr)
			*ptr = 0;

		AliasLen = (int)strlen(AliasCopy);

		RIF[10] = AliasLen+2;
		RIF[11] = 0;
		memcpy(&RIF[12], Alias, AliasLen);
		RIF[12+AliasLen] = 0;

		RIFLen = 13 + AliasLen;
		return RIFLen;
	}
	RIF[10] = 0;
	
	return (11);
}


VOID SendOurRIF(struct ROUTE * Route)
{
	struct _L3MESSAGEBUFFER * Msg;
	int RIFLen;
	int totLen = 1;
	int App;
	APPLCALLS * APPL;

	Msg = GetBuff();
	if (Msg == 0)
		return;

	Msg->L3SRCE[0] = 0xff;

	// send a RIF for our Node and all our APPLCalls

	RIFLen = BuildRIF(&Msg->L3SRCE[totLen], MYCALL, MYALIASTEXT, 1, 0);
	totLen += RIFLen;

	for (App = 0; App < NumberofAppls; App++)
	{
		APPL=&APPLCALLTABLE[App];

		if (APPL->APPLQUAL > 0)
		{
			RIFLen = BuildRIF(&Msg->L3SRCE[totLen], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, 0);
			totLen += RIFLen;
		}
	}

	Msg->L3PID = NRPID;
	Msg->LENGTH = totLen + 1 + MSGHDDRLEN;

	SendNetFrame(Route, Msg);
}

int SendRIPTimer()
{
	int count, nodes;
	struct ROUTE * Route = NEIGHBOURS;
	int MaxRoutes = MAXNEIGHBOURS;
	int INP3Delay;

	for (count=0; count<MaxRoutes; count++)
	{
		if (Route->NEIGHBOUR_CALL[0] != 0)
		{
			if (Route->NoKeepAlive)					// Keepalive Disabled
			{
				Route++;
				continue;
			}
			
			if (Route->NEIGHBOUR_LINK == 0 || Route->NEIGHBOUR_LINK->LINKPORT == 0)
			{
				if (Route->NEIGHBOUR_QUAL == 0)
				{
					Route++;
					continue;						// Qual zero is a locked out route
				}

				// Dont Activate if link has no nodes unless INP3

				if (Route->INP3Node == 0)
				{
					nodes = COUNTNODES(Route);
			
					if (nodes == 0)
					{
						Route++;
						continue;
					}
				}

				// Delay more if Locked - they could be retrying for a long time

				if ((Route->NEIGHBOUR_FLAG & 1))	 // LOCKED ROUTE
					INP3Delay = 1200;
				else
					INP3Delay = 600;
 
				if (Route->LastConnectAttempt &&
					(REALTIMETICKS - Route->LastConnectAttempt) < INP3Delay) 
				{
					Route++;
					continue;						// No room for link
				}

				// Try to activate link

				L2SETUPCROSSLINKEX(Route, 2);		// Only try SABM twice

				Route->LastConnectAttempt = REALTIMETICKS;
				
				if (Route->NEIGHBOUR_LINK == 0)
				{
					Route++;
					continue;						// No room for link
				}
			}

			if (Route->NEIGHBOUR_LINK->L2STATE != 5)	// Not up yet
			{
				Route++;
				continue;
			}

			if (Route->NEIGHBOUR_LINK->KILLTIMER > ((L4LIMIT - 60) * 3))	// IDLETIME - 1 Minute
			{
				SendKeepAlive(Route);
				Route->NEIGHBOUR_LINK->KILLTIMER = 0;		// Keep Open
			}

#ifdef NOINP3

			Route++;
			continue;

#endif
			if (Route->INP3Node)
			{
				if (Route->Timeout)
				{
					// Waiting for response

					Route->Timeout--;

					if (Route->Timeout)
					{
						Route++;
						continue;				// Wait
					}
					// No response Try again

					Route->Retries--;

					if (Route->Retries)
					{
						// More Left

						SendRTTMsg(Route);
					}
					else
					{
						// No Response - Kill all Nodes via this link

						if (Route->Status)
						{
							char Call [11] = "";

							ConvFromAX25(Route->NEIGHBOUR_CALL, Call);
							Debugprintf("BPQ32 INP Neighbour %s Lost", Call);

							Route->Status = 0;	// Down
						}

						Route->BCTimer=5;		// Wait a while before retrying
					}
				}

				if (Route->BCTimer)
				{
					Route->BCTimer--;
				}
				else
				{
					Route->BCTimer = RTTInterval;
					Route->Retries = RTTRetries;
					SendRTTMsg(Route);
				}
			}
		}

		Route++;
	}

	return (0);
}

// Create an Empty RIF

struct _L3MESSAGEBUFFER * CreateRIFHeader(struct ROUTE * Route)
{
	struct _L3MESSAGEBUFFER * Msg = GetBuff();
	UCHAR AliasCopy[10] = "";

	Msg->LENGTH = 1;
	Msg->L3SRCE[0] = 0xff;

	Msg->L3PID = NRPID;

	return Msg;

}

VOID SendRIF(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Msg)
{
	Msg->LENGTH += MSGHDDRLEN + 1;		// PID

	SendNetFrame(Route, Msg);
}

VOID SendRIPToOtherNeighbours(UCHAR * axcall, UCHAR * alias, struct DEST_ROUTE_ENTRY * Entry)
{
	struct ROUTE * Routes = NEIGHBOURS;
	struct _L3MESSAGEBUFFER * Msg;
	int count, MaxRoutes = MAXNEIGHBOURS;

	for (count=0; count<MaxRoutes; count++)
	{
		if ((Routes->INP3Node) && 
			(Routes->Status) && 
			(Routes != Entry->ROUT_NEIGHBOUR))	// Dont send to originator of route
		{
			Msg = Routes->Msg;
			
			if (Msg == NULL) 
				Msg = Routes->Msg = CreateRIFHeader(Routes);
			
			Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH],
				axcall, alias, Entry->Hops + 1, Entry->SRTT + Entry->ROUT_NEIGHBOUR->SRTT/10);

			if (Msg->LENGTH > 250 - 15)
//			if (Msg->LENGTH > Routes->NBOUR_PACLEN - 11)
			{
				SendRIF(Routes, Msg);
				Routes->Msg = NULL;
			}
		}
		Routes+=1;
	}
}

VOID SendRIPToNeighbour(struct ROUTE * Route)
{
	int i;
	struct DEST_LIST * Dest =  DESTS;
	struct DEST_ROUTE_ENTRY * Entry;
	struct _L3MESSAGEBUFFER * Msg;

	Dest--;

	// Send all entries not via this Neighbour - used when link starts

	for (i=0; i < MAXDESTS; i++)
	{
		Dest++;

		Entry = &Dest->ROUTE[0];

		if (Entry->ROUT_NEIGHBOUR && Entry->Hops && Route != Entry->ROUT_NEIGHBOUR)	
		{
			// Best Route not via this neighbour - send
		
			Msg = Route->Msg;
			
			if (Msg == NULL) 
				Msg = Route->Msg = CreateRIFHeader(Route);
			
			Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH],
				Dest->DEST_CALL, Dest->DEST_ALIAS,
				Entry->Hops + 1, Entry->SRTT + Entry->ROUT_NEIGHBOUR->SRTT/10);

			if (Msg->LENGTH > 250 - 15)
			{
				SendRIF(Route, Msg);
				Route->Msg = NULL;
			}
		}
	}
	if (Route->Msg)
	{
		SendRIF(Route, Route->Msg);
		Route->Msg = NULL;
	}
}

VOID FlushRIFs()
{
	struct ROUTE * Routes = NEIGHBOURS;
	int count, MaxRoutes = MAXNEIGHBOURS;

	for (count=0; count<MaxRoutes; count++)
	{
		if (Routes->Msg)
		{
			SendRIF(Routes, Routes->Msg);
			Routes->Msg = NULL;
		}
		Routes+=1;
	}
}

VOID SendNegativeInfo()
{
	int i, Preload;
	struct DEST_LIST * Dest =  DESTS;
	struct DEST_ROUTE_ENTRY * Entry;

	Dest--;

	// Send RIF for any Dests that have got worse
	
	// ?? Should we send to one Neighbour at a time, or do all in parallel ??

	// The spec says send Negative info as soon as possible so I'll try building them in parallel
	// That will mean building several packets in parallel


	for (i=0; i < MAXDESTS; i++)
	{
		Dest++;

		Entry = &Dest->ROUTE[0];

		if (Entry->SRTT > Entry->LastRTT)
		{
			if (Entry->LastRTT)		// if zero haven't yet reported +ve info
			{
				if (Entry->LastRTT == 1)	// if 1, probably new, so send alias
					SendRIPToOtherNeighbours(Dest->DEST_CALL, Dest->DEST_ALIAS, Entry);
				else
					SendRIPToOtherNeighbours(Dest->DEST_CALL, 0, Entry);

				Preload = Entry->SRTT /10;
				if (Entry->SRTT < 60000)
					Entry->LastRTT = Entry->SRTT + Preload;	//10% Negative Preload
			}
		}
			
		if (Entry->SRTT >= 60000)
		{
			// It is dead, and we have reported it if necessary, so remove if no NETROM Routes

			if (Dest->NRROUTE[0].ROUT_NEIGHBOUR == 0)			// No more Netrom Routes
			{
				char call[11]="";
				ConvFromAX25(Dest->DEST_CALL, call);
				Debugprintf("Deleting Node %s", call);
				REMOVENODE(Dest);			// Clear buffers, Remove from Sorted Nodes chain, and zap entry	
			}
			else
			{
				// Have a NETROM route, so zap the INP3 one

				memset(Entry, 0, sizeof(struct DEST_ROUTE_ENTRY));
			}
		}
	}
}

VOID SendPositiveInfo()
{
	int i;
	struct DEST_LIST * Dest =  DESTS;
	struct DEST_ROUTE_ENTRY * Entry;

	Dest--;

	// Send RIF for any Dests that have got significantly better or are newly discovered

	for (i=0; i < MAXDESTS; i++)
	{
		Dest++;

		Entry = &Dest->ROUTE[0];

		if (( (Entry->SRTT) && (Entry->LastRTT == 0) )|| 		// if zero haven't yet reported +ve info
			((((Entry->SRTT * 125) /100) < Entry->LastRTT) && // Better by 25%
			((Entry->LastRTT - Entry->SRTT) > 10)))			  // and 100ms
		{
			SendRIPToOtherNeighbours(Dest->DEST_CALL, 0, Entry);
			Dest->ROUTE[0].LastRTT = (Dest->ROUTE[0].SRTT * 11) / 10;	//10% Negative Preload
		}
	}
}

VOID SendNewInfo()
{
	int i;
	unsigned int NewRTT;
	struct DEST_LIST * Dest =  DESTS;
	struct DEST_ROUTE_ENTRY * Entry;

	Dest--;

	// Send RIF for any Dests that have just been added

	for (i=0; i < MAXDESTS; i++)
	{
		Dest++;

		if (Dest->INP3FLAGS & NewNode)
		{
			Dest->INP3FLAGS &= ~NewNode;
			
			Entry = &Dest->ROUTE[0];

			SendRIPToOtherNeighbours(Dest->DEST_CALL, Dest->DEST_ALIAS, Entry);

			NewRTT = (Entry->SRTT * 11) / 10;
			Entry->LastRTT = NewRTT;	//10% Negative Preload
		}
	}
}


VOID INP3TIMER()
{
	if (RTTMsg.ID[0] == 0)
		InitialiseRTT();

	// Called once per second

#ifdef NOINP3

	if (RIPTimerCount == 0)
	{
		RIPTimerCount = 10;
		SendRIPTimer();
	}
	else
		RIPTimerCount--;

	return;

#endif

	SendNegativeInfo();					// Urgent

	if (RIPTimerCount == 0)
	{
		RIPTimerCount = 10;
		SendNewInfo();					// Not quite so urgent
		SendRIPTimer();
	}
	else
		RIPTimerCount--;

	if (PosTimerCount == 0)
	{
		PosTimerCount = 300;			// 5 mins
		SendPositiveInfo();
	}
	else
		PosTimerCount--;

	FlushRIFs();

}


UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, unsigned int msglen)
{
	char call[10];
	int calllen;
	int hops;
	unsigned short rtt;
	unsigned int len;
	unsigned int opcode;
	char alias[10] = "";
	UCHAR IP[6];
	int i;

	ptr2+=sprintf(ptr2, " INP3 RIF:\r Alias  Call  Hops  RTT\r");

	while (msglen > 0)
	{
		calllen = ConvFromAX25(ptr1, call);
		call[calllen] = 0;

		// Validate the call

		for (i = 0; i < calllen; i++)
		{
			if (!isupper(call[i]) && !isdigit(call[i]) && call[i] != '-')
			{
				ptr2+=sprintf(ptr2, " Corrupt RIF\r");
				return ptr2;
			}
		}
				
		ptr1+=7;

		hops = *ptr1++;
		rtt = (*ptr1++ << 8);
		rtt += *ptr1++;

		IP[0] = 0;
		strcpy(alias, "      ");

		msglen -= 10;

		while (*ptr1 && msglen > 0)
		{
			len = *ptr1;
			opcode = *(ptr1+1);

			if (len < 2 || len > msglen)
			{
				ptr2+=sprintf(ptr2, " Corrupt RIF\r");
				return ptr2;
			}
			if (opcode == 0 && len < 9)
			{
				memcpy(&alias[6 - (len - 2)], ptr1+2, len-2);		// Right Justiify
			}
			else
			if (opcode == 1 && len < 8)
			{
				memcpy(IP, ptr1+2, len-2);
			}

			ptr1+=len;
			msglen -=len;
		}

		if (IP[0])
			ptr2+=sprintf(ptr2, " %s:%s %d %4.2d %d.%d.%d.%d\r", alias, call, hops, rtt, IP[0], IP[1], IP[2], IP[3]);
		else
			ptr2+=sprintf(ptr2, " %s:%s %d %4.2d\r", alias, call, hops, rtt);

		ptr1++;
		msglen--;		// EOP
	}
	
	return ptr2;
}

