1388 lines
28 KiB
C
1388 lines
28 KiB
C
/*
|
|
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"
|
|
|
|
uint64_t timeLoadedMS = 0;
|
|
|
|
|
|
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;
|
|
|
|
int COUNTNODES(struct ROUTE * ROUTE);
|
|
|
|
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() - timeLoadedMS) - 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
|
|
|
|
Stamp = time(NULL) % 86400; // Secs into day
|
|
HH = Stamp / 3600;
|
|
|
|
Stamp -= HH * 3600;
|
|
MM = Stamp / 60;
|
|
|
|
Route->NEIGHBOUR_TIME = 256 * HH + MM;
|
|
|
|
while (msglen > 0)
|
|
{
|
|
if (msglen < 10)
|
|
{
|
|
Debugprintf("Corrupt INP3 Message");
|
|
return;
|
|
}
|
|
|
|
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++;
|
|
|
|
// rtt is value from remote node. Add our RTT to that node and update hops
|
|
|
|
rtt += Route->SRTT;
|
|
hops++;
|
|
|
|
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, "%10llu %10d %10d %10d ", (GetTickCount() - timeLoadedMS), 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;
|
|
}
|
|
|