linbpq/L3Code.c

1471 lines
31 KiB
C
Raw Normal View History

2022-08-28 09:35:46 +01:00
/*
Copyright 2001-2018 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
*/
/*
Copyright 2001-2018 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
*/
//
// C replacement for L3Code.asm
//
#define Kernel
#define _CRT_SECURE_NO_DEPRECATE
#pragma data_seg("_BPQDATA")
#include "time.h"
#include "stdio.h"
#include <fcntl.h>
#include "CHeaders.h"
VOID UPDATEDESTLIST();
VOID MOVEALL(dest_list * DEST);
VOID MOVE3TO2(dest_list * DEST);
VOID CLEARTHIRD(dest_list * DEST);
VOID L3TRYNEXTDEST(struct ROUTE * ROUTE);
VOID SendNETROMRoute(struct PORTCONTROL * PORT, unsigned char * axcall);
extern BOOL NODESINPROGRESS ;;
PPORTCONTROL L3CURRENTPORT;
extern dest_list * CURRENTNODE;
int L3_10SECS = 10;
VOID L3BG()
{
// TRANSFER MESSAGES FROM DEST TO LINK
int n = MAXDESTS;
struct DEST_LIST * DEST = DESTS; // NODE LIST
struct PORTCONTROL * PORT = PORTTABLE;
struct ROUTE * ROUTE;
struct _LINKTABLE * LINK;
while (n--)
{
if (DEST->DEST_CALL[0]) // Active entry?
{
while(DEST->DEST_Q) // FRAMES TO SEND?
{
int ActiveRoute = DEST->DEST_ROUTE;
if (ActiveRoute)
{
ROUTE = DEST->NRROUTE[ActiveRoute - 1].ROUT_NEIGHBOUR;
if (ROUTE)
LINK = ROUTE->NEIGHBOUR_LINK;
else
LINK= NULL;
if (LINK)
{
if (LINK->L2STATE == 0)
{
// LINK ENTRY IS INVALID - IT PROBABLY HAS BEEN 'ZAPPED', SO CANCEL IT
ROUTE->NEIGHBOUR_LINK = NULL;
}
else
{
if (LINK->L2STATE < 5)
{
goto NextDest; // Wait for it to activate
}
else
{
ROUTE->NBOUR_IFRAMES++;
C_Q_ADD(&LINK->TX_Q, Q_REM(&DEST->DEST_Q));
continue; // See if more
}
}
}
// Drop through to Activate
}
if (ACTIVATE_DEST(DEST) == FALSE)
{
// Node has no routes - get rid of it
REMOVENODE(DEST);
return; // Avoid riskof looking at lod entries
}
}
}
NextDest:
DEST++;
}
}
BOOL ACTIVATE_DEST(struct DEST_LIST * DEST)
{
int n = MAXDESTS;
struct PORTCONTROL * PORT = PORTTABLE;
struct ROUTE * ROUTE;
struct _LINKTABLE * LINK;
int ActiveRoute;
if (DEST->DEST_ROUTE == 0) // ALREADY HAVE A SELECTED ROUTE?
DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST
ActiveRoute = DEST->DEST_ROUTE - 1;
ROUTE = DEST->NRROUTE[ActiveRoute].ROUT_NEIGHBOUR;
if (ROUTE == 0)
{
// Currnet Route not present
// If current route is 1, then we must have INP3 routes (or entry is corrupt)
if (DEST->DEST_ROUTE != 1)
goto NOROUTETODEST;
// Current Route is 1
if (DEST->ROUTE[0].ROUT_NEIGHBOUR == 0)
return FALSE; // No INP3 so No Routes
DEST->DEST_ROUTE = 4; // First INP3
ROUTE = DEST->ROUTE[0].ROUT_NEIGHBOUR;
}
LINK = ROUTE->NEIGHBOUR_LINK;
if (LINK == 0)
{
// Need to Activate Link
// SET UP LINK TABLE ENTRY
return L2SETUPCROSSLINK(ROUTE);
}
// We mst be waiting for link to come up
return TRUE;
NOROUTETODEST:
// CURRENT NEIGHBOUR NOT DEFINED - RESET TO USE FIRST
if (DEST->DEST_ROUTE == 1)
return FALSE; // First not defined so give up
DEST->DEST_ROUTE = 0;
return TRUE;
}
char Call1[10];
char Call2[10];
char Call3[10];
VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT)
{
// PROCESS A NET/ROM 'NODES' MESSAGE
// UPDATE _NEIGHBOURS LIST WITH ORIGINATING CALL, AND
// DESTINATION LIST WITH ANY PRACTICAL ROUTES
struct DEST_LIST * DEST;
struct ROUTE * ROUTE;
int Portno = PORT->PORTNUMBER;
time_t Stamp;
int HH, MM;
int Msglen = Msg->LENGTH;
int n;
UCHAR * ptr1, * ptr2, * saveptr;
int Qual;
APPLCALLS * APPL;
int App;
if (PORT->PORTQUALITY == 0 || PORT->INP3ONLY)
return;
// SEE IF OUR CALL - DONT WANT TO PUT IT IN LIST!
if (CompareCalls(Msg->ORIGIN, NETROMCALL))
return;
if (CheckExcludeList(Msg->ORIGIN) == 0)
return;
for (App = 0; App < NumberofAppls; App++)
{
APPL=&APPLCALLTABLE[App];
if (APPL->APPLHASALIAS == 0 && CompareCalls(Msg->ORIGIN, APPL->APPLCALL))
return;
}
Msg->ORIGIN[6] &= 0x1E; // MASK OFF LAST ADDR BIT
/*
// validate call ptr = &Buffer->ORIGIN[0];
n = 6;
while(n--)
{
// Try a bit harder to detect corruption
c = *(ptr++);
if (c & 1)
{
ReleaseBuffer(Buffer);
return;
}
c = c >> 1;
if (!isalnum(c) && !(c == '#') && !(c == ' '))
{
ReleaseBuffer(Buffer);
return;
}
}
*/
// SEE IF ORIGINATING CALL IS IN NEIGHBOUR LIST - IF NOT ADD IT
if (FindNeighbour(Msg->ORIGIN, Portno, &ROUTE) == 0)
{
// Not in list
if (ROUTE == NULL)
return; // Table full
// CREATE NEIGHBOUR RECORD
memcpy(ROUTE->NEIGHBOUR_CALL, Msg->ORIGIN, 7);
ROUTE->NEIGHBOUR_PORT = Portno;
ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY;
ROUTE->NEIGHBOUR_LINK = 0; // CANT HAVE A LINK IF NEW _NODE
ROUTE->NoKeepAlive = PORT->PortNoKeepAlive;
}
// if locked route with quality zero ignore
if ((ROUTE->NEIGHBOUR_FLAG & 1)) // LOCKED ROUTE
if (ROUTE->NEIGHBOUR_QUAL == 0)
return;
// If Ignoreunlocked set, ignore it not locked
if ((ROUTE->NEIGHBOUR_FLAG & 1) == 0) // LOCKED ROUTE
if (PORT->IgnoreUnlocked)
return;
SendNETROMRoute(PORT, Msg->ORIGIN);
// if not locked, update route quality from port quality (may have changed config and not cleared SAVENODES
if ((ROUTE->NEIGHBOUR_FLAG & 1) == 0) // Not LOCKED ROUTE
ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY;
// GET TIME FROM BIOS DATA AREA OR RTC
time((time_t *)&Stamp);
Stamp = Stamp % 86400; // Secs into day
HH = (int)(Stamp / 3600);
Stamp -= HH * 3600;
MM = (int)(Stamp / 60);
ROUTE->NEIGHBOUR_TIME = 256 * HH + MM;
// GET QUALITY
Qual = ROUTEQUAL = ROUTE->NEIGHBOUR_QUAL; // FOR INITIAL ROUTE TABLE UPDATE
// CHECK LINK IS IN DEST LIST
if (FindDestination(Msg->ORIGIN, &DEST) == 0)
{
if (DEST == NULL)
return; // Tsble Full
// CREATE DESTINATION RECORD
memset(DEST, 0, sizeof(struct DEST_LIST));
memcpy(DEST->DEST_CALL, Msg->ORIGIN, 7);
NUMBEROFNODES++;
}
// ALWAYS UPDATE ALIAS IN CASE NOT PRESENT IN ORIGINAL TABLE
ptr1 = &Msg->L2DATA[1];
ptr2 = &DEST->DEST_ALIAS[0];
if (*ptr1 > ' ') // Only of present
{
// Validate Alias, mainly for DISABL KPC3 Problem
UCHAR c;
n = 6;
while (n--)
{
c = *(ptr1++);
if (c < 0x20 || c > 0x7A)
c = ' ';
*(ptr2++) = c;
}
}
else
ptr1 += 6;
// UPDATE QUALITY AND OBS COUNT
PROCROUTES(DEST, ROUTE, Qual);
Msglen -= (MSGHDDRLEN + 23); // LEVEL 2 HEADER FLAG and NODE MNEMONIC
// PROCESS DESTINATIONS from message
// ptr1 = start
saveptr = ptr1 - 21;
while (Msglen >= 21) // STILL AT LEAST 1 ENTRY LEFT
{
Msglen -= 21;
saveptr += 21;
ptr1 = saveptr;
// SEE IF OUR CALL - DONT WANT TO PUT IT IN LIST!
if (CompareCalls(ptr1, MYCALL))
{
// But use it to get route quality setting from other end
// As we now get qual ftom highest, only use this after a reload in
// case other end has changed.
if (ROUTE->FirstTimeFlag == 0)
{
// Check if route is via our node
if (memcmp(ptr1, &ptr1[13], 7) == 0)
{
if (ROUTE->OtherendLocked == 0) // Dont update locked quality
ROUTE->OtherendsRouteQual = ptr1[20];
ROUTE->FirstTimeFlag = 1; // Only do it first time after load
}
}
continue;
}
if (CheckExcludeList(ptr1) == 0) // Excluded
continue;
for (n = 0; n < 32; n++)
{
if (CompareCalls(ptr1, APPLCALLTABLE[n].APPLCALL))
continue;
}
// MAKE SURE ITS NOT CORRUPTED
n = 6;
while (n--)
{
if (*(ptr1++) < 0x40) // Call
{
Call1[ConvFromAX25(Msg->ORIGIN, Call1)] = 0;
Call2[ConvFromAX25(saveptr, Call2)] = 0;
memcpy(Call3, saveptr + 7,6);
Call3[6] = 0;
Debugprintf("Corrupt Node Entry from %s for %s:%s", Call1, Call2, Call3);
goto IgnoreNode;
}
}
ptr1++; // skip ssid
n = 6;
while (n--)
{
if (*(ptr1) && ((*(ptr1) < 0x20 || *(ptr1) > 0x7A)) ) // Should we accept zeros or convert to spaces??
{
Call1[ConvFromAX25(Msg->ORIGIN, Call1)] = 0;
Call2[ConvFromAX25(saveptr, Call2)] = 0;
memcpy(Call3, saveptr + 7,6);
Call3[6] = 0;
Debugprintf("Corrupt Node Entry from %s for %s:%s", Call1, Call2, Call3);
goto IgnoreNode;
}
ptr1++;
}
ptr1 -= 13; // Back to start
// CALCULATE ROUTE QUALITY
// Experimantal Code to adjust received route qualities based on deduced quality
// settings at other end of link.
// Don't mess with Application Qualities. There are almost always 255, and
// if not there is probably a good reason for the value chosen.
if (CompareCalls(Msg->ORIGIN, &ptr1[13]))
{
// Application Node - Just do normal update
Qual = (((ROUTEQUAL * ptr1[20]) + 128)) / 256;
}
else
{
// Try using the highest reported indirect route as remote qual
if (ROUTE->OtherendLocked == 0) // Not locked
{
if (ptr1[20] > ROUTE->OtherendsRouteQual)
ROUTE->OtherendsRouteQual = ptr1[20];
}
// Treat 255 as 254, so 255 routes doen't get included with minquals
// designed to only include applcalls
if (ptr1[20] == 255)
ptr1[20] = 254;
Qual = (((ROUTEQUAL * ptr1[20]) + 128)) / 256;
// I think we should normalize indirect routes twice
// as node not only sends differnet qual but adjusts incoming qual with it
// or should we ............
// We don't touch appl callsigns so I think everything here is an indirect route
if (ROUTE->OtherendsRouteQual && PORT->NormalizeQuality)
{
Qual = (Qual * ROUTEQUAL) / ROUTE->OtherendsRouteQual;
// not sure about this! Qual = (Qual * ROUTEQUAL) / ROUTE->OtherendsRouteQual; // Twice
if (Qual > ROUTEQUAL)
Qual = ROUTEQUAL;
}
}
// SEE IF BELOW MIN QUAL FOR AUTO UPDATES
if (Qual < MINQUAL)
continue;
// CHECK LINK IS IN DEST LIST
if (FindDestination(ptr1, &DEST) == 0)
{
if (DEST == NULL)
continue;
// CREATE DESTINATION RECORD
memset(DEST, 0, sizeof(struct DEST_LIST));
memcpy(DEST->DEST_CALL, ptr1, 7);
NUMBEROFNODES++;
}
ptr1 += 7;
// UPDATE ALIAS
memcpy(DEST->DEST_ALIAS, ptr1, 6);
ptr1 += 6;
// NOW POINTING AT BEST NEIGHBOUR - IF THIS IS US, THEN ROUTE IS A LOOP
if (CompareCalls(ptr1, NETROMCALL))
Qual = 0;
// DEST IS NOW IN TABLE -
// 1. SEE IF THIS ROUTE IS IN TABLE - IF SO UPDATE QUALITY,
// IF NOT, ADD THIS ONE IF IT HAS HIGHER QUALITY THAN EXISTING ONES
PROCROUTES(DEST, ROUTE, Qual);
ptr1 += 8;
IgnoreNode:;
}
}
VOID PROCROUTES(struct DEST_LIST * DEST, struct ROUTE * ROUTE, int Qual)
{
// ADD NEIGHBOUR ADDRESS IN ROUTE TO NODE's ROUTE TABLE IF BETTER QUALITY THAN THOSE PRESENT
int Index = 0;
struct NR_DEST_ROUTE_ENTRY Temp;
if (DEST->DEST_STATE & 0x80) // BBS ENTRY
return;
for (Index = 0; Index < 4; Index++)
{
if (DEST->NRROUTE[Index].ROUT_NEIGHBOUR == ROUTE)
{
if (Index == 0)
{
// THIS IS A REFRESH OF BEST - IF THIS ISNT ACTIVE ROUTE, MAKE IT ACTIVE
if (DEST->DEST_ROUTE > 1) // LEAVE IT IF NOT SELECTED OR ALREADY USING BEST
DEST->DEST_ROUTE = 1;
}
goto UpdatateThisEntry;
}
}
// NOT IN ANY ROUTE
Index = 0;
if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == 0)
goto UpdatateThisEntry; // SPARE ENTRY, SO USE IT
if (DEST->NRROUTE[0].ROUT_QUALITY < Qual)
{
// New route is better than best, so move other two down and add here
DEST->NRROUTE[2] = DEST->NRROUTE[1];
DEST->NRROUTE[1] = DEST->NRROUTE[0];
DEST->DEST_ROUTE = 0; // Se we will switch to new one
goto UpdatateThisEntry;
}
Index = 1;
if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0)
goto UpdatateThisEntry; // SPARE ENTRY, SO USE IT
if (DEST->NRROUTE[1].ROUT_QUALITY < Qual)
{
// New route is better than second, so move down and add here
DEST->NRROUTE[2] = DEST->NRROUTE[1];
goto UpdatateThisEntry;
}
Index = 2;
if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == 0)
goto UpdatateThisEntry;
if (DEST->NRROUTE[2].ROUT_QUALITY < Qual)
{
// New route is better than third, so add here
goto UpdatateThisEntry;
}
// THIS ROUTE IS WORSE THAN ANY OF THE CURRENT 3 - IGNORE IT
return;
UpdatateThisEntry:
DEST->NRROUTE[Index].ROUT_NEIGHBOUR = ROUTE;
// I DONT KNOW WHY I DID THIS, BUT IT CAUSES REFLECTED ROUTES
// TO BE SET UP WITH OBS = 0. THIS MAY PREVENT A VALID ALTERNATE
// VIA THE SAME NODE TO FAIL TO BE FOUND. SO I'LL TAKE OUT THE
// TEST AND SEE IF ANYTHING NASTY HAPPENS
// IT DID - THIS IS ALSO CALLED BY CHECKL3TABLES. TRY RESETING
// OBS, BUT NOT QUALITY
if ((DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80) == 0)
DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT; // SET OBSOLESCENCE COUNT
if (Qual)
DEST->NRROUTE[Index].ROUT_QUALITY = Qual; // IF ZERO, SKIP UPDATE
// IT IS POSSIBLE ROUTES ARE NOW OUT OF ORDER
SORTROUTES:
if (DEST->NRROUTE[1].ROUT_QUALITY > DEST->NRROUTE[0].ROUT_QUALITY)
{
// SWAP 1 AND 2
Temp = DEST->NRROUTE[0];
DEST->NRROUTE[0] = DEST->NRROUTE[1];
DEST->NRROUTE[1] = Temp;
DEST->DEST_ROUTE = 0; // FORCE A RE-ASSESSMENT
}
if (DEST->NRROUTE[2].ROUT_QUALITY > DEST->NRROUTE[1].ROUT_QUALITY)
{
// SWAP 2 AND 3
Temp = DEST->NRROUTE[1];
DEST->NRROUTE[1] = DEST->NRROUTE[2];
DEST->NRROUTE[2] = Temp;
goto SORTROUTES; // 1 AND 2 MAY NOW BE WRONG!
}
}
int COUNTNODES(struct ROUTE * ROUTE)
{
// COUNT NODES WITH ROUTE VIA NEIGHBOUR
int count = 0;
int n = MAXDESTS;
struct DEST_LIST * DEST = DESTS; // NODE LIST
while (n--)
{
if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == ROUTE)
count++;
else if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == ROUTE)
count++;
else if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == ROUTE)
count++;
else if (DEST->ROUTE[0].ROUT_NEIGHBOUR == ROUTE)
count++;
else if (DEST->ROUTE[1].ROUT_NEIGHBOUR == ROUTE)
count++;
else if (DEST->ROUTE[2].ROUT_NEIGHBOUR == ROUTE)
count++;
DEST++;
}
return count;
}
VOID SENDNODE00();
VOID L3BG();
VOID SENDNEXTNODESFRAGMENT();
VOID SENDNODESMSG()
{
if (NODESINPROGRESS)
return;
L3CURRENTPORT = PORTTABLE;
SENDNEXTNODESFRAGMENT();
}
int fragmentCount = 0; // Node broadcast packets sent to current port
VOID SENDNEXTNODESFRAGMENT()
{
// SEND NEXT FRAGMENT OF A NODES BROADCAST
// FRAGMENTS ARE SENT AT 10 SECONDS INTERVALS - PARTLY TO REDUCE
// QRM, PARTLY TO REDUCE LOAD ON BUFFERS (AND TX POWER SUPPLIES!)
// SEND TO PORT IN CURRENTPORT, STARTING AT CURRENTNODE
struct PORTCONTROL * PORT = L3CURRENTPORT;
dest_list * DEST = CURRENTNODE;
MESSAGE * Buffer;
int Count;
int Qual;
int CURRENTPORTNO, TXMINQUAL;
UCHAR * ptr1, * ptr2;
if (DEST == 0)
{
// FIRST FRAGMENT TO A PORT
fragmentCount = 0;
while (PORT->PORTQUALITY == 0 || PORT->TXPORT || PORT->INP3ONLY)
// Don't send NODES to Shared TX or INP3 Port
{
// No NODES to this port, so go to next
PORT = PORT->PORTPOINTER;
if (PORT == NULL)
{
// Finished
NODESINPROGRESS = 0;
return;
}
}
L3CURRENTPORT = PORT;
DEST = CURRENTNODE = DESTS; // START OF LIST
NODESINPROGRESS = 1;
}
CURRENTPORTNO = PORT->PORTNUMBER;
TXMINQUAL = PORT->PORTMINQUAL;
if (TXMINQUAL == 0)
TXMINQUAL = 1; // Dont send zero
Buffer = GetBuff();
if (Buffer == 0)
return;
Buffer->PORT = CURRENTPORTNO;
memcpy(Buffer->ORIGIN, NETROMCALL, 7);
memcpy(Buffer->DEST, NODECALL, 7);
Buffer->ORIGIN[6] |= 0x61; // SET CMD END AND RESERVED BITS
Buffer->CTL = UI;
Buffer->PID = 0xCF; // Netrom
ptr1 = &Buffer->L2DATA[0];
*(ptr1++) = 0xff; // Nodes Flag
memcpy(ptr1, MYALIASTEXT, 6);
ptr1+= 6;
// ADD DESTINATION INFO (UNLESS BBS ONLY)
// If NODE = 0 just send application nodes
Count = PORT->NODESPACLEN;
if (Count == 0)
Count = 256;
if (Count < 50) // STUPIDLY SMALL?
Count = 50; // EVEN THIS IS RATHER SILLY
Count -= 22; // Fixed Part
Count /= 21; // 21 Bytres per entry
while (Count)
{
if (DEST >= ENDDESTLIST)
{
CURRENTNODE = 0; // Finished on this port
L3CURRENTPORT = PORT->PORTPOINTER;
if (L3CURRENTPORT == NULL)
{
// Finished
NODESINPROGRESS = 0;
}
goto Sendit;
}
if (DEST->NRROUTE[0].ROUT_QUALITY >= TXMINQUAL &&
DEST->NRROUTE[0].ROUT_OBSCOUNT >= OBSMIN &&
(NODE == 1 || DEST->DEST_STATE & 0x80)) // Only send appl nodes if DEST = 0;
{
// Send it
ptr2 = &DEST->DEST_CALL[0];
memcpy(ptr1, ptr2, 13); // Dest and Alias
ptr1 += 13;
ptr2 = (UCHAR *)DEST->NRROUTE[0].ROUT_NEIGHBOUR;
if (ptr2 == 0)
// DUMMY POINTER IN BBS ENTRY - PUT IN OUR CALL
ptr2 = MYCALL;
memcpy(ptr1, ptr2, 7); // Neighbour Call
ptr1 += 7;
Qual = 100;
if (DEST->NRROUTE[0].ROUT_NEIGHBOUR && DEST->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_PORT == CURRENTPORTNO)
{
// BEST NEIGHBOUR IS ON CURRENT PORT - REDUCE QUALITY BY QUAL_ADJUST
Qual -= PORT->QUAL_ADJUST;
}
Qual *= DEST->NRROUTE[0].ROUT_QUALITY;
Qual /= 100;
*(ptr1++) = (UCHAR)Qual;
Count--;
}
DEST++;
}
CURRENTNODE = DEST;
Sendit:
Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer);
if (Buffer->LENGTH > 35 || fragmentCount == 0) // Always send first even if no other nodes
{
PUT_ON_PORT_Q(PORT, Buffer);
fragmentCount++;
}
else
ReleaseBuffer(Buffer);
}
VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason)
{
// L2 SESSION HAS SHUT DOWN (PROBABLY DUE TO INACTIVITY)
struct ROUTE * ROUTE;
// CLEAR NEIGHBOUR
ROUTE = LINK->NEIGHBOUR; // TO NEIGHBOUR
if (ROUTE)
{
LINK->NEIGHBOUR = NULL; // Clear links
ROUTE->NEIGHBOUR_LINK = NULL;
CLEARACTIVEROUTE(ROUTE, Reason); // CLEAR ASSOCIATED DEST ENTRIES
}
}
VOID CLEARACTIVEROUTE(struct ROUTE * ROUTE, int Reason)
{
// FIND ANY DESINATIONS WITH [ESI] AS ACTIVE NEIGHBOUR, AND
// SET INACTIVE
dest_list * DEST;
int n;
if (Reason != NORMALCLOSE || ROUTE->INP3Node)
TellINP3LinkGone(ROUTE);
DEST = DESTS;
n = MAXDESTS;
DEST--; // So we can increment at start of loop
while (n--)
{
DEST++;
if (DEST->DEST_ROUTE == 0)
continue;
if (DEST->ROUTE[DEST->DEST_ROUTE].ROUT_NEIGHBOUR == ROUTE) // Is this the active route
{
// Yes, so clear
DEST->DEST_ROUTE = 0; // SET NO ACTIVE ROUTE
}
}
}
VOID L3TimerProc()
{
int i;
struct PORTCONTROL * PORT = PORTTABLE;
struct ROUTE * ROUTE;
struct _LINKTABLE * LINK;
// CHECK FOR EXCESSIVE BUFFERS QUEUED AT LINK LEVEL
if (QCOUNT < 100)
{
LINK = LINKS;
i = MAXLINKS;
while (i--);
{
if (LINK->LINKTYPE == 3) // Only if Internode
{
if (COUNT_AT_L2(LINK) > 50)
{
Debugprintf("Excessive L3 Queue");
L3LINKCLOSED(LINK, LINKSTUCK); // REPORT TO LEVEL 3
CLEAROUTLINK(LINK);
}
}
LINK++;
}
}
STATSTIME++;
if (IDTIMER) // Not if Disabled
{
IDTIMER--;
if (IDTIMER == 0)
{
IDTIMER = IDINTERVAL;
SENDIDMSG();
}
}
// CHECK FOR BEACON
if (BTTIMER) // Not if Disabled
{
BTTIMER--;
if (BTTIMER == 0)
{
BTTIMER = BTINTERVAL;
SENDBTMSG();
}
}
// CHECK FOR NODES BROADCAST
if (L3TIMER) // Not if Disabled
{
L3TIMER--;
if (L3TIMER == 0)
{
// UPDATE DEST LIST AND SEND 'NODES' MESSAGE
L3TIMER = L3INTERVAL;
UPDATEDESTLIST();
SENDNODESMSG();
}
}
// TIDY ROUTES
ROUTE = NEIGHBOURS;
i = MAXNEIGHBOURS;
ROUTE--;
while (i--)
{
ROUTE++;
if (ROUTE->NEIGHBOUR_FLAG & 1) // Locked?
continue;
if (ROUTE->NEIGHBOUR_LINK) // Has an active Session
continue;
if (COUNTNODES(ROUTE) == 0) // NODES USING THIS DESTINATION
{
// IF NUMBER USING ROUTE IS ZERO, DELETE IT
memset(ROUTE, 0, sizeof (struct ROUTE));
}
}
}
VOID L3FastTimer()
{
// CALLED ONCE PER SECOND - USED ONLY TO SEND NEXT PART OF A NODES OR
// ID MESSAGE SEQUENCE
MESSAGE * Msg;
struct PORTCONTROL * PORT ;
INP3TIMER();
L3_10SECS--;
if (L3_10SECS == 0)
{
L3_10SECS = 10;
if (IDMSG_Q) // ID/BEACON TO SEND
{
Msg = Q_REM(&IDMSG_Q);
PORT = GetPortTableEntryFromPortNum(Msg->PORT);
if (PORT && PORT->TXPORT == 0) // DONT SEND IF SHARED TX
PUT_ON_PORT_Q(PORT, Msg);
else
ReleaseBuffer(Msg);
}
if (NODESINPROGRESS)
SENDNEXTNODESFRAGMENT();
}
}
VOID UPDATEDESTLIST()
{
// DECREMENT OBS COUNTERS ON EACH ROUTE, AND REMOVE 'DEAD' ENTRIES
dest_list * DEST;
int n;
DEST = DESTS;
n = MAXDESTS;
DEST--; // So we can increment at start of loop
while (n--)
{
DEST++;
if (DEST->DEST_CALL[0] == 0) // SPARE ENTRY
continue;
if (DEST->DEST_STATE & 0x80) // LOCKED DESTINATION
continue;
UPDEST000:
if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == 0) // NO DESTINATIONS - DELETE ENTRY unless inp3 routes
{
// Any INP3 Routes?
if (DEST->ROUTE[0].ROUT_NEIGHBOUR == 0)
{
// NO ROUTES LEFT TO DEST - REMOVE IT
REMOVENODE(DEST); // Unchain, Clear queue and zap
}
continue;
}
if (DEST->NRROUTE[0].ROUT_OBSCOUNT == 0)
{
// FAILED IN USE - DELETE
MOVEALL(DEST);
goto UPDEST000; //LOOP BACK TO PROCESS MOVED ENTRIES
}
if ((DEST->NRROUTE[0].ROUT_OBSCOUNT & 0x80) == 0) // Locked?
{
DEST->NRROUTE[0].ROUT_OBSCOUNT--;
if (DEST->NRROUTE[0].ROUT_OBSCOUNT == 0) // Timed out
{
MOVEALL(DEST);
goto UPDEST000; //LOOP BACK TO PROCESS MOVED ENTRIES
}
}
// Process Next Neighbour
UPDEST010:
if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0)
continue; // NO MORE DESTINATIONS
if (DEST->NRROUTE[1].ROUT_OBSCOUNT == 0)
{
// FAILED IN USE - DELETE
MOVE3TO2(DEST);
goto UPDEST010; //LOOP BACK TO PROCESS MOVED ENTRIES
}
if ((DEST->NRROUTE[1].ROUT_OBSCOUNT & 0x80) == 0) // Locked?
{
DEST->NRROUTE[1].ROUT_OBSCOUNT--;
if (DEST->NRROUTE[1].ROUT_OBSCOUNT == 0) // Timed out
{
MOVE3TO2(DEST);
goto UPDEST010; //LOOP BACK TO PROCESS MOVED ENTRIES
}
}
// Process Next Neighbour
if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == 0)
continue; // NO MORE DESTINATIONS
if (DEST->NRROUTE[2].ROUT_OBSCOUNT == 0)
{
// FAILED IN USE - DELETE
CLEARTHIRD(DEST);
continue;
}
if ((DEST->NRROUTE[2].ROUT_OBSCOUNT & 0x80) == 0) // Locked?
{
DEST->NRROUTE[2].ROUT_OBSCOUNT--;
if (DEST->NRROUTE[2].ROUT_OBSCOUNT == 0) // Timed out
{
CLEARTHIRD(DEST);
continue;
}
}
}
}
VOID MOVEALL(dest_list * DEST)
{
DEST->NRROUTE[0] = DEST->NRROUTE[1];
MOVE3TO2(DEST);
}
VOID MOVE3TO2(dest_list * DEST)
{
DEST->NRROUTE[1] = DEST->NRROUTE[2];
CLEARTHIRD(DEST);
}
VOID CLEARTHIRD(dest_list * DEST)
{
memset(&DEST->NRROUTE[2], 0, sizeof (struct NR_DEST_ROUTE_ENTRY));
DEST->DEST_ROUTE = 0; // CANCEL ACTIVE ROUTE, SO WILL RE-ASSESS BEST
}
// L4 Flags Values
#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED
VOID REMOVENODE(dest_list * DEST)
{
TRANSPORTENTRY * L4 = L4TABLE;
int n = MAXCIRCUITS;
// Remove a node, either because routes have gone, or APPL API has invalidated it
while (DEST->DEST_Q)
ReleaseBuffer(Q_REM(&DEST->DEST_Q));
// MAY NEED TO CHECK FOR L4 CIRCUITS USING DEST, BUT PROBABLY NOT,
// AS THEY SHOULD HAVE TIMED OUT LONG AGO
// Not necessarily true with INP3, so had better check
while (n--)
{
if (L4->L4USER[0])
{
if (L4->L4TARGET.DEST == DEST)
{
// Session to/from this Dest
TRANSPORTENTRY * Partner = L4->L4CROSSLINK;
char Nodename[20];
Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate
Debugprintf("Delete Node for %s Called with active L4 Session - State %d",
Nodename, L4->L4STATE);
if (Partner)
{
// if connnecting, send error message and drop back to command level
if (L4->L4STATE == 2)
{
struct DATAMESSAGE * Msg = GetBuff();
Partner->L4CROSSLINK = 0; // Back to command lewel
if (Msg)
{
UCHAR * ptr1;
Msg->PID = 0xf0;
ptr1 = SetupNodeHeader(Msg);
ptr1 += sprintf(ptr1, "Error - Node %s has disappeared\r", Nodename);
Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg);
C_Q_ADD(&Partner->L4TX_Q, Msg);
PostDataAvailable(Partner);
}
}
else
{
// Failed in session - treat as if a L4DREQ received
CLOSECURRENTSESSION(Partner);
}
}
CLEARSESSIONENTRY(L4);
}
}
L4++;
}
memset(DEST, 0, sizeof(struct DEST_LIST));
NUMBEROFNODES--;
}
VOID L3CONNECTFAILED(struct _LINKTABLE * LINK)
{
// L2 LINK SETUP HAS FAILED - SEE IF ANOTHER NEIGHBOUR CAN BE USED
struct PORTCONTROL * PORT = PORTTABLE;
struct ROUTE * ROUTE;
ROUTE = LINK->NEIGHBOUR; // TO NEIGHBOUR
if (ROUTE == NULL)
return; // NOTHING ???
TellINP3LinkSetupFailed(ROUTE);
ROUTE->NEIGHBOUR_LINK = 0; // CLEAR IT
L3TRYNEXTDEST(ROUTE); // RESET ASSOCIATED DEST ENTRIES
}
VOID L3TRYNEXTDEST(struct ROUTE * ROUTE)
{
// FIND ANY DESINATIONS WITH [ESI] AS ACTIVE NEIGHBOUR, AND
// SET NEXT BEST NEIGHBOUR (IF ANY) ACTIVE
int n = MAXDESTS;
struct DEST_LIST * DEST = DESTS; // NODE LIST
int ActiveRoute;
while (n--)
{
ActiveRoute = DEST->DEST_ROUTE;
if (ActiveRoute)
{
ActiveRoute --; // Routes numbered 1 - 6, idex from 0
if (DEST->NRROUTE[ActiveRoute].ROUT_NEIGHBOUR == ROUTE)
{
// We were best
// NEIGHBOUR HAS FAILED - DECREMENT OBSCOUNT
// AND TRY TO ACTIVATE ANOTHER (IF ANY)
if (DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT)
{
// IF ALREADY ZERO - WILL BE DELETED BY NEXT NODES UPDATE
if ((DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT & 0x80) == 0)
{
// not Locked
DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT--;
// if ROUTE HAS EXPIRED - WE SHOULD CLEAR IT, AND MOVE OTHERS (IF ANY) UP
}
}
// REMOVE FIRST MESSAGE FROM DEST_Q. L4 WILL RETRY - IF IT IS LEFT HERE
// WE WILL TRY TO ACTIVATE THE DESTINATION FOR EVER
if (DEST->DEST_Q)
ReleaseBuffer(Q_REM(&DEST->DEST_Q));
DEST->DEST_ROUTE++; // TO NEXT
if (DEST->DEST_ROUTE = 7)
DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST
}
}
DEST++;
}
}
VOID CHECKNEIGHBOUR(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg)
{
// MESSAGE RECEIVED ON LINK WITH NO ROUTE - SET ONE UP
struct ROUTE * ROUTE;
struct PORTCONTROL * PORT = LINK->LINKPORT;
int Portno = PORT->PORTNUMBER;
if (FindNeighbour(LINK->LINKCALL, Portno, &ROUTE) == 0)
{
if (ROUTE == 0)
goto L3CONN08; // TABLE FULL??
// FIRST MAKE SURE WE ARE ALLOWING NETWORK ACTIVITY ON THIS PORT
if (PORT->PORTQUALITY == 0 && PORT->INP3ONLY == 0)
return;
ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY;
ROUTE->INP3Node = PORT->INP3ONLY;
memcpy(ROUTE->NEIGHBOUR_CALL, LINK->LINKCALL, 7);
ROUTE->NEIGHBOUR_PORT = Portno;
ROUTE->NoKeepAlive = PORT->PortNoKeepAlive;
}
// SET THIS AS ACTIVE LINK IF NONE PRESENT
if (ROUTE->NEIGHBOUR_LINK == 0)
{
ROUTE->NEIGHBOUR_LINK = LINK;
ROUTE->NEIGHBOUR_PORT = Portno;
}
L3CONN08:
LINK->NEIGHBOUR = ROUTE; // SET LINK - NEIGHBOUR
}
struct DEST_LIST * CHECKL3TABLES(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg)
{
// CHECK THAT FAR NODE IS IN 'NODES'.
// RETURNS POINTER TO NEIGHBOUR ENTRY
struct ROUTE * ROUTE;
struct DEST_LIST * DEST;
int Qual;
ROUTE = LINK->NEIGHBOUR;
if (FindDestination(Msg->L3SRCE, &DEST))
return DEST; // Ok
if (DEST == NULL)
return NULL; // Tsble Full
// ADD DESTINATION VIA NEIGHBOUR, UNLESS ON BLACK LIST
#ifdef EXCLUDEBITS
if (CheckExcludeList(Msg->L3SRCE) == 0)
return 0;
#endif
memcpy(DEST->DEST_CALL, Msg->L3SRCE, 7);
NUMBEROFNODES++;
// MAKE SURE NEIGHBOUR IS DEFINED FOR DESTINATION
// IF NODE is NEIGHBOUR, THEN CAN USE NEIGHBOUR QUALITY,
// OTHERWISE WE DONT KNOW ROUTE, SO MUST SET QUAL TO 0
Qual = 0; // DONT KNOW ROUTING, SO SET QUALITY TO ZERO
PROCROUTES(DEST, ROUTE, Qual); // ADD NEIGHBOUR IF NOT PRESENT
if (DEST->DEST_ROUTE == 0)
{
// MAKE CURRENT NEIGHBOUR ACTIVE
int n = 0;
DEST->DEST_ROUTE = 1;
while (n < 3)
{
if (DEST->NRROUTE[n].ROUT_NEIGHBOUR == ROUTE)
break;
DEST->DEST_ROUTE++;
n++;
}
if (DEST->DEST_ROUTE > 3)
{
DEST->DEST_ROUTE = 1; // CURRENT NEIGHBOUR ISNT IN DEST LIST - SET TO USE BEST
return DEST; // Can't update OBS
}
}
// REFRESH OBS COUNT
if (DEST->DEST_ROUTE)
{
int Index = DEST->DEST_ROUTE -1;
if (DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80) // Locked:
return DEST;
DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT;
}
return DEST;
}
VOID REFRESHROUTE(TRANSPORTENTRY * Session)
{
// RESET OBS COUNT ON CURRENT ROUTE TO DEST FOR SESSION IN [EBX]
// CALLED WHEN INFO ACK RECEIVED, INDICATING ROUTE IS STILL OK
struct DEST_LIST * DEST;
int Index;
DEST = Session->L4TARGET.DEST;
if (DEST == 0)
return; // No Dest ???
Index = DEST->DEST_ROUTE;
if (Index == 0)
return; // NONE ACTIVE???
Index--;
if (DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80)
return; // Locked
DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT;
}