1169 lines
31 KiB
C
1169 lines
31 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
|
|
*/
|
|
|
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
|
|
|
#define _CRT_SECURE_NO_DEPRECATE
|
|
|
|
#include "compatbits.h"
|
|
#include <string.h>
|
|
#include "asmstrucs.h"
|
|
#include "tncinfo.h"
|
|
#include "cheaders.h"
|
|
|
|
VOID __cdecl Debugprintf(const char * format, ...);
|
|
|
|
#ifndef WIN32
|
|
|
|
#define APIENTRY
|
|
#define DllExport
|
|
#define VOID void
|
|
|
|
#else
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
extern BOOL EventsEnabled;
|
|
void MQTTReportSession(char * Msg);
|
|
extern int MQTT;
|
|
extern time_t TimeLoaded;
|
|
|
|
int UDPSeq = 1;
|
|
|
|
extern SOCKET NodeAPISocket;
|
|
extern SOCKADDR_IN UDPreportdest;
|
|
|
|
extern char Modenames[19][10];
|
|
|
|
extern char NODECALLLOPPED[10];
|
|
extern char MYALIASLOPPED[10];
|
|
extern char LOC[7];
|
|
extern char VersionString[50];
|
|
extern double LatFromLOC;
|
|
extern double LonFromLOC;
|
|
|
|
void hookL2SessionClosed(struct _LINKTABLE * LINK, char * Reason, char * Direction);
|
|
int ConvFromAX25(unsigned char * incall, unsigned char * outcall);
|
|
int COUNT_AT_L2(struct _LINKTABLE * LINK);
|
|
int CountFramesQueuedOnSession(TRANSPORTENTRY * Session);
|
|
int decodeNETROMUIMsg(unsigned char * Msg, int iLen, char * Buffer, int BufferLen);
|
|
int decodeNETROMIFrame(unsigned char * Msg, int iLen, char * Buffer, int BufferLen);
|
|
int decodeINP3RIF(unsigned char * Msg, int iLen, char * Buffer, int BufferLen);
|
|
int decodeRecordRoute(L3MESSAGE * L3, int iLen, char * Buffer, int BufferLen);
|
|
|
|
// Runs use specified routine on certain event
|
|
|
|
#ifndef WIN32
|
|
|
|
void RunEventProgram(char * Program, char * Param)
|
|
{
|
|
char * arg_list[] = {Program, NULL, NULL};
|
|
pid_t child_pid;
|
|
|
|
if (EventsEnabled == 0)
|
|
return;
|
|
|
|
signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children.
|
|
|
|
if (Param && Param[0])
|
|
arg_list[1] = Param;
|
|
|
|
// Fork and Exec Specified program
|
|
|
|
// Duplicate this process.
|
|
|
|
child_pid = fork ();
|
|
|
|
if (child_pid == -1)
|
|
{
|
|
printf ("Event fork() Failed\n");
|
|
return;
|
|
}
|
|
|
|
if (child_pid == 0)
|
|
{
|
|
execvp (arg_list[0], arg_list);
|
|
|
|
// The execvp function returns only if an error occurs.
|
|
|
|
printf ("Failed to run %s\n", arg_list[0]);
|
|
exit(0); // Kill the new process
|
|
}
|
|
|
|
#else
|
|
|
|
DllExport void APIENTRY RunEventProgram(char * Program, char * Param)
|
|
{
|
|
int n = 0;
|
|
char cmdLine[256];
|
|
|
|
STARTUPINFO SInfo; // pointer to STARTUPINFO
|
|
PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION
|
|
|
|
if (EventsEnabled == 0)
|
|
return;
|
|
|
|
|
|
SInfo.cb=sizeof(SInfo);
|
|
SInfo.lpReserved=NULL;
|
|
SInfo.lpDesktop=NULL;
|
|
SInfo.lpTitle=NULL;
|
|
SInfo.dwFlags=0;
|
|
SInfo.cbReserved2=0;
|
|
SInfo.lpReserved2=NULL;
|
|
|
|
sprintf(cmdLine, "%s %s", Program, Param);
|
|
|
|
if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo))
|
|
Debugprintf("Failed to Start %s Error %d ", Program, GetLastError());
|
|
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK)
|
|
{
|
|
// Incoming SABM accepted
|
|
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
|
|
LINK->ConnectTime = time(NULL);
|
|
LINK->bytesTXed = LINK->bytesRXed = LINK->framesResent = LINK->framesRXed = LINK->framesTXed = 0;
|
|
|
|
strcpy(LINK->callingCall, remotecall);
|
|
strcpy(LINK->receivingCall, ourcall);
|
|
strcpy(LINK->Direction, "In");
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
LINK->lastStatusSentTime = time(NULL);
|
|
|
|
udplen = sprintf(UDPMsg, "{\"@type\":\"LinkUpEvent\", \"node\": \"%s\", \"id\": %d, \"direction\": \"incoming\", \"port\": \"%d\", \"remote\": \"%s\", \"local\": \"%s\"}",
|
|
NODECALLLOPPED, UDPSeq++, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
|
|
void hookL2SessionDeleted(struct _LINKTABLE * LINK)
|
|
{
|
|
// calculate session time and av bytes/min in and out
|
|
|
|
if (LINK->ConnectTime)
|
|
{
|
|
if (LINK->bytesTXed == 0 && LINK->bytesRXed == 0)
|
|
{
|
|
// assume failed connect and ignore for now - maybe log later
|
|
|
|
}
|
|
else
|
|
{
|
|
char Msg[256];
|
|
char timestamp[64];
|
|
time_t sessionTime = time(NULL) - LINK->ConnectTime;
|
|
double avBytesSent = LINK->bytesTXed / (sessionTime / 60.0);
|
|
double avBytesRXed = LINK->bytesRXed / (sessionTime / 60.0);
|
|
time_t Now = time(NULL);
|
|
struct tm * TM = localtime(&Now);
|
|
|
|
sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec);
|
|
|
|
if (sessionTime == 0)
|
|
sessionTime = 1; // Or will get divide by zero error
|
|
|
|
Debugprintf("KISS Session Stats Port %d %s %s %d secs Bytes Sent %d BPM %4.2f Bytes Received %d %4.2f BPM ",
|
|
LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp);
|
|
|
|
|
|
sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d,"
|
|
"\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}",
|
|
"KISS", LINK->Direction, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime,
|
|
LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp);
|
|
|
|
if (MQTT)
|
|
MQTTReportSession(Msg);
|
|
|
|
LINK->ConnectTime = 0;
|
|
}
|
|
|
|
if (LINK->Sent && LINK->Received && (LINK->SentAfterCompression || LINK->ReceivedAfterExpansion))
|
|
Debugprintf("L2 Compression Stats %s %s TX %d %d %d%% RX %d %d %d%%", LINK->callingCall, LINK->receivingCall,
|
|
LINK->Sent, LINK->SentAfterCompression, ((LINK->Sent - LINK->SentAfterCompression) * 100) / LINK->Sent,
|
|
LINK->Received, LINK->ReceivedAfterExpansion, ((LINK->ReceivedAfterExpansion - LINK->Received) * 100) / LINK->Received);
|
|
|
|
}
|
|
}
|
|
|
|
void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK)
|
|
{
|
|
LINK->ConnectTime = time(NULL);
|
|
LINK->bytesTXed = LINK->bytesRXed = LINK->framesResent = LINK->framesRXed = LINK->framesTXed = 0;
|
|
|
|
strcpy(LINK->callingCall, ourcall);
|
|
strcpy(LINK->receivingCall, remotecall);
|
|
strcpy(LINK->Direction, "Out");
|
|
}
|
|
|
|
void hookL2SessionConnected(struct _LINKTABLE * LINK)
|
|
{
|
|
// UA received in reponse to SABM
|
|
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
LINK->lastStatusSentTime = time(NULL);
|
|
|
|
udplen = sprintf(UDPMsg, "{\"@type\":\"LinkUpEvent\", \"node\": \"%s\", \"id\": %d, \"direction\": \"outgoing\", \"port\": \"%d\", \"remote\": \"%s\", \"local\": \"%s\"}",
|
|
NODECALLLOPPED, UDPSeq++, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
void hookL2SessionClosed(struct _LINKTABLE * LINK, char * Reason, char * Direction)
|
|
{
|
|
// Link closed. Could be normal, ie disc send/received or restried out etc
|
|
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
if (LINK->receivingCall[0] == 0 || LINK->callingCall[0] == 0)
|
|
return;
|
|
|
|
if (strcmp(Direction, "Out") == 0)
|
|
udplen = sprintf(UDPMsg, "{\"@type\":\"LinkDownEvent\", \"node\": \"%s\", \"id\": %d, \"direction\": \"outgoing\", \"port\": \"%d\", \"remote\": \"%s\", \"local\": \"%s\","
|
|
"\"bytesSent\": %d, \"bytesRcvd\": %d, \"frmsSent\": %d, \"frmsRcvd\": %d, \"frmsQueued\": %d, \"frmsResent\": %d, \"reason\": \"%s\"}",
|
|
NODECALLLOPPED, UDPSeq++, LINK->LINKPORT->PORTNUMBER, LINK->receivingCall, LINK->callingCall,
|
|
LINK->bytesTXed , LINK->bytesRXed, LINK->framesTXed, LINK->framesRXed, COUNT_AT_L2(LINK), LINK->framesResent, Reason);
|
|
else
|
|
udplen = sprintf(UDPMsg, "{\"@type\":\"LinkDownEvent\", \"node\": \"%s\", \"id\": %d, \"direction\": \"incoming\", \"port\": \"%d\", \"remote\": \"%s\", \"local\": \"%s\","
|
|
"\"bytesSent\": %d, \"bytesRcvd\": %d, \"frmsSent\": %d, \"frmsRcvd\": %d, \"frmsQueued\": %d, \"frmsResent\": %d, \"reason\": \"%s\"}",
|
|
NODECALLLOPPED, UDPSeq++, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall,
|
|
LINK->bytesTXed , LINK->bytesRXed, LINK->framesTXed, LINK->framesRXed, COUNT_AT_L2(LINK), LINK->framesResent, Reason);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
void hookL2SessionStatus(struct _LINKTABLE * LINK)
|
|
{
|
|
// Send at regular intervals on open links
|
|
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
LINK->lastStatusSentTime = time(NULL);
|
|
|
|
if (strcmp(LINK->Direction, "Out") == 0)
|
|
udplen = sprintf(UDPMsg, "{\"@type\":\"LinkStatus\", \"node\": \"%s\", \"id\": %d, \"direction\": \"outgoing\", \"port\": \"%d\", \"remote\": \"%s\", \"local\": \"%s\","
|
|
"\"bytesSent\": %d, \"bytesRcvd\": %d, \"frmsSent\": %d, \"frmsRcvd\": %d, \"frmsQueued\": %d, \"frmsResent\": %d}",
|
|
NODECALLLOPPED, UDPSeq++, LINK->LINKPORT->PORTNUMBER, LINK->receivingCall, LINK->callingCall,
|
|
LINK->bytesTXed , LINK->bytesRXed, LINK->framesTXed, LINK->framesRXed, 0, LINK->framesResent);
|
|
else
|
|
udplen = sprintf(UDPMsg, "{\"@type\":\"LinkStatus\", \"node\": \"%s\", \"id\": %d, \"direction\": \"incoming\", \"port\": \"%d\", \"remote\": \"%s\", \"local\": \"%s\","
|
|
"\"bytesSent\": %d, \"bytesRcvd\": %d, \"frmsSent\": %d, \"frmsRcvd\": %d, \"frmsQueued\": %d, \"frmsResent\": %d}",
|
|
NODECALLLOPPED, UDPSeq++, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall,
|
|
LINK->bytesTXed , LINK->bytesRXed, LINK->framesTXed, LINK->framesRXed, COUNT_AT_L2(LINK), LINK->framesResent);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void hookL4SessionAttempt(struct STREAMINFO * STREAM, char * remotecall, char * ourcall)
|
|
{
|
|
// Outgoing Connect
|
|
|
|
STREAM->ConnectTime = time(NULL);
|
|
STREAM->bytesTXed = STREAM->bytesRXed = 0;
|
|
|
|
strcpy(STREAM->callingCall, ourcall);
|
|
strcpy(STREAM->receivingCall, remotecall);
|
|
strcpy(STREAM->Direction, "Out");
|
|
}
|
|
|
|
void hookL4SessionAccepted(struct STREAMINFO * STREAM, char * remotecall, char * ourcall)
|
|
{
|
|
// Incoming Connect
|
|
|
|
STREAM->ConnectTime = time(NULL);
|
|
STREAM->bytesTXed = STREAM->bytesRXed = 0;
|
|
|
|
strcpy(STREAM->callingCall, remotecall);
|
|
strcpy(STREAM->receivingCall, ourcall);
|
|
strcpy(STREAM->Direction, "In");
|
|
}
|
|
|
|
/*
|
|
{
|
|
"eventSource": "circuit",
|
|
"time": "2025-10-08T14:05:54+00:00",
|
|
"id": 23,
|
|
"direction": "incoming",
|
|
"port": "0",
|
|
"remote": "G8PZT@G8PZT:15aa",
|
|
"local": "GE8PZT:0017",
|
|
"event": "disconnect",
|
|
"@type": "event"
|
|
}
|
|
*/
|
|
|
|
void hookL4SessionDeleted(struct TNCINFO * TNC, struct STREAMINFO * STREAM)
|
|
{
|
|
char Msg[256];
|
|
|
|
char timestamp[16];
|
|
|
|
if (STREAM->ConnectTime)
|
|
{
|
|
time_t sessionTime = time(NULL) - STREAM->ConnectTime;
|
|
double avBytesRXed = STREAM->bytesRXed / (sessionTime / 60.0);
|
|
double avBytesSent = STREAM->bytesTXed / (sessionTime / 60.0);
|
|
time_t Now = time(NULL);
|
|
struct tm * TM = localtime(&Now);
|
|
sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec);
|
|
|
|
if (sessionTime == 0)
|
|
sessionTime = 1; // Or will get divide by zero error
|
|
|
|
sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d,"
|
|
"\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}",
|
|
Modenames[TNC->Hardware - 1], STREAM->Direction, TNC->Port, STREAM->callingCall, STREAM->receivingCall, sessionTime,
|
|
STREAM->bytesTXed, avBytesSent, STREAM->bytesRXed, avBytesRXed, timestamp);
|
|
|
|
if (MQTT)
|
|
MQTTReportSession(Msg);
|
|
|
|
STREAM->ConnectTime = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void hookNodeStarted()
|
|
{
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
#ifdef LINBPQ
|
|
char Software[80] = "LinBPQ";
|
|
|
|
if (sizeof(void *) == 4)
|
|
strcat(Software, "(32 bit)");
|
|
#else
|
|
char Software[80] = "BPQ32";
|
|
#endif
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
int ret;
|
|
|
|
udplen = sprintf(UDPMsg, "{\"@type\": \"NodeUpEvent\", \"nodeCall\": \"%s\", \"nodeAlias\": \"%s\", \"locator\": \"%s\","
|
|
"\"latitude\": %f, \"longitude\": %f, \"software\": \"%s\", \"version\": \"%s\"}",
|
|
NODECALLLOPPED, MYALIASLOPPED, LOC, LatFromLOC, LonFromLOC, Software, VersionString);
|
|
|
|
ret = sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
|
|
if (ret != udplen)
|
|
Debugprintf("%d %d %s", ret, WSAGetLastError(), UDPMsg);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void hookNodeClosing(char * Reason)
|
|
{
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
udplen = sprintf(UDPMsg, "{\"@type\": \"NodeDownEvent\", \"nodeCall\": \"%s\", \"nodeAlias\": \"%s\", \"reason\": \"%s\"}",
|
|
NODECALLLOPPED, MYALIASLOPPED, Reason);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
void hookNodeRunning()
|
|
{
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
#ifdef LINBPQ
|
|
char Software[80] = "LinBPQ";
|
|
|
|
if (sizeof(void *) == 4)
|
|
strcat(Software, "(32 bit)");
|
|
#else
|
|
char Software[80] = "BPQ32";
|
|
#endif
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
|
|
udplen = sprintf(UDPMsg, "{\"@type\": \"NodeStatus\", \"nodeCall\": \"%s\", \"nodeAlias\": \"%s\", \"locator\": \"%s\","
|
|
"\"latitude\": %f, \"longitude\": %f, \"software\": \"%s\", \"version\": \"%s\", \"uptimeSecs\": %d}",
|
|
NODECALLLOPPED, MYALIASLOPPED, LOC, LatFromLOC, LonFromLOC, Software, VersionString, time(NULL) - TimeLoaded);
|
|
|
|
|
|
// Debugprintf(UDPMsg);
|
|
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
void IncomingL4ConnectionEvent(TRANSPORTENTRY * L4)
|
|
{
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
|
|
char remotecall[64];
|
|
char ourcall[64];
|
|
char circuitinfo[32];
|
|
|
|
// CACK sent to CREQ
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
remotecall[ConvFromAX25(L4->L4TARGET.DEST->DEST_CALL, remotecall)] = 0;
|
|
// remotecall[ConvFromAX25(L4->L4USER, remotecall)] = 0;
|
|
ourcall[ConvFromAX25(L4->L4MYCALL, ourcall)] = 0;
|
|
|
|
sprintf(circuitinfo, ":%02x%02x", L4->FARINDEX, L4->FARID);
|
|
strcat(remotecall, circuitinfo);
|
|
|
|
sprintf(circuitinfo, ":%02x%02x", L4->CIRCUITINDEX, L4->CIRCUITID);
|
|
strcat(ourcall, circuitinfo);
|
|
|
|
udplen = sprintf(UDPMsg, "{\"@type\": \"CircuitUpEvent\", \"node\": \"%s\", \"id\": %d, \"direction\": \"incoming\","
|
|
"\"service\": %d, \"remote\": \"%s\", \"local\": \"%s\"}",
|
|
NODECALLLOPPED, UDPSeq++, L4->Service, remotecall, ourcall);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
|
|
void OutgoingL4ConnectionEvent(TRANSPORTENTRY * L4)
|
|
{
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
char remotecall[64];
|
|
char ourcall[64];
|
|
char circuitinfo[32];
|
|
|
|
// CACK received
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
remotecall[ConvFromAX25(L4->L4TARGET.DEST->DEST_CALL, remotecall)] = 0;
|
|
// remotecall[ConvFromAX25(L4->L4USER, remotecall)] = 0;
|
|
ourcall[ConvFromAX25(L4->L4MYCALL, ourcall)] = 0;
|
|
|
|
sprintf(circuitinfo, ":%02x%02x", L4->FARID, L4->FARINDEX);
|
|
strcat(remotecall, circuitinfo);
|
|
|
|
sprintf(circuitinfo, ":%02x%02x", L4->CIRCUITID, L4->CIRCUITINDEX);
|
|
strcat(ourcall, circuitinfo);
|
|
|
|
udplen = sprintf(UDPMsg, "{\"@type\": \"CircuitUpEvent\", \"node\": \"%s\", \"id\": %d, \"direction\": \"outgoing\","
|
|
"\"service\": %d, \"remote\": \"%s\", \"local\": \"%s\"}",
|
|
NODECALLLOPPED, UDPSeq++, L4->Service, remotecall, ourcall);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
/*
|
|
{
|
|
"@type": "CircuitUpEvent",
|
|
"node": "G8PZT"
|
|
"id": 1,
|
|
"direction": "incoming",
|
|
"service": 0,
|
|
"remote": "G8PZT@G8PZT:14c0",
|
|
"local": "G8PZT-4:0001"
|
|
}
|
|
|
|
|
|
"segsSent": 5,
|
|
"segsRcvd": 27,
|
|
"segsResent": 0,
|
|
"segsQueued": 0,
|
|
"reason": "rcvd DREQ"
|
|
|
|
*/
|
|
|
|
void L4DisconnectEvent(TRANSPORTENTRY * L4, char * Direction, char * Reason)
|
|
{
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
char remotecall[64];
|
|
char ourcall[64];
|
|
char circuitinfo[32];
|
|
int Count;
|
|
|
|
// CACK received
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
remotecall[ConvFromAX25(L4->L4TARGET.DEST->DEST_CALL, remotecall)] = 0;
|
|
// remotecall[ConvFromAX25(L4->L4USER, remotecall)] = 0;
|
|
ourcall[ConvFromAX25(L4->L4MYCALL, ourcall)] = 0;
|
|
|
|
sprintf(circuitinfo, ":%02x%02x", L4->FARINDEX, L4->FARID);
|
|
strcat(remotecall, circuitinfo);
|
|
|
|
sprintf(circuitinfo, ":%02x%02x", L4->CIRCUITINDEX, L4->CIRCUITID);
|
|
strcat(ourcall, circuitinfo);
|
|
|
|
|
|
if (L4->L4CROSSLINK) // CONNECTED?
|
|
Count = CountFramesQueuedOnSession(L4->L4CROSSLINK);
|
|
else
|
|
Count = CountFramesQueuedOnSession(L4);
|
|
|
|
udplen = sprintf(UDPMsg, "{\"@type\": \"CircuitDownEvent\", \"node\": \"%s\", \"id\": %d, \"direction\": \"%s\","
|
|
"\"service\": %d, \"remote\": \"%s\", \"local\": \"%s\", \"segsSent\": %d, \"segsRcvd\": %d, \"segsResent\": %d, \"segsQueued\": %d, \"reason\": \"%s\"}",
|
|
NODECALLLOPPED, UDPSeq++, Direction, 0, remotecall, ourcall,L4->segsSent, L4->segsRcvd, L4->segsResent, Count, Reason);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
void L4StatusSeport(TRANSPORTENTRY * L4)
|
|
{
|
|
char UDPMsg[1024];
|
|
int udplen;
|
|
char remotecall[64];
|
|
char ourcall[64];
|
|
char nodecall[16];
|
|
char circuitinfo[32];
|
|
int Count;
|
|
|
|
// CACK received
|
|
|
|
if (NodeAPISocket)
|
|
{
|
|
nodecall[ConvFromAX25(L4->L4TARGET.DEST->DEST_CALL, nodecall)] = 0;
|
|
remotecall[ConvFromAX25(L4->L4USER, remotecall)] = 0;
|
|
ourcall[ConvFromAX25(L4->L4MYCALL, ourcall)] = 0;
|
|
|
|
sprintf(circuitinfo, ":%02x%02x", L4->FARINDEX, L4->FARID);
|
|
strcat(remotecall, circuitinfo);
|
|
|
|
sprintf(circuitinfo, ":%02x%02x", L4->CIRCUITINDEX, L4->CIRCUITID);
|
|
strcat(ourcall, circuitinfo);
|
|
|
|
|
|
if (L4->L4CROSSLINK) // CONNECTED?
|
|
Count = CountFramesQueuedOnSession(L4->L4CROSSLINK);
|
|
else
|
|
Count = CountFramesQueuedOnSession(L4);
|
|
|
|
udplen = sprintf(UDPMsg, "{\"@type\": \"CircuitStatus\", \"node\": \"%s\", \"id\": %d, \"direction\": \"outgoing\","
|
|
"\"service\": %d, \"remote\": %s, \"local\": \"%s\", \"segsSent\": %d, \"segsRcvd\": %d, \"segsResent\": %d, \"segsQueued\": %d}",
|
|
NODECALLLOPPED, UDPSeq++, 0, remotecall, ourcall,L4->segsSent, L4->segsRcvd, L4->segsResent, Count);
|
|
|
|
// Debugprintf(UDPMsg);
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
}
|
|
}
|
|
|
|
|
|
// L2/3/4 Tracing
|
|
|
|
#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE
|
|
#define NETROM_PID 0xCF
|
|
#define IP_PID 0xCC
|
|
#define ARP_PID 0xCD
|
|
|
|
char * PIDtoText(int PID)
|
|
{
|
|
switch (PID)
|
|
{
|
|
case 240:
|
|
return "DATA";
|
|
case NETROM_PID:
|
|
return "NET/ROM";
|
|
case IP_PID:
|
|
return "IP";
|
|
case ARP_PID:
|
|
return "ARP";
|
|
}
|
|
return "?";
|
|
}
|
|
|
|
void APIL2Trace(struct _MESSAGE * Message, char Dirn)
|
|
{
|
|
char UDPMsg[2048];
|
|
int udplen;
|
|
char srcecall[64];
|
|
char destcall[16];
|
|
char CR[3] = "";
|
|
char PF[2] = "";
|
|
int iLen = 0;
|
|
int CTL = Message->CTL;
|
|
char Type[16] = "Unknown";
|
|
int UIFlag = 0;
|
|
int IFlag = 0;
|
|
int UFlag = 0;
|
|
int NS;
|
|
int NR;
|
|
|
|
if ((Message->ORIGIN[6] & 1) == 0) // Digis
|
|
return;
|
|
|
|
destcall[ConvFromAX25(Message->DEST, destcall)] = 0;
|
|
srcecall[ConvFromAX25(Message->ORIGIN, srcecall)] = 0;
|
|
|
|
// See if any Digis
|
|
|
|
if ((Message->ORIGIN[6] & 1) == 0) // Digis - ignore for now
|
|
return;
|
|
|
|
|
|
if ((Message->DEST[6] & 0x80) == 0 && (Message->ORIGIN[6] & 0x80) == 0)
|
|
strcpy(CR, "V1");
|
|
else if ((Message->DEST[6] & 0x80))
|
|
strcpy(CR, "C");
|
|
else if (Message->ORIGIN[6] & 0x80)
|
|
strcpy(CR, "R");
|
|
else
|
|
strcpy(CR, "V1");
|
|
|
|
if (CTL & PFBIT)
|
|
{
|
|
if (CR[0] == 'C')
|
|
PF[0] = 'P';
|
|
else if (CR[0] == 'R')
|
|
PF[0] = 'F';
|
|
}
|
|
|
|
CTL &= ~PFBIT;
|
|
|
|
if ((CTL & 1) == 0) // I frame
|
|
{
|
|
NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S)
|
|
NR = (CTL >> 5) & 7;
|
|
|
|
IFlag = 1;
|
|
iLen = Message->LENGTH - (MSGHDDRLEN + 16); // Dest origin ctl pid
|
|
|
|
strcpy(Type, "I");
|
|
}
|
|
else if (CTL == 3)
|
|
{
|
|
// Un-numbered Information Frame
|
|
|
|
strcpy(Type, "UI");
|
|
UIFlag = 1;
|
|
iLen = Message->LENGTH - (MSGHDDRLEN + 16); // Dest origin ctl pid
|
|
}
|
|
|
|
if (CTL & 2)
|
|
{
|
|
// UnNumbered
|
|
|
|
UFlag = 1;
|
|
|
|
switch (CTL)
|
|
{
|
|
case SABM:
|
|
|
|
strcpy(Type, "C");
|
|
break;
|
|
|
|
case SABME:
|
|
|
|
strcpy(Type, "SABME");
|
|
break;
|
|
|
|
case XID:
|
|
|
|
strcpy(Type, "XID");
|
|
break;
|
|
|
|
case TEST:
|
|
|
|
strcpy(Type, "TEST");
|
|
break;
|
|
|
|
case DISC:
|
|
|
|
strcpy(Type, "D");
|
|
break;
|
|
|
|
case DM:
|
|
|
|
strcpy(Type, "DM");
|
|
break;
|
|
|
|
case UA:
|
|
|
|
strcpy(Type, "UA");
|
|
break;
|
|
|
|
|
|
case FRMR:
|
|
|
|
strcpy(Type, "FRMR");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Super
|
|
|
|
NR = (CTL >> 5) & 7;
|
|
NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S)
|
|
|
|
switch (CTL & 0x0F)
|
|
{
|
|
case RR:
|
|
|
|
strcpy(Type, "RR");
|
|
break;
|
|
|
|
case RNR:
|
|
|
|
strcpy(Type, "RNR");
|
|
break;
|
|
|
|
case REJ:
|
|
|
|
strcpy(Type, "REJ");
|
|
break;
|
|
|
|
case SREJ:
|
|
|
|
strcpy(Type, "SREJ");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Common to all frame types
|
|
|
|
udplen = snprintf(UDPMsg, 2048,
|
|
"{\"@type\": \"L2Trace\", \"reportFrom\": \"%s\", \"port\": \"%d\", \"srce\": \"%s\", \"dest\": \"%s\", \"ctrl\": %d,"
|
|
"\"l2type\": \"%s\", \"modulo\": 8, \"cr\": \"%s\"",
|
|
NODECALLLOPPED, Message->PORT, srcecall, destcall, Message->CTL, Type, CR);
|
|
|
|
if (UIFlag)
|
|
{
|
|
udplen += snprintf(&UDPMsg[udplen], 2048 - udplen,
|
|
", \"ilen\": %d, \"pid\": %d, \"ptcl\": \"%s\"", iLen, Message->PID, PIDtoText(Message->PID));
|
|
|
|
if (Message->PID == NETROM_PID)
|
|
{
|
|
udplen += decodeNETROMUIMsg(Message->L2DATA, iLen, &UDPMsg[udplen], 2048 - udplen);
|
|
}
|
|
}
|
|
else if (IFlag)
|
|
{
|
|
if (PF[0])
|
|
udplen += snprintf(&UDPMsg[udplen], 2048 - udplen,
|
|
", \"ilen\": %d, \"pid\": %d, \"ptcl\": \"%s\", \"pf\": \"%s\", \"rseq\": %d, \"tseq\": %d",
|
|
iLen, Message->PID, PIDtoText(Message->PID), PF, NR, NS);
|
|
else
|
|
udplen += snprintf(&UDPMsg[udplen], 2048 - udplen,
|
|
", \"ilen\": %d, \"pid\": %d, \"ptcl\": \"%s\", \"rseq\": %d, \"tseq\": %d",
|
|
iLen, Message->PID, PIDtoText(Message->PID), NR, NS);
|
|
|
|
if (Message->PID == NETROM_PID)
|
|
{
|
|
int n = decodeNETROMIFrame(Message->L2DATA, iLen, &UDPMsg[udplen], 2048 - udplen);
|
|
|
|
if (n == 0)
|
|
return; // Can't decode so don't trace anything;
|
|
|
|
udplen += n;
|
|
}
|
|
|
|
}
|
|
else if (UFlag)
|
|
{
|
|
if (PF[0])
|
|
udplen += snprintf(&UDPMsg[udplen], 2048 - udplen, ", \"pf\": \"%s\"", PF);
|
|
}
|
|
else
|
|
{
|
|
// supervisory
|
|
|
|
if (PF[0])
|
|
udplen += snprintf(&UDPMsg[udplen], 2048 - udplen, ", \"pf\": \"%s\", \"rseq\": %d, \"tseq\": %d", PF, NR, NS);
|
|
else
|
|
udplen += snprintf(&UDPMsg[udplen], 2048 - udplen, ", \"rseq\": %d, \"tseq\": %d", NR, NS);
|
|
}
|
|
|
|
|
|
UDPMsg[udplen++] = '}';
|
|
UDPMsg[udplen] = 0;
|
|
// Debugprintf(UDPMsg);
|
|
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
|
|
|
|
}
|
|
|
|
|
|
int decodeNETROMUIMsg(unsigned char * Msg, int iLen, char * Buffer, int BufferLen)
|
|
{
|
|
int Len = 0;
|
|
|
|
// UI with NETROM PID are assumed to by NODES broadcast (INP3 routes are sent in I frames)
|
|
|
|
// But check first byte is 0xff to be sure, or 0xfe for Paula's char Alias[7]= "";
|
|
|
|
char Dest[10];
|
|
char Node[10];
|
|
char Alias[10] = "";
|
|
|
|
memcpy(Alias, &Msg[1], 6);
|
|
strlop(Alias, ' ');
|
|
|
|
if (Msg[0] == 0xfe) // Paula's Nodes Poll
|
|
{
|
|
Len = snprintf(Buffer, BufferLen, ", \"l3Type\": \"Routing info\", \"type\": \"Routing poll\"");
|
|
return Len;
|
|
}
|
|
|
|
if (Msg[0] != 0xff)
|
|
return 0;
|
|
|
|
Msg += 7; // to first field
|
|
|
|
Len = snprintf(Buffer, BufferLen, ", \"l3Type\": \"Routing info\", \"type\": \"NODES\", \"nodes\": [");
|
|
|
|
iLen -= 7; //Header, mnemonic and signature length
|
|
|
|
if (iLen < 21) // No Entries
|
|
{
|
|
Buffer[Len++] = ']';
|
|
return Len;
|
|
}
|
|
|
|
while(iLen > 20) // Entries are 21 bytes
|
|
{
|
|
Dest[ConvFromAX25(Msg, Dest)] = 0;
|
|
Msg +=7;
|
|
memcpy(Alias, Msg, 6);
|
|
Msg +=6;
|
|
strlop(Alias, ' ');
|
|
Node[ConvFromAX25(Msg, Node)] = 0;
|
|
Msg +=7;
|
|
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, "{\"call\": \"%s\", \"alias\": \"%s\", \"via\": \"%s\", \"qual\": %d},", Dest, Alias, Node, Msg[0]);
|
|
Msg++;
|
|
iLen -= 21;
|
|
}
|
|
// Have to replace trailing , with ]
|
|
|
|
Buffer[Len - 1] = ']';
|
|
return Len;
|
|
}
|
|
|
|
int decodeNETROMIFrame(unsigned char * Msg, int iLen, char * Buffer, int BufferLen)
|
|
{
|
|
int Len = 0;
|
|
L3MESSAGE * L3MSG = (L3MESSAGE *)Msg;
|
|
char srcecall[64];
|
|
char destcall[16];
|
|
char srcUser[16];
|
|
char srcNode[16];
|
|
int Opcode;
|
|
int netromx = 0;
|
|
int service = 0;
|
|
|
|
|
|
if (Msg[0] == 0xff) // RIF?
|
|
return decodeINP3RIF(&Msg[1], iLen - 1, Buffer, BufferLen);
|
|
|
|
// Netrom L3 /4 frame. Do standard L3 header
|
|
|
|
destcall[ConvFromAX25(L3MSG->L3DEST, destcall)] = 0;
|
|
srcecall[ConvFromAX25(L3MSG->L3SRCE, srcecall)] = 0;
|
|
|
|
if (strcmp(destcall, "KEEPLI") == 0)
|
|
return 0;
|
|
|
|
Len = snprintf(Buffer, BufferLen, ", \"l3Type\": \"NetRom\", \"l3src\": \"%s\", \"l3dst\": \"%s\", \"ttl\": %d", srcecall, destcall, L3MSG->L3TTL);
|
|
|
|
// L4 Stuff
|
|
|
|
Opcode = L3MSG->L4FLAGS & 15;
|
|
|
|
switch (Opcode)
|
|
{
|
|
case 0:
|
|
|
|
// OPCODE 0 is used for a variety of functions, using L4INDEX and L4ID as qualifiers
|
|
// 0c0c is used for IP. Ignore for now
|
|
|
|
// 00 01 Seesm to be Netrom Record Route
|
|
|
|
if (L3MSG->L4ID == 1 && L3MSG->L4INDEX == 0)
|
|
{
|
|
Len += decodeRecordRoute(L3MSG, iLen, &Buffer[Len], BufferLen - Len);
|
|
return Len;
|
|
}
|
|
|
|
case L4CREQX:
|
|
|
|
netromx = 1;
|
|
service = (L3MSG->L4RXNO << 8) | L3MSG->L4TXNO;
|
|
|
|
case L4CREQ:
|
|
|
|
srcUser[ConvFromAX25(&L3MSG->L4DATA[1], srcUser)] = 0;
|
|
srcNode[ConvFromAX25(&L3MSG->L4DATA[8], srcNode)] = 0;
|
|
|
|
if (netromx)
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"CONN REQX\", \"fromCct\": %d, \"srcUser\": \"%s\", \"srcNode\": \"%s\", \"window\": %d, \"service\": %d",
|
|
(L3MSG->L4INDEX << 8) | L3MSG->L4ID, srcUser, srcNode, L3MSG->L4DATA[0], service);
|
|
else
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"CONN REQ\", \"fromCct\": %d, \"srcUser\": \"%s\", \"srcNode\": \"%s\", \"window\": %d",
|
|
(L3MSG->L4INDEX << 8) | L3MSG->L4ID, srcUser, srcNode, L3MSG->L4DATA[0]);
|
|
|
|
return Len;
|
|
|
|
case L4CACK:
|
|
|
|
// Can be ACK or NACK depending on Choke flag
|
|
|
|
if (L3MSG->L4FLAGS & L4BUSY)
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"CONN NACK\", \"toCct\": %d",
|
|
(L3MSG->L4INDEX << 8) | L3MSG->L4ID);
|
|
else
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"CONN ACK\", \"toCct\": %d, \"fromCct\": %d, \"accWin\": %d",
|
|
(L3MSG->L4INDEX << 8) | L3MSG->L4ID, (L3MSG->L4TXNO << 8) | L3MSG->L4RXNO, L3MSG->L4DATA[0]);
|
|
|
|
return Len;
|
|
|
|
|
|
case L4INFO:
|
|
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"INFO\", \"toCct\": %d, \"txSeq\": %d, \"rxSeq\": %d, \"paylen\": %d",
|
|
(L3MSG->L4INDEX << 8) | L3MSG->L4ID, L3MSG->L4TXNO, L3MSG->L4RXNO, iLen - 20);
|
|
|
|
return Len;
|
|
|
|
case L4IACK:
|
|
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"INFO ACK\", \"toCct\": %d, \"rxSeq\": %d",
|
|
(L3MSG->L4INDEX << 8) | L3MSG->L4ID, L3MSG->L4RXNO);
|
|
|
|
return Len;
|
|
|
|
|
|
case L4DREQ:
|
|
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"DISC REQ\", \"toCct\": %d", (L3MSG->L4INDEX << 8) | L3MSG->L4ID);
|
|
return Len;
|
|
|
|
case L4DACK:
|
|
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"DISC ACK\", \"toCct\": %d", (L3MSG->L4INDEX << 8) | L3MSG->L4ID);
|
|
return Len;
|
|
|
|
case L4RESET:
|
|
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"RSET\", \"fromCct\": %d", (L3MSG->L4INDEX << 8) | L3MSG->L4ID);
|
|
return Len;
|
|
|
|
|
|
/*
|
|
"NRR Request" Netrom Record Route Request
|
|
"NRR Reply" Netrom Record Route Reply
|
|
"CONN REQ" Connect Request
|
|
"CONN REQX" Extended Connect Request
|
|
"CONN ACK" Connection Acknowledgement
|
|
"CONN NAK" Connection Negative Ack (refusal)
|
|
"DISC REQ" Disconnect request
|
|
"DISC ACK" Disconnect Acknowledgement
|
|
"INFO" Information-bearing frame
|
|
"INFO ACK" Acknowledgement for an INFO frame.
|
|
"RSET" Circuit Reset (kill)
|
|
"PROT EXT" Protocol Extension (e.g. IP, NCMP etc)
|
|
"unknown" Unrecognised type (shouldn't happen)
|
|
|
|
|
|
|
|
|
|
"l4type": "CONN ACK",
|
|
"fromCct": 10,
|
|
"toCct": 23809,
|
|
"accWin": 4,
|
|
*/
|
|
|
|
}
|
|
return Len;
|
|
}
|
|
|
|
int decodeRecordRoute(L3MESSAGE * L3, int iLen, char * Buffer, int BufferLen)
|
|
{
|
|
int Len = 0;
|
|
char callList[512];
|
|
char * ptr1 = callList;
|
|
unsigned char * ptr = L3->L4DATA;
|
|
char call[16];
|
|
int Response = 0;
|
|
|
|
iLen -= 20;
|
|
|
|
while (iLen > 0)
|
|
{
|
|
call[ConvFromAX25(ptr, call)] = 0;
|
|
|
|
ptr1 += sprintf(ptr1, " %s", call);
|
|
|
|
if ((ptr[7] & 0x80) == 0x80) // Check turnround bit
|
|
{
|
|
*ptr1++ = '*';
|
|
Response = 1;
|
|
}
|
|
|
|
ptr += 8;
|
|
iLen -= 8;
|
|
}
|
|
|
|
*ptr1 = 0;
|
|
|
|
if (Response)
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"NRR Reply\", \"nrrId\": %d, \"nrrRoute\": \"%s\"",
|
|
(L3->L4TXNO << 8) | L3->L4RXNO, callList);
|
|
else
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"l4Type\": \"NRR Request\", \"nrrId\": %d, \"nrrRoute\": \"%s\"",
|
|
(L3->L4TXNO << 8) | L3->L4RXNO, callList);
|
|
|
|
Debugprintf(Buffer);
|
|
|
|
return Len;
|
|
}
|
|
|
|
|
|
int decodeINP3RIF(unsigned char * Msg, int iLen, char * Buffer, int BufferLen)
|
|
{
|
|
char call[10];
|
|
int calllen;
|
|
int hops;
|
|
unsigned short rtt;
|
|
unsigned int len;
|
|
unsigned int opcode;
|
|
char alias[10] = "";
|
|
UCHAR IP[6];
|
|
int i;
|
|
int Len = 0;
|
|
|
|
Len = snprintf(Buffer, BufferLen, ", \"l3Type\": \"Routing info\", \"type\": \"INP3\", \"nodes\": [");
|
|
|
|
if (iLen < 10) // No Entries
|
|
{
|
|
Buffer[Len++] = ']';
|
|
return Len;
|
|
}
|
|
|
|
while (iLen > 1)
|
|
{
|
|
calllen = ConvFromAX25(Msg, call);
|
|
call[calllen] = 0;
|
|
|
|
// Validate the call
|
|
|
|
for (i = 0; i < calllen; i++)
|
|
{
|
|
if (!isupper(call[i]) && !isdigit(call[i]) && call[i] != '-')
|
|
return 0;
|
|
}
|
|
|
|
Msg+=7;
|
|
|
|
hops = *Msg++;
|
|
rtt = (*Msg++ << 8);
|
|
rtt += *Msg++;
|
|
|
|
IP[0] = 0;
|
|
strcpy(alias, " ");
|
|
|
|
iLen -= 10;
|
|
|
|
// Process optional fields
|
|
|
|
while (*Msg && iLen > 0) // Have an option
|
|
{
|
|
len = *Msg;
|
|
opcode = *(Msg+1);
|
|
|
|
if (len < 2 || len > iLen)
|
|
return 0;
|
|
|
|
if (opcode == 0 && len < 9)
|
|
{
|
|
memcpy(alias, Msg+2, len-2);
|
|
}
|
|
else if (opcode == 1 && len < 8)
|
|
{
|
|
memcpy(IP, Msg+2, len-2);
|
|
}
|
|
|
|
Msg += len;
|
|
iLen -= len;
|
|
|
|
}
|
|
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, "{\"call\": \"%s\", \"hops\": %d, \"tt\": %d", call, hops, rtt);
|
|
|
|
if (alias[0] > ' ')
|
|
Len += snprintf(&Buffer[Len], BufferLen - Len, ", \"alias\": \"%s\"", alias);
|
|
|
|
Buffer[Len++] = '}';
|
|
Buffer[Len++] = ',';
|
|
|
|
Msg++;
|
|
iLen--; // Over EOP
|
|
|
|
}
|
|
// Have to replace trailing , with ]
|
|
|
|
Buffer[Len - 1] = ']';
|
|
return Len;
|
|
}
|
|
|