/* 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 */ // // C replacement for L2Code.asm // #define Kernel #define _CRT_SECURE_NO_DEPRECATE #pragma data_seg("_BPQDATA") #include "time.h" #include "stdio.h" #include "CHeaders.h" #include "tncinfo.h" #define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE #define REJSENT 1 // SET WHEN FIRST REJ IS SENT IN REPLY // TO AN I(P) #define RNRSET 2 // RNR RECEIVED FROM OTHER END #define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED #define RNRSENT 0x10 // WE HAVE SEND RNR #define POLLSENT 0x20 // POLL BIT OUTSTANDING #define ONEMINUTE 60*3 #define TENSECS 10*3 #define THREESECS 3*3 VOID L2SENDCOMMAND(); VOID L2ROUTINE(); MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD); VOID SendSupervisCmd(struct _LINKTABLE * LINK); void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF); VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD); VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD); VOID ACKMSG(struct _LINKTABLE * LINK); VOID InformPartner(struct _LINKTABLE * LINK, int Reason); UINT RR_OR_RNR(struct _LINKTABLE * LINK); VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT); VOID CLEAROUTLINK(struct _LINKTABLE * LINK); VOID SENDFRMR(struct _LINKTABLE * LINK); char * SetupNodeHeader(struct DATAMESSAGE * Buffer); VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session); VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT); VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL); VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS); VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer); VOID RESET2X(struct _LINKTABLE * LINK); VOID RESET2(struct _LINKTABLE * LINK); VOID CONNECTREFUSED(struct _LINKTABLE * LINK); VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL); VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG); VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG); VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK); VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG); BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE); VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG); VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER); VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER); VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL); int COUNTLINKS(int Port); VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG); TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK); BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions); VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer); VOID L2SWAPADDRESSES(MESSAGE * Buffer); BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); VOID SENDSABM(struct _LINKTABLE * LINK); VOID L2SENDXID(struct _LINKTABLE * LINK); VOID __cdecl Debugprintf(const char * format, ...); VOID Q_IP_MSG(MESSAGE * Buffer); VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT); VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); BOOL CompareAliases(UCHAR * c1, UCHAR * c2); VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly); VOID DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg); VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer); BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg); VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL); UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg); VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); int CountBits(uint32_t in); void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer); void DetachKISSHF(struct PORTCONTROL * PORT); void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK); void WriteConnectLog(char * fromcall, char * tocall, UCHAR * Mode); extern int REALTIMETICKS; // MSGFLAG contains CMD/RESPONSE BITS #define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND #define RESP 2 // CURRENT MSG IS RESPONSE #define VER1 1 // CURRENT MSG IS VERSION 1 // FRMR REJECT FLAGS #define SDINVC 1 // INVALID COMMAND #define SDNRER 8 // INVALID N(R) UCHAR NO_CTEXT = 0; UCHAR ALIASMSG = 0; extern UINT APPLMASK; static UCHAR ISNETROMMSG = 0; UCHAR MSGFLAG = 0; extern UCHAR * ALIASPTR; UCHAR QSTCALL[7] = {'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; // QST IN AX25 UCHAR NODECALL[7] = {0x9C, 0x9E, 0x88, 0x8A, 0xA6, 0x40, 0xE0}; // 'NODES' IN AX25 FORMAT extern BOOL LogAllConnects; APPLCALLS * APPL; VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer) { // LEVEL 2 PROCESSING MESSAGE * ADJBUFFER; struct _LINKTABLE * LINK; UCHAR * ptr; int n; UCHAR CTL; uintptr_t Work; UCHAR c; // Check for invalid length (< 22 7Header + 7Addr + 7Addr + CTL if (Buffer->LENGTH < (18 + sizeof(void *))) { Debugprintf("BPQ32 Bad L2 Msg Port %d Len %d", PORT->PORTNUMBER, Buffer->LENGTH); ReleaseBuffer(Buffer); return; } PORT->L2FRAMES++; ALIASMSG = 0; APPLMASK = 0; ISNETROMMSG = 0; MSGFLAG = 0; // CMD/RESP UNDEFINED // Check for Corrupted Callsign in Origin (to keep MH list clean) ptr = &Buffer->ORIGIN[0]; n = 6; c = *(ptr) >> 1; if (c == ' ') // Blank Call { Debugprintf("BPQ32 Blank Call Port &%", PORT->PORTNUMBER); ReleaseBuffer(Buffer); return; } 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; } } // Check Digis if present if ((Buffer->ORIGIN[6] & 1) == 0) // Digis { ptr = &Buffer->CTL; n = 6; while(n--) { c = *(ptr++); if (c & 1) { ReleaseBuffer(Buffer); return; } c = c >> 1; if (!isalnum(c) && !(c == '#') && !(c == ' ')) { ReleaseBuffer(Buffer); return; } } } BPQTRACE(Buffer, TRUE); // TRACE - RX frames to APRS if (PORT->PORTMHEARD) MHPROC(PORT, Buffer); /// TAJ added 07/12/2020 for 'all RX traffic as IfinOctects InOctets[PORT->PORTNUMBER] += Buffer->LENGTH - MSGHDDRLEN; // CHECK THAT ALL DIGIS HAVE BEEN ACTIONED, // AND ADJUST FOR DIGIPEATERS IF PRESENT n = 8; // MAX DIGIS ptr = &Buffer->ORIGIN[6]; // End of Address bit while ((*ptr & 1) == 0) { // MORE TO COME ptr += 7; if ((*ptr & 0x80) == 0) // Digi'd bit { // FRAME HAS NOT BEEN REPEATED THROUGH CURRENT DIGI - // SEE IF WE ARE MEANT TO DIGI IT struct XDIGI * XDigi = PORT->XDIGIS; // Cross port digi setup ptr -= 6; // To start of Call if (CompareCalls(ptr, MYCALL) || CompareAliases(ptr, MYALIAS) || CompareCalls(ptr, PORT->PORTALIAS) || CompareCalls(ptr, PORT->PORTALIAS2)) { Digipeat(PORT, Buffer, ptr, 0, 0); // Digi it (if enabled) return; } while (XDigi) { if (CompareCalls(ptr, XDigi->Call)) { Digipeat(PORT, Buffer, ptr, XDigi->Port, XDigi->UIOnly); // Digi it (if enabled) return; } XDigi = XDigi->Next; } ReleaseBuffer(Buffer); return; // not complete and not for us } n--; if (n == 0) { ReleaseBuffer(Buffer); return; // Corrupt - no end of address bit } } // Reached End of digis, and all actioned, so can process it Work = (uintptr_t)&Buffer->ORIGIN[6]; ptr -= Work; // ptr is now length of digis Work = (uintptr_t)Buffer; ptr += Work; ADJBUFFER = (MESSAGE * )ptr; // ADJBUFFER points to CTL, etc. allowing for digis // GET CMD/RESP BITS if (Buffer->DEST[6] & 0x80) { if (Buffer->ORIGIN[6] & 0x80) // Both set, assume V1 MSGFLAG |= VER1; else MSGFLAG |= CMDBIT; } else { if (Buffer->ORIGIN[6] & 0x80) // Only Dest Set MSGFLAG |= RESP; else MSGFLAG |= VER1; // Neither, assume V1 } // SEE IF FOR AN ACTIVE LINK SESSION CTL = ADJBUFFER->CTL; // IF A UI, THERE IS NO SESSION if (FindLink(Buffer->ORIGIN, Buffer->DEST, PORT->PORTNUMBER, &LINK)) { L2LINKACTIVE(LINK, PORT, Buffer,ADJBUFFER, CTL, MSGFLAG); return; } // NOT FOR ACTIVE LINK - SEE IF ADDRESSED TO OUR ADDRESSES // FIRST TRY PORT ADDR/ALIAS if(PORT->PORTBBSFLAG == 1) goto PORTCALLISBBS; // PORT CALL/ALIAS ARE FOR BBS if (NODE) goto USING_NODE; PORTCALLISBBS: // NODE IS NOT ACTIVE, SO PASS CALLS TO PORTCALL/ALIAS TO BBS APPLMASK = 1; if (CompareCalls(Buffer->DEST, NETROMCALL)) { ISNETROMMSG = 1; goto FORUS; } if (PORT->PORTL3FLAG) // L3 Only Port? goto NOTFORUS; // If L3ONLY, only accept calls to NETROMCALL ISNETROMMSG = 0; USING_NODE: if (CompareCalls(Buffer->DEST, PORT->PORTCALL)) goto FORUS; ALIASMSG = 1; if (CompareAliases(Buffer->DEST, PORT->PORTALIAS)) // only compare 6 bits - allow any ssid goto FORUS; if (NODE == 0) goto TRYBBS; // NOT USING NODE SYSTEM ALIASMSG = 0; if (CompareCalls(Buffer->DEST, MYCALL)) goto FORUS; ALIASMSG = 1; if (CompareAliases(Buffer->DEST, MYALIAS)) // only compare 6 bits - allow any ssid goto FORUS; TRYBBS: if (BBS == 0) goto NOWTRY_NODES; // NOT USING BBS CALLS // TRY APPLICATION CALLSIGNS/ALIASES APPLMASK = 1; ALIASPTR = &CMDALIAS[0][0]; n = NumberofAppls; APPL = APPLCALLTABLE; while (n--) { if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr { // WE MAY NOT BE ALLOWED TO USE THE BBS CALL ON SOME BANDS DUE TO // THE RATHER ODD UK LICENCING RULES! // For backward compatibility only apply to appl 1 if ((PORT->PERMITTEDAPPLS & APPLMASK) != 0) { ALIASMSG = 0; if (CompareCalls(Buffer->DEST, APPL->APPLCALL)) goto FORUS; ALIASMSG = 1; if (CompareAliases(Buffer->DEST, APPL->APPLALIAS)) // only compare 6 bits - allow any ssid goto FORUS; if (CompareAliases(Buffer->DEST, APPL->L2ALIAS)) // only compare 6 bits - allow any ssid goto FORUS; } } APPLMASK <<= 1; ALIASPTR += ALIASLEN; APPL++; } // NOT FOR US - SEE IF 'NODES' OR IP/ARP BROADCAST MESSAGE NOWTRY_NODES: if (CompareCalls(Buffer->DEST, QSTCALL)) { Q_IP_MSG(Buffer); // IP BROADCAST return; } if (ADJBUFFER->PID != 0xCF) // NETROM MSG? goto NOTFORUS; // NO if (CompareCalls(Buffer->DEST, NODECALL)) { if (Buffer->L2DATA[0] == 0xff) // Valid NODES Broadcast { PROCESSNODEMESSAGE(Buffer, PORT); } } ReleaseBuffer(Buffer); return; NOTFORUS: // // MAY JUST BE A REPLY TO A 'PRIMED' CQ CALL // if ((CTL & ~PFBIT) == SABM) if (CheckForListeningSession(PORT, Buffer)) return; // Used buffer to send UA ReleaseBuffer(Buffer); return; FORUS: // if a UI frame and UIHook Specified, call it if (PORT->UIHook && CTL == 3) PORT->UIHook(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); L2FORUS(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); } VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer) { PMHSTRUC MH = PORT->PORTMHEARD; PMHSTRUC MHBASE = MH; int i; int OldCount = 0; char Freq[16] = ""; char DIGI = '*'; double ReportFreq = 0; // if port has Rigcontrol associated with it, get frequency struct TNCINFO * TNC = PORT->TNC; if (TNC && TNC->RIG && TNC->RIG->Valchar[0]) { if (TNC->Hardware == H_UZ7HO) { // See if we have Center Freq Info if (TNC->AGWInfo->CenterFreq) { ReportFreq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0); } #ifdef WIN32 else if (TNC->AGWInfo->hFreq) { char Centre[16]; double ModemFreq; SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre); ModemFreq = atof(Centre); ReportFreq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000); } #endif else ReportFreq = atof(TNC->RIG->Valchar) + 0.0015; // Assume 1500 } else // Not UZ7HO or Linux ReportFreq = atof(TNC->RIG->Valchar) + 0.0015; _gcvt(ReportFreq, 9, Freq); } else { if (PORT->RIGPort) { struct TNCINFO * TNC = TNCInfo[PORT->RIGPort]; if (TNC && TNC->RIG) { strcpy(Freq, TNC->RIG->Valchar); Freq[11] = 0; } } } // if (Buffer->ORIGIN[6] & 1) DIGI = 0; // DOn't think we want to do this // See if in list for (i = 0; i < MHENTRIES; i++) { if ((MH->MHCALL[0] == 0) || (CompareCalls(Buffer->ORIGIN, MH->MHCALL) && MH->MHDIGI == DIGI)) // Spare or our entry { OldCount = MH->MHCOUNT; goto DoMove; } MH++; } // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP i = MHENTRIES - 1; // Move others down and add at front DoMove: if (i != 0) // First memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); // Save Digis MHBASE->MHDIGI = DIGI; MHBASE->MHTIME = time(NULL); MHBASE->MHCOUNT = ++OldCount; strcpy(MHBASE->MHFreq, Freq); MHBASE->MHLocator[0] = 0; return; } int CountFramesQueuedOnSession(TRANSPORTENTRY * Session) { // COUNT NUMBER OF FRAMES QUEUED ON A SESSION if (Session == 0) return 0; if (Session->L4CIRCUITTYPE & BPQHOST) { return C_Q_COUNT(&Session->L4TX_Q); } if (Session->L4CIRCUITTYPE & SESSION) { // L4 SESSION - GET NUMBER UNACKED, AND ADD NUMBER ON TX QUEUE int Count = C_Q_COUNT(&Session->L4TX_Q); UCHAR Unacked = Session->TXSEQNO - Session->L4WS; return Count + Unacked; } if (Session->L4CIRCUITTYPE & PACTOR) { // PACTOR Type - Frames are queued on the Port Entry struct PORTCONTROL * PORT = Session->L4TARGET.PORT; EXTPORTDATA * EXT = (EXTPORTDATA *)PORT; int ret = EXT->FramesQueued; // Check L4 Queue as messages can stay there briefly ret += C_Q_COUNT(&Session->L4RX_Q); return ret + C_Q_COUNT(&PORT->PORTTX_Q); } // L2 CIRCUIT { int SessCount = C_Q_COUNT(&Session->L4TX_Q); struct _LINKTABLE * LINK = Session->L4TARGET.LINK; int L2 = COUNT_AT_L2(LINK); return SessCount + L2; } } int CHECKIFBUSYL2(TRANSPORTENTRY * Session) { // RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY if (Session->L4CROSSLINK) // CONNECTED? { Session = Session->L4CROSSLINK; if (CountFramesQueuedOnSession(Session) > 10) return L4BUSY;; } return 0; } VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) { // MESSAGE ADDRESSED TO OUR CALL OR ALIAS, BUT NOT FOR AN ACTIVE SESSION // LINK points to an empty link table entry struct ROUTE * ROUTE; int CTLlessPF = CTL & ~PFBIT; PORT->L2FRAMESFORUS++; NO_CTEXT = 0; // ONLY SABM or UI ALLOWED IF NO SESSION // Plus XID/TEST/SABME if V2.2 support enabled if (CTLlessPF == 3) // UI { // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES switch(ADJBUFFER->PID) { case 0xcf: // Netrom if (Buffer->L2DATA[0] == 0xff) // NODES PROCESSNODEMESSAGE(Buffer, PORT); break; case 0xcc: // TCP case 0xcd: // ARP case 0x08: // NOS FRAGMENTED AX25 TCP/IP Q_IP_MSG( Buffer); return; } ReleaseBuffer(Buffer); return; } if (PORT->PortUIONLY) // Port is for UI only { ReleaseBuffer(Buffer); return; } if (CTLlessPF == SABME) { // Although some say V2.2 requires SABME I don't agree! // Reject until we support Mod 128 L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); return; } if (CTLlessPF == SREJ) // Used to see if other end supports SREJ on 2.0 { // Send FRMR if dont support SREJ // Send DM if we do if (SUPPORT2point2) L2SENDRESP(PORT, Buffer, ADJBUFFER, DM); else L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); return; } if (CTLlessPF == XID) { // Send FRMR if we only support V 2.0 if (SUPPORT2point2 == FALSE) { L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); return; } // if Support 2.2 drop through } if (CTLlessPF == TEST) { // I can't see amy harm in replying to TEST L2SENDRESP(PORT, Buffer, ADJBUFFER, TEST); return; } // if (CTLlessPF != SABM && CTLlessPF != SABME) if (CTLlessPF != SABM && CTLlessPF != XID) { if ((MSGFLAG & CMDBIT) && (CTL & PFBIT)) // Command with P? L2SENDDM(PORT, Buffer, ADJBUFFER); else ReleaseBuffer(Buffer); // Ignore if not return; } // Exclude and limit tests are done for XID and SABM if (NODE == 0 && BBS == 0) // Don't want any calls { ReleaseBuffer(Buffer); return; } #ifdef EXCLUDEBITS // CHECK ExcludeList if (CheckExcludeList(Buffer->ORIGIN) == 0) { ReleaseBuffer(Buffer); return; } #endif // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT if (PORT->PERMITTEDCALLS) { UCHAR * ptr = PORT->PERMITTEDCALLS; while (TRUE) { if (memcmp(Buffer->ORIGIN, ptr, 6) == 0) // Ignore SSID break; ptr += 7; if ((*ptr) == 0) // Not in list { ReleaseBuffer(Buffer); return; } } } // IF CALL REQUEST IS FROM A LOCKED NODE WITH QUALITY ZERO, IGNORE IT if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) { // From a known node NO_CTEXT = 1; if (ROUTE->NEIGHBOUR_FLAG == 1 && ROUTE->NEIGHBOUR_QUAL == 0) // Locked, qual 0 { ReleaseBuffer(Buffer); return; } } // CHECK PORT CONNECT LIMITS if (PORT->USERS) { if (COUNTLINKS(PORT->PORTNUMBER) >= PORT->USERS) { L2SENDDM(PORT, Buffer, ADJBUFFER); return; } } // if KISSHF, check if attached. If so, reject. If not, attach. if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) { struct TNCINFO * TNC = PORT->TNC; if (TNC->PortRecord->ATTACHEDSESSIONS[0]) { L2SENDDM(PORT, Buffer, ADJBUFFER); return; } } // OK to accept SABM or XID if (CTLlessPF == XID) { ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); return; } // Not XID, so must be SABM L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM } VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) { // I think it is fairly safe to accept XID as soon as we // can process SREJ, but only accept Mod 8 and 256 Byte frames // I think the only way to run 2.2 Mod 8 is to preceed a // SABM with XID, but others don't seem to agree! // Run through XID fields, changing any we don't like, // then return an XID response // Decode and process XID UCHAR * ptr = &ADJBUFFER->PID; UCHAR * ptr1, * ptr2; UCHAR TEMPDIGI[57]; int n; if (*ptr++ == 0x82 && *ptr++ == 0x80) { int Type; int Len; unsigned int value; int xidlen = *(ptr++) << 8; xidlen += *ptr++; // XID is set of Type, Len, Value n-tuples while (xidlen > 0) { Type = *ptr++; Len = *ptr++; value = 0; xidlen -= (Len + 2); while (Len--) { value <<=8; value += *ptr++; } switch(Type) { case 2: //Bin fields break; case 3: if ((value & OPMustHave) != OPMustHave) goto BadXID; if ((value & OPMod8) == 0) goto BadXID; if ((value & OPSREJMult) == 0) goto BadXID; // Reply Mod 8 SREJMULTI value = OPMustHave | OPSREJMult | OPMod8; ptr -=3; *ptr++ = value >> 16; *ptr++ = value >> 8; *ptr++ = value; break; case 6: //RX Size break; case 8: //RX Window break; } } // Send back as XID response LINK->L2STATE = 1; // XID received LINK->Ver2point2 = TRUE; // Must support 2.2 if sent XID LINK->L2TIME = PORT->PORTT1; LINK->LINKPORT = PORT; // save calls so we can match up SABM when it comes memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); LINK->LINKCALL[6] &= 0x1e; // Mask SSID memcpy(LINK->OURCALL, Buffer->DEST, 7); LINK->OURCALL[6] &= 0x1e; // Mask SSID memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address { // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT ptr1 = &Buffer->ORIGIN[6]; // End of add ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi while((*ptr1 & 1) == 0) // End of address bit { ptr1++; memcpy(ptr2, ptr1, 7); ptr2[6] &= 0x1e; // Mask Repeated and Last bits ptr2 -= 7; ptr1 += 6; } // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY n = PORT->PORTMAXDIGIS; ptr1 = ptr2 + 7; // First in TEMPDIGIS ptr2 = &LINK->DIGIS[0]; while (*ptr1) { if (n == 0) { // Too many for us CLEAROUTLINK(LINK); ReleaseBuffer(Buffer); return; } memcpy(ptr2, ptr1, 7); ptr1 += 7; ptr2 += 7; n--; } } ADJBUFFER->CTL = CTL | PFBIT; // Buffer->LENGTH = (UCHAR *)ADJBUFFER - (UCHAR *)Buffer + MSGHDDRLEN + 15; // SET UP BYTE COUNT L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS PUT_ON_PORT_Q(PORT, Buffer); return; } BadXID: L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); return; } int COUNTLINKS(int Port) { //COUNT LINKS ON PORT int i = MAXLINKS, n = 0; struct _LINKTABLE * LINK = LINKS; while (i--) { if (LINK->LINKPORT && LINK->LINKPORT->PORTNUMBER == Port) n++; LINK++; } return n; } VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) { // MESSAGE ON AN ACTIVE LINK int CTLlessPF = CTL & ~PFBIT; PORT->L2FRAMESFORUS++; // ONLY SABM or UI ALLOWED IF NO SESSION if (CTLlessPF == 3) // UI { // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES switch(ADJBUFFER->PID) { case 0xcf: // Netrom if (Buffer->L2DATA[0] == 0xff) // NODES PROCESSNODEMESSAGE(Buffer, PORT); break; case 0xcc: // TCP case 0xcd: // ARP case 0x08: // NOS FRAGMENTED AX25 TCP/IP Q_IP_MSG( Buffer); return; } ReleaseBuffer(Buffer); return; } if (CTLlessPF == DISC) { InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END CLEAROUTLINK(LINK); L2SENDUA(PORT, Buffer, ADJBUFFER); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); return; } if (LINK->L2STATE == 1) { // XID State. Should be XID response if 2.2 ok or DM/FRMR if not if (MSGFLAG & RESP) { if (CTLlessPF == DM || CTLlessPF == FRMR) { // Doesn't support XID - Send SABM LINK->L2STATE = 2; LINK->Ver2point2 = FALSE; LINK->L2TIMER = 1; // USe retry to send SABM } else if (CTLlessPF == XID) { // Process response to make sure ok, Send SABM or DISC LINK->L2STATE = 2; LINK->Ver2point2 = TRUE;// Must support 2.2 if responded to XID LINK->L2TIMER = 1; // USe retry to send SABM } ReleaseBuffer(Buffer); return; } // Command on existing session. Could be due to other end missing // the XID response, so if XID just resend response } if (CTLlessPF == XID && (MSGFLAG & CMDBIT)) { // XID Command on active session. Other end may be restarting. Send Response ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); return; } if (CTLlessPF == SABM) { // SABM ON EXISTING SESSION - IF DISCONNECTING, REJECT if (LINK->L2STATE == 1) // Sent XID? { L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM return; } if (LINK->L2STATE == 4) // DISCONNECTING? { L2SENDDM(PORT, Buffer, ADJBUFFER); return; } // THIS IS A SABM ON AN EXISTING SESSION // THERE ARE SEVERAL POSSIBILITIES: // 1. RECONNECT COMMAND TO TNC // 2. OTHER END THINKS LINK HAS DIED // 3. RECOVERY FROM FRMR CONDITION // 4. REPEAT OF ORIGINAL SABM COS OTHER END MISSED UA // FOR 1-3 IT IS REASONABLE TO FULLY RESET THE CIRCUIT, BUT IN 4 // SUCH ACTION WILL LOSE THE INITIAL SIGNON MSG IF CONNECTING TO A // BBS. THE PROBLEM IS TELLING THE DIFFERENCE. I'M GOING TO SET A FLAG // WHEN FIRST INFO RECEIVED - IF SABM REPEATED BEFORE THIS, I'LL ASSUME // CONDITION 4, AND JUST RESEND THE UA if (LINK->SESSACTIVE == 0) // RESET OF ACTIVE CIRCUIT? { L2SENDUA(PORT, Buffer, ADJBUFFER); // No, so repeat UA return; } InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END LINK->CIRCUITPOINTER = 0; L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM return; } L2_PROCESS(LINK, PORT, Buffer, CTL, MSGFLAG); } VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG) { // SET UP NEW SESSION (OR RESET EXISTING ONE) TRANSPORTENTRY * Session; int CONERROR; if (LINK == 0) // NO LINK ENTRIES - SEND DM RESPONSE { L2SENDDM(PORT, Buffer, ADJBUFFER); return; } SETUPNEWL2SESSION(LINK, PORT, Buffer, MSGFLAG); if (LINK->L2STATE != 5) // Setup OK? { L2SENDDM(PORT, Buffer, ADJBUFFER); // Failed return; } // IF CONNECT TO APPL ADDRESS, SET UP APPL SESSION if (APPLMASK == 0) { // Not ATTACH TO APPL // Send CTEXT if connect to NODE/Port Alias, or NODE/Port Call, and FULL_CTEXT set // Dont sent to known NODEs, or appl connects struct DATAMESSAGE * Msg; int Totallen = 0; int Paclen= PORT->PORTPACLEN; UCHAR * ptr; if (LogAllConnects) { char toCall[12], fromCall[12]; toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; WriteConnectLog(fromCall, toCall, "AX.25"); } L2SENDUA(PORT, Buffer, ADJBUFFER); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) AttachKISSHF(PORT, Buffer); if (NO_CTEXT == 1) return; if (FULL_CTEXT == 0 && !ALIASMSG) // Any connect, or call to alias return; // if Port CTEXT defined, use it if (PORT->CTEXT) { Totallen = strlen(PORT->CTEXT); ptr = PORT->CTEXT; } else if (CTEXTLEN) { Totallen = CTEXTLEN; ptr = CTEXTMSG; } else return; if (Paclen == 0) Paclen = PACLEN; while(Totallen) { Msg = GetBuff(); if (Msg == NULL) break; // No Buffers Msg->PID = 0xf0; if (Paclen > Totallen) Paclen = Totallen; memcpy(Msg->L2DATA, ptr, Paclen); Msg->LENGTH = Paclen + MSGHDDRLEN + 1; C_Q_ADD(&LINK->TX_Q, Msg); ptr += Paclen; Totallen -= Paclen; } return; } // Connnect to APPL if (LINK->LINKTYPE != 1) { L2SENDUA(PORT, Buffer, ADJBUFFER); // RESET OF DOWN/CROSSLINK return; } if (LINK->CIRCUITPOINTER) { L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET return; } // IF RUNNING ONLY BBS (NODE=0), THIS MAY BE EITHER A USER OR NODE // TRYING TO SET UP A L4 CIRCUIT - WE DONT WANT TO ATTACH A NODE TO // THE BBS! if (NODE == 0) { // NOW THINGS GET DIFICULT - WE MUST EITHER WAIT TO SEE IF A PID CF MSG // ARRIVES, OR ASSUME ALL NODES ARE IN NEIGHBOURS - I'LL TRY THE LATTER // AND SEE HOW IT GOES. tHIS MEANS THAT YOU MUST DEFINE ALL ROUTES // IN CONFIG FILE struct ROUTE * ROUTE; if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) { // It's a node L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET return; } } Session = SetupSessionForL2(LINK); // CREATE INCOMING L4 SESSION if (Session == NULL) { CLEAROUTLINK(LINK); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); L2SENDDM(PORT, Buffer, ADJBUFFER); return; } // NOW TRY A BBS CONNECT // IF APPL CONNECT, SEE IF APPL HAS AN ALIAS if (ALIASPTR[0] > ' ') { struct DATAMESSAGE * Msg; // ACCEPT THE CONNECT, THEN INVOKE THE ALIAS L2SENDUA(PORT, Buffer, ADJBUFFER); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) { struct DATAMESSAGE * Msg; int Totallen = 0; int Paclen= PORT->PORTPACLEN; UCHAR * ptr; AttachKISSHF(PORT, Buffer); // if Port CTEXT defined, use it if (PORT->CTEXT) { Totallen = strlen(PORT->CTEXT); ptr = PORT->CTEXT; } else if (HFCTEXTLEN) { Totallen = HFCTEXTLEN; ptr = HFCTEXT; } if (Paclen == 0) Paclen = PACLEN; while(Totallen) { Msg = GetBuff(); if (Msg == NULL) break; // No Buffers Msg->PID = 0xf0; if (Paclen > Totallen) Paclen = Totallen; memcpy(Msg->L2DATA, ptr, Paclen); Msg->LENGTH = Paclen + MSGHDDRLEN + 1; C_Q_ADD(&LINK->TX_Q, Msg); ptr += Paclen; Totallen -= Paclen; } } if (LogAllConnects) { char toCall[12], fromCall[12]; toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; WriteConnectLog(fromCall, toCall, "AX.25"); } Msg = GetBuff(); if (Msg) { Msg->PID = 0xf0; memcpy(Msg->L2DATA, APPL->APPLCMD, 12); Msg->L2DATA[12] = 13; Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR C_Q_ADD(&LINK->RX_Q, Msg); } return; } if (cATTACHTOBBS(Session, APPLMASK, PORT->PORTPACLEN, &CONERROR) == 0) { // NO BBS AVAILABLE CLEARSESSIONENTRY(Session); CLEAROUTLINK(LINK); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); L2SENDDM(PORT, Buffer, ADJBUFFER); return; } if (LogAllConnects) { char toCall[12], fromCall[12]; toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; WriteConnectLog(fromCall, toCall, "AX.25"); } L2SENDUA(PORT, Buffer, ADJBUFFER); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) { struct DATAMESSAGE * Msg; int Totallen = 0; int Paclen= PORT->PORTPACLEN; UCHAR * ptr; AttachKISSHF(PORT, Buffer); // if Port CTEXT defined, use it if (PORT->CTEXT) { Totallen = strlen(PORT->CTEXT); ptr = PORT->CTEXT; } else if (HFCTEXTLEN) { Totallen = HFCTEXTLEN; ptr = HFCTEXT; } else return; if (Paclen == 0) Paclen = PACLEN; while(Totallen) { Msg = GetBuff(); if (Msg == NULL) break; // No Buffers Msg->PID = 0xf0; if (Paclen > Totallen) Paclen = Totallen; memcpy(Msg->L2DATA, ptr, Paclen); Msg->LENGTH = Paclen + MSGHDDRLEN + 1; C_Q_ADD(&LINK->TX_Q, Msg); ptr += Paclen; Totallen -= Paclen; } return; } } VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG) { // COPY ADDRESS INFO TO LINK TABLE UCHAR * ptr1, * ptr2; UCHAR TEMPDIGI[57]; int n; memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); LINK->LINKCALL[6] &= 0x1e; // Mask SSID memcpy(LINK->OURCALL, Buffer->DEST, 7); LINK->OURCALL[6] &= 0x1e; // Mask SSID memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT LINK->L2TIME = PORT->PORTT1; // Set tomeoiut for no digis if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address { // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT ptr1 = &Buffer->ORIGIN[6]; // End of add ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi while((*ptr1 & 1) == 0) // End of address bit { ptr1++; memcpy(ptr2, ptr1, 7); ptr2[6] &= 0x1e; // Mask Repeated and Last bits ptr2 -= 7; ptr1 += 6; } // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY n = PORT->PORTMAXDIGIS; ptr1 = ptr2 + 7; // First in TEMPDIGIS ptr2 = &LINK->DIGIS[0]; while (*ptr1) { if (n == 0) { // Too many for us CLEAROUTLINK(LINK); return; } memcpy(ptr2, ptr1, 7); ptr1 += 7; ptr2 += 7; n--; LINK->L2TIME += PORT->PORTT1; // Adjust timeout for digis } } // THIS MAY BE RESETTING A LINK - BEWARE OF CONVERTING A CROSSLINK TO // AN UPLINK AND CONFUSING EVERYTHING LINK->LINKPORT = PORT; if (LINK->LINKTYPE == 0) { if (ISNETROMMSG && NODE == 0) // Only allow crosslink if node = 0 LINK->LINKTYPE = 3; // Crosslink else LINK->LINKTYPE = 1; // Uplink } LINK->L2TIMER = 0; // CANCEL TIMER LINK->L2SLOTIM = T3; // SET FRAME SENT RECENTLY LINK->LINKWINDOW = PORT->PORTWINDOW; RESET2(LINK); // RESET ALL FLAGS LINK->L2STATE = 5; // IF VERSION 1 MSG, SET FLAG if (MSGFLAG & VER1) LINK->VER1FLAG |= 1; } VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER) { L2SENDRESP(PORT, Buffer, ADJBUFFER, UA); } VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER) { if (CheckExcludeList(Buffer->ORIGIN) == 0) // if in exclude, don't send DM { ReleaseBuffer(Buffer); // not sure that this is the right place for releasing? return; } L2SENDRESP(PORT, Buffer, ADJBUFFER, DM); } VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL) { // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY // SET APPROPRIATE P/F BIT ADJBUFFER->CTL = CTL | PFBIT; Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 15; // SET UP BYTE COUNT L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS PUT_ON_PORT_Q(PORT, Buffer); return; } VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL) { // Send FRMR Invalid Control field // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY // SET APPROPRIATE P/F BIT UCHAR * ptr; ADJBUFFER->CTL = FRMR | PFBIT; ptr = &ADJBUFFER->PID; *(ptr++) = CTL; // MOVE REJECT C-BYTE *(ptr++) = 0; *(ptr++) = SDINVC; // MOVE REJECT FLAGS Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 18; // SET UP BYTE COUNT L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS PUT_ON_PORT_Q(PORT, Buffer); return; } VOID L2SWAPADDRESSES(MESSAGE * Buffer) { // EXCHANGE ORIGIN AND DEST, AND REVERSE DIGIS (IF PRESENT) char TEMPFIELD[7]; UCHAR * ptr1, * ptr2; UCHAR TEMPDIGI[57]; memcpy(TEMPFIELD, Buffer->ORIGIN, 7); memcpy(Buffer->ORIGIN, Buffer->DEST, 7); memcpy(Buffer->DEST, TEMPFIELD, 7); Buffer->ORIGIN[6] &= 0x1e; // Mask SSID Buffer->ORIGIN[6] |= 0xe0; // Reserved and Response Buffer->DEST[6] &= 0x1e; // Mask SSID Buffer->DEST[6] |= 0x60; // Reserved if ((TEMPFIELD[6] & 1) == 0) { // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT ptr1 = &Buffer->ORIGIN[6]; // End of add ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi while((*ptr1 & 1) == 0) // End of address bit { ptr1++; memcpy(ptr2, ptr1, 7); ptr2[6] &= 0x1e; // Mask Repeated and Last bits ptr2 -= 7; ptr1 += 6; } // LIST OF DIGI CALLS COMPLETE - copy back ptr1 = ptr2 + 7; // First in TEMPDIGIS ptr2 = &Buffer->CTL; while (*ptr1) { memcpy(ptr2, ptr1, 7); ptr1 += 7; ptr2 += 7; } *(ptr2 - 1) |= 1; // End of addresses } else { Buffer->ORIGIN[6] |= 1; // End of address } } BOOL InternalL2SETUPCROSSLINK(PROUTE ROUTE, int Retries) { // ROUTE POINTS TO A NEIGHBOUR - FIND AN L2 SESSION FROM US TO IT, OR INITIATE A NEW ONE struct _LINKTABLE * LINK; struct PORTCONTROL * PORT; int FRACK; if (FindLink(ROUTE->NEIGHBOUR_CALL, NETROMCALL, ROUTE->NEIGHBOUR_PORT, &LINK)) { // SESSION ALREADY EXISTS LINK->LINKTYPE = 3; // MAKE SURE IT KNOWS ITS A CROSSLINK ROUTE->NEIGHBOUR_LINK = LINK; LINK->NEIGHBOUR = ROUTE; return TRUE; } // SET UP NEW SESSION (OR RESET EXISTING ONE) if (LINK == NULL) return FALSE; // No free links ROUTE->NEIGHBOUR_LINK = LINK; LINK->NEIGHBOUR = ROUTE; LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(ROUTE->NEIGHBOUR_PORT); if (PORT == NULL) return FALSE; // maybe port has been deleted // IF ROUTE HAS A FRACK, SET IT if (ROUTE->NBOUR_FRACK) FRACK = ROUTE->NBOUR_FRACK; else FRACK = PORT->PORTT1; LINK->L2TIME = FRACK; // SET TIMER VALUE // IF ROUTE HAS A WINDOW, SET IT if (ROUTE->NBOUR_MAXFRAME) LINK->LINKWINDOW = ROUTE->NBOUR_MAXFRAME; else LINK->LINKWINDOW = PORT->PORTWINDOW; // if (SUPPORT2point2) // LINK->L2STATE = 1; // Send XID // else LINK->L2STATE = 2; memcpy(LINK->LINKCALL, ROUTE->NEIGHBOUR_CALL, 7); memcpy(LINK->OURCALL, NETROMCALL, 7); if (ROUTE->NEIGHBOUR_DIGI1[0]) { memcpy(LINK->DIGIS, ROUTE->NEIGHBOUR_DIGI1, 7); LINK->L2TIME += FRACK; } if (ROUTE->NEIGHBOUR_DIGI2[0]) { memcpy(&LINK->DIGIS[7], ROUTE->NEIGHBOUR_DIGI1, 7); LINK->L2TIME += FRACK; } LINK->LINKTYPE = 3; // CROSSLINK if (Retries) LINK->L2RETRIES = PORT->PORTN2 - Retries; if (LINK->L2STATE == 1) L2SENDXID(LINK); else SENDSABM(LINK); return TRUE; } BOOL L2SETUPCROSSLINKEX(PROUTE ROUTE, int Retries) { // Allows caller to specify number of times SABM should be sent return InternalL2SETUPCROSSLINK(ROUTE, Retries); } BOOL L2SETUPCROSSLINK(PROUTE ROUTE) { return InternalL2SETUPCROSSLINK(ROUTE, 0); } VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG) { // PROCESS LEVEL 2 PROTOCOL STUFF // SEE IF COMMAND OR RESPONSE if ((MSGFLAG & CMDBIT) == 0) { // RESPONSE OR VERSION 1 // IF RETRYING, MUST ONLY ACCEPT RESPONSES WITH F SET (UNLESS RUNNING V1) if ((CTL & PFBIT) || LINK->VER1FLAG == 1) { // F SET or V1 - CAN CANCEL TIMER LINK->L2TIMER = 0; // CANCEL LINK TIMER } } if (LINK->L2STATE == 3) { // FRMR STATE - IF C(P) SEND FRMR, ELSE IGNORE if (CTL & PFBIT) { if (CTL == (FRMR | PFBIT)) // if both ends in FRMR state, reset link { RESET2(LINK); LINK->L2STATE = 2; // INITIALISING LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM L2SENDCOMMAND(LINK, SABM | PFBIT); } } if (MSGFLAG & CMDBIT) { // SEND FRMR AGAIN SENDFRMR(LINK); } ReleaseBuffer(Buffer); return; } if (LINK->L2STATE >= 5) { // LINK IN STATE 5 OR ABOVE - LINK RUNNING if ((CTL & 1) == 0) // I frame { SDIFRM(LINK, PORT, Buffer, CTL, MSGFLAG); // consumes buffer return; } if ((CTL & 2)) // U frame { SDUFRM(LINK, PORT, Buffer, CTL); //consumes buffer return; } // ELSE SUPERVISORY, MASK OFF N(R) AND P-BIT switch (CTL & 0x0f) { // is there any harm in accepoting SREJ even if we don't // otherwise support 2.2? case REJ: case SREJ: PORT->L2REJCOUNT++; case RR: case RNR: SFRAME(LINK, PORT, CTL, MSGFLAG); break; default: // UNRECOGNISABLE COMMAND LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE LINK->SDREJF |= SDINVC; // SET INVALID COMMAND REJECT SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION } ReleaseBuffer(Buffer); return; } // NORMAL DISCONNECT MODE // COULD BE UA, DM - SABM AND DISC HANDLED ABOVE switch (CTL & ~PFBIT) { case UA: // UA RECEIVED if (LINK->L2STATE == 2) { // RESPONSE TO SABM - SET LINK UP RESET2X(LINK); // LEAVE QUEUED STUFF LINK->L2STATE = 5; LINK->L2TIMER = 0; // CANCEL TIMER LINK->L2RETRIES = 0; LINK->L2SLOTIM, T3; // SET FRAME SENT RECENTLY // IF VERSION 1 MSG, SET FLAG if (MSGFLAG & VER1) LINK->VER1FLAG |= 1; // TELL PARTNER CONNECTION IS ESTABLISHED if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) KISSHFConnected(PORT, LINK); SENDCONNECTREPLY(LINK); ReleaseBuffer(Buffer); return; } if (LINK->L2STATE == 4) // DISCONNECTING? { InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END CLEAROUTLINK(LINK); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); } // UA, BUT NOT IN STATE 2 OR 4 - IGNORE ReleaseBuffer(Buffer); return; case DM: // DM RESPONSE - IF TO SABM, SEND BUSY MSG if (LINK->L2STATE == 2) { CONNECTREFUSED(LINK); // SEND MESSAGE IF DOWNLINK return; } // DM RESP TO DISC RECEIVED - OTHER END HAS LOST SESSION // CLEAR OUT TABLE ENTRY - IF INTERNAL TNC, SHOULD SEND *** DISCONNECTED InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END CLEAROUTLINK(LINK); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); ReleaseBuffer(Buffer); return; case FRMR: // FRAME REJECT RECEIVED - LOG IT AND RESET LINK RESET2(LINK); LINK->L2STATE = 2; // INITIALISING LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM PORT->L2FRMRRX++; L2SENDCOMMAND(LINK, SABM | PFBIT); return; default: // ANY OTHER - IGNORE ReleaseBuffer(Buffer); } } VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL) { // PROCESS AN UNSEQUENCED COMMAND (IN LINK UP STATES) switch (CTL & ~PFBIT) { case UA: // DISCARD - PROBABLY REPEAT OF ACK OF SABM break; case FRMR: // FRAME REJECT RECEIVED - LOG IT AND RESET LINK RESET2(LINK); LINK->L2STATE = 2; // INITIALISING LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM PORT->L2FRMRRX++; L2SENDCOMMAND(LINK, SABM | PFBIT); break; case DM: // DM RESPONSE - SESSION MUST HAVE GONE // SEE IF CROSSLINK ACTIVE InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END CLEAROUTLINK(LINK); if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); break; default: // UNDEFINED COMMAND LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE LINK->SDREJF |= SDINVC; SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION } ReleaseBuffer(Buffer); } VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG) { // CHECK COUNTS, AND IF RNR INDICATE _BUFFER SHORTAGE AT OTHER END if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET? { SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION return; } SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R) if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW? { SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION return; } if ((CTL & 0xf) == SREJ) { // Probably safer to handle SREJ completely separately // Can we get SREJ Command with P??(Yes) // Can we just resend missing frame ?? (Think so!) // We support MultiSREJ (can gave additional missing frame // numbers in the Info field // I don't see the point of Multi unless we wait fot an F bit, // bur maybe not safe to assume others do the same // So if I get SREJ(F) I can send missing frame(s) if (MSGFLAG & RESP) { // SREJ Response if (CTL & PFBIT) { // SREJ(F). Send Frames() UCHAR NS = (CTL >> 5) & 7; // Frame to resend struct PORTCONTROL * PORT; UCHAR * ptr1, * ptr2; UCHAR CTL; int count; MESSAGE * Msg; MESSAGE * Buffer; Msg = LINK->FRAMES[NS]; // is frame available? if (Msg == NULL) return; // Wot!! // send the frame // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES Buffer = GetBuff(); if (Buffer == NULL) return; ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses // ptr2 NOW POINTS TO COMMAND BYTE // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS CTL |= NS << 1; // BITS 1-3 OF CONTROL BYTE // SET P BIT IF NO MORE TO SEND (only more if Multi SREJ) if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1 { CTL |= PFBIT; LINK->L2FLAGS |= POLLSENT; LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE) Buffer->Linkptr = LINK; } *(ptr2++) = CTL; // TO DATA (STARTING WITH PID) count = Msg->LENGTH - MSGHDDRLEN; if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY { ptr1 = (UCHAR *)Msg; ptr1 += MSGHDDRLEN; memcpy(ptr2, ptr1, count); } Buffer->DEST[6] |= 0x80; // SET COMMAND Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER PORT = LINK->LINKPORT; if (PORT) { Buffer->PORT = PORT->PORTNUMBER; PUT_ON_PORT_Q(PORT, Buffer); } else { Buffer->Linkptr = 0; ReleaseBuffer(Buffer); } } } return; } // VALID RR/RNR RECEIVED LINK->L2FLAGS &= ~RNRSET; //CLEAR RNR if ((CTL & 0xf) == RNR) LINK->L2FLAGS |= RNRSET; //Set RNR if (MSGFLAG & CMDBIT) { // ALWAYS REPLY TO RR/RNR/REJ COMMAND (even if no P bit ??) // FIRST PROCESS RESEQ QUEUE //; CALL PROCESS_RESEQ // IGNORE IF AN 'F' HAS BEEN SENT RECENTLY if (LINK->LAST_F_TIME + 15 > REALTIMETICKS) return; // DISCARD CTL = RR_OR_RNR(LINK); CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS CTL |= PFBIT; L2SENDRESPONSE(LINK, CTL); LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY LINK->L2ACKREQ = 0; // CANCEL DELAYED ACKL2 // SAVE TIME IF 'F' SENT' LINK->LAST_F_TIME = REALTIMETICKS; return; } // Response if ((CTL & PFBIT) == 0 && LINK->VER1FLAG == 0) { // RESPONSE WITHOUT P/F DONT RESET N(S) (UNLESS V1) return; } // RESPONSE WITH P/F - MUST BE REPLY TO POLL FOLLOWING TIMEOUT OR I(P) // THERE IS A PROBLEM WITH REPEATED RR(F), SAY CAUSED BY DELAY AT L1 // AS FAR AS I CAN SEE, WE SHOULD ONLY RESET N(S) IF AN RR(F) FOLLOWS // AN RR(P) AFTER A TIMEOUT - AN RR(F) FOLLOWING AN I(P) CANT POSSIBLY // INDICATE A LOST FRAME. ON THE OTHER HAND, A REJ(F) MUST INDICATE // A LOST FRAME. So dont reset NS if not retrying, unless REJ // someone (probably WLE KISS Driver) is sending REJ followed by RR(F) // after lost frame and i(p) /* 1:Fm W4DHW-10 To W4DHW [17:08:03R] [+++] JZK)x@DBrN4X;i#CM,нҼrO NX5(|#UcYl)璘oȼ>9*G(65C!LُQpIH;i>9pB [17:08:03T] 1:Fm W4DHW To W4DHW-10 [17:08:03T] 1:Fm W4DHW To W4DHW-10 [17:08:03T] is there a problem with restting on RR(F) following I(P)? I think the problem is restting NS twice if you get delayed responses to I or RR (P). So lets try only resetting NS once for each P sent */ // if ((CTL & 0xf) == REJ || LINK->L2RETRIES) if ((LINK->L2FLAGS & POLLSENT)) { RESETNS(LINK, (CTL >> 5) & 7); // RESET N(S) AND COUNT RETRIED FRAMES LINK->L2RETRIES = 0; LINK->L2TIMER = 0; // WILL RESTART TIMER WHEN RETRY SENT } LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) or RR(P) SET if ((CTL & 0xf) == RNR) { // Dont Clear timer on receipt of RNR(F), spec says should poll for clearing of busy, // and loss of subsequent RR will cause hang. Perhaps should set slightly longer time?? // Timer may have been cleared earlier, so restart it LINK->L2TIMER = LINK->L2TIME; } } //*** PROCESS AN INFORMATION FRAME VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG) { int NS; if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET? { SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION ReleaseBuffer(Buffer); return; } SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R) if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW? { SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION ReleaseBuffer(Buffer); return; } LINK->SESSACTIVE = 1; // SESSION IS DEFINITELY SET UP NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) // IPOLL (sending an I(P) frame following timeout instead of RR(P)) // is a problem. We need to send REJ(F), but shouldn't add to collector. // We also need to handle repeated I(P), so shouldn't set REJSENT in // this state. if ((((NS + 1) & 7) == LINK->LINKNR) && (CTL & PFBIT)) { // Previous Frame and P set - Assume IPOLL PORT->L2OUTOFSEQ++; LINK->L2STATE = 6; LINK->L2ACKREQ = 0; // CANCEL RR NEEDED // We need to protect against sending multiple REJ(F) if channel // delays mean we get two I(P) close together (how close is close ??) // SM has default IPOLL limit of 30 bytes or about a second at 300 // ACKMODE should avoid this anyway, and resptime of under 3 secs // is unlikely so say 2.5 secs ?? if (LINK->LAST_F_TIME + 25 > REALTIMETICKS) { ReleaseBuffer(Buffer); return; } SEND_RR_RESP(LINK, PFBIT); LINK->LAST_F_TIME = REALTIMETICKS; ReleaseBuffer(Buffer); return; } CheckNSLoop: if (NS != LINK->LINKNR) // EQUAL TO OUR N(R)? { // There is a frame missing. // if we have just sent a REJ we have at least one out // of sequence frame in RXFRAMES // so if we have frame LINK->LINKNR we can process it // and remove it from RXFRAMES. If we are then back // in sequence we just carry on. if (LINK->RXFRAMES[LINK->LINKNR]) { // We have the first missing frame. Process it. MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); Debugprintf("L2 process saved Frame %d", LINK->LINKNR); PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer // NR has been updated. goto CheckNSLoop; // See if OK or we have another saved frame } // BAD FRAME, SEND REJ (AFTER RESPTIME - OR WE MAY SEND LOTS!) // ALSO SAVE THE FRAME - NEXT TIME WE MAY GET A DIFFERENT SUBSET // AND SOON WE WILL HANDLE SREJ PORT->L2OUTOFSEQ++; LINK->L2STATE = 6; // IF RUNNING VER1, AND OTHER END MISSES THIS REJ, LINK WILL FAIL // SO TIME OUT REJ SENT STATE (MUST KEEP IT FOR A WHILE TO AVOID // 'MULTIPLE REJ' PROBLEM) if (LINK->VER1FLAG == 1) LINK->REJTIMER = TENSECS; // SET ACK REQUIRED TIMER - REJ WILL BE SENT WHEN IT EXPIRES // if configured RESPTIME is longer than 3 secs use it (may be longer on HF) if (PORT->PORTT2 > THREESECS) LINK->L2ACKREQ = PORT->PORTT2; else LINK->L2ACKREQ = THREESECS; // EXTRA LONG RESPTIME, AS SENDING TOO MANY REJ'S IS SERIOUS if (LINK->RXFRAMES[NS]) { // Already have a copy, so discard old and keep this Debugprintf ("Frame %d out of seq but already have copy - release it", NS); ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); } else { Debugprintf ("Frame %d out of seq - save", NS); } Buffer->CHAIN = 0; LINK->RXFRAMES[NS] = Buffer; goto CheckPF; } // IN SEQUENCE FRAME // Remove any stored frame with this seq if (LINK->RXFRAMES[NS]) ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); if (LINK->L2STATE == 6) // REJ? { // If using REJ we can cancel REJ state. // If using SREJ we only cancel REJ if we have no stored frames if (LINK->Ver2point2) { // see if any frames saved. int i; for (i = 0; i < 8; i++) { if (LINK->RXFRAMES[i]) goto stayinREJ; } // Drop through if no stored frames } // CANCEL REJ LINK->L2STATE = 5; LINK->L2FLAGS &= ~REJSENT; } stayinREJ: PROC_I_FRAME(LINK, PORT, Buffer); // Passes on or releases Buffer CheckPF: if (LINK->Ver2point2 == 0) // Unless using SREJ { if (LINK->L2FLAGS & REJSENT) { return; // DONT SEND ANOTHER TILL REJ IS CANCELLED } } if (CTL & PFBIT) { if (LINK->L2STATE == 6) LINK->L2FLAGS |= REJSENT; // Set "REJ Sent" else { // we have all frames. Clear anything in RXFRAMES int n = 0; while (n < 8) { if (LINK->RXFRAMES[n]) ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n])); n++; } } LINK->L2ACKREQ = 0; // CANCEL RR NEEDED SEND_RR_RESP(LINK, PFBIT); // RECORD TIME LINK->LAST_F_TIME = REALTIMETICKS; } } VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer) { int Length; char * Info; UCHAR PID; struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)Buffer; UCHAR * EOA; int n = 8; // Max Digis LINK->LINKNR++; // INCREMENT OUR N(R) LINK->LINKNR &= 7; // MODULO 8 // ATTACH I FRAMES TO LINK TABLE RX QUEUE - ONLY DATA IS ADDED (NOT ADDRESSES) // IF DISC PENDING SET, IGNORE FRAME if (LINK->L2FLAGS & DISCPENDING) { ReleaseBuffer(Buffer); return; } // Copy data down the buffer so PID comes after Header (DATAMESSAGE format) Length = Buffer->LENGTH - (MSGHDDRLEN + 15); // Buffer Header + addrs + CTL Info = &Buffer->PID; // Adjust for DIGIS EOA = &Buffer->ORIGIN[6]; // End of address Bit while (((*EOA & 1) == 0) && n--) { Length -= 7; Info += 7; EOA += 7; } PID = EOA[2]; switch(PID) { case 0xcc: case 0xcd: // IP Message if (n < 8) // If digis, move data back down buffer { memmove(&Buffer->PID, &EOA[2], Length); Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID); } Q_IP_MSG( Buffer); break; case 8: // NOS FRAGMENTED IP if (n < 8) // If digis, move data back down buffer { memmove(&Buffer->PID, &EOA[2], Length); Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID); } C_Q_ADD(&LINK->L2FRAG_Q, Buffer); if (Buffer->L2DATA[0] == 0) { // THERE IS A WHOLE MESSAGE ON FRAG_Q - PASS TO IP while(LINK->L2FRAG_Q) { Buffer = Q_REM(&LINK->L2FRAG_Q); Q_IP_MSG( Buffer); } } break; default: if (Length < 1 || Length > 257) { ReleaseBuffer(Buffer); return; } // Copy Data back over memmove(&Msg->PID, Info, Length); Buffer->LENGTH = Length + MSGHDDRLEN; C_Q_ADD(&LINK->RX_Q, Buffer); } LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER } //*** CHECK RECEIVED N(R) COUNT VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL) { UCHAR NR = (CTL >> 5) & 7; if (NR >= LINK->LINKWS) // N(R) >= WINDOW START? { // N(R) ABOVE OR EQUAL TO WINDOW START - OK IF NOT ABOVE N(S), OR N(S) BELOW WS if (NR > LINK->LINKNS) // N(R) <= WINDOW END? { // N(R) ABOVE N(S) - DOES COUNT WRAP? if (LINK->LINKNS >= LINK->LINKWS) // Doesnt wrap goto BadNR; } GoodNR: if ((CTL & 0x0f) == SREJ) if ((CTL & PFBIT) == 0) return; // SREJ without F doesn't ACK anything LINK->LINKWS = NR; // NEW WINDOW START = RECEIVED N(R) ACKMSG(LINK); // Remove any acked messages return; } // N(R) LESS THAN WINDOW START - ONLY OK IF WINDOW WRAPS if (NR <= LINK->LINKNS) // N(R) <= WINDOW END? goto GoodNR; BadNR: // RECEIVED N(R) IS INVALID LINK->SDREJF |= SDNRER; // FLAG A REJECT CONDITION LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE } VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS) { int Resent = (LINK->LINKNS - NS) & 7; // FRAMES TO RESEND LINK->LINKNS = NS; // RESET N(S) if (LINK->LINKTYPE == 3) // mode-Node { if (LINK->NEIGHBOUR) LINK->NEIGHBOUR->NBOUR_RETRIES += Resent; } } int COUNT_AT_L2(struct _LINKTABLE * LINK) { // COUNTS FRAMES QUEUED ON AN L2 SESSION (IN BX) int count = 0, abovelink = 0; int n = 0; if (LINK == NULL) return 0; abovelink = C_Q_COUNT((UINT *)&LINK->TX_Q); // COUNT FRAMES IN TSLOTS while (n < 8) { if (LINK->FRAMES[n]) count++; n++; } // ADD AL,AH ; TOTAL IN AL, NUMBER ABOVE LINK IN AH return abovelink + count; } //*** RESET HDLC AND PURGE ALL QUEUES ETC. VOID RESET2X(struct _LINKTABLE * LINK) { LINK->SDREJF = 0; // CLEAR FRAME REJECT FLAGS LINK->LINKWS = 0; // CLEAR WINDOW POINTERS LINK->LINKOWS = 0; LINK->LINKNR = 0; // CLEAR N(R) LINK->LINKNS = 0; // CLEAR N(S) LINK->SDTSLOT= 0; LINK->L2STATE = 5; // RESET STATE LINK->L2FLAGS = 0; } VOID CLEARL2QUEUES(struct _LINKTABLE * LINK) { // GET RID OF ALL FRAMES THAT ARE QUEUED int n = 0; while (n < 8) { while (LINK->FRAMES[n]) ReleaseBuffer(Q_REM(&LINK->FRAMES[n])); while (LINK->RXFRAMES[n]) ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n])); n++; } // GET RID OF ALL FRAMES THAT ARE // QUEUED ON THE TX HOLDING QUEUE, RX QUEUE AND LEVEL 3 QUEUE while (LINK->TX_Q) ReleaseBuffer(Q_REM(&LINK->TX_Q)); while (LINK->RX_Q) ReleaseBuffer(Q_REM(&LINK->RX_Q)); } VOID RESET2(struct _LINKTABLE * LINK) { CLEARL2QUEUES(LINK); RESET2X(LINK); } VOID SENDSABM(struct _LINKTABLE * LINK) { L2SENDCOMMAND(LINK, SABM | PFBIT); } VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer) { // TIME STAMP IT time(&Buffer->Timestamp); if (PORT->TXPORT) { Buffer->PORT = PORT->TXPORT; // update port no in header PORT = GetPortTableEntryFromPortNum(PORT->TXPORT); if (PORT == NULL) { ReleaseBuffer(Buffer); return; } } C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); } UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg) { // COPY ADDRESSES FROM LINK TABLE TO MESSAGE _BUFFER UCHAR * ptr1 = &LINK->DIGIS[0]; UCHAR * ptr2 = &Msg->CTL; int Digis = 8; memcpy(&Msg->DEST[0], &LINK->LINKCALL[0], 14); // COPY DEST AND ORIGIN Msg->DEST[6] |= 0x60; Msg->ORIGIN[6] |= 0x60; while (Digis) { if (*(ptr1)) // any more to copy? { memcpy(ptr2, ptr1, 7); ptr1 += 7; ptr2 += 7; Digis--; } else break; } *(ptr2 - 1) |= 1; // SET END OF ADDRESSES return ptr2; // Pointer to CTL } VOID SDETX(struct _LINKTABLE * LINK) { // Start sending frsmes if possible struct PORTCONTROL * PORT; int Outstanding; UCHAR * ptr1, * ptr2; UCHAR CTL; int count; MESSAGE * Msg; MESSAGE * Buffer; // DONT SEND IF RESEQUENCING RECEIVED FRAMES - CAN CAUSE FRMR PROBLEMS // if (LINK->L2RESEQ_Q) // return; if (LINK->LINKPORT->PORTNUMBER == 19) { int i = 0; } Outstanding = LINK->LINKNS - LINK->LINKOWS; // Was WS not NS if (Outstanding < 0) Outstanding += 8; // allow for wrap if (Outstanding >= LINK->LINKWINDOW) // LIMIT return; // See if we can load any more frames into the frame holding q while (LINK->TX_Q && LINK->FRAMES[LINK->SDTSLOT] == NULL) { Msg = Q_REM(&LINK->TX_Q); Msg->CHAIN = NULL; LINK->FRAMES[LINK->SDTSLOT] = Msg; LINK->SDTSLOT ++; LINK->SDTSLOT &= 7; } // dont send while poll outstanding while ((LINK->L2FLAGS & POLLSENT) == 0) { Msg = LINK->FRAMES[LINK->LINKNS]; // is next frame available? if (Msg == NULL) return; // send the frame // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES Buffer = GetBuff(); if (Buffer == NULL) return; ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses // ptr2 NOW POINTS TO COMMAND BYTE // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS CTL |= LINK->LINKNS << 1; // BITS 1-3 OF CONTROL BYTE LINK->LINKNS++; // INCREMENT NS LINK->LINKNS &= 7; // mod 8 // SET P BIT IF END OF WINDOW OR NO MORE TO SEND if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1 { Outstanding = LINK->LINKNS - LINK->LINKOWS; if (Outstanding < 0) Outstanding += 8; // allow for wrap // if at limit, or no more to send, set P) if (Outstanding >= LINK->LINKWINDOW || LINK->FRAMES[LINK->LINKNS] == NULL) { CTL |= PFBIT; LINK->L2FLAGS |= POLLSENT; LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE) Buffer->Linkptr = LINK; } } *(ptr2++) = CTL; // TO DATA (STARTING WITH PID) count = Msg->LENGTH - MSGHDDRLEN; if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY { ptr1 = (UCHAR *)Msg; ptr1 += MSGHDDRLEN; memcpy(ptr2, ptr1, count); } Buffer->DEST[6] |= 0x80; // SET COMMAND Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER PORT = LINK->LINKPORT; if (PORT) { Buffer->PORT = PORT->PORTNUMBER; PUT_ON_PORT_Q(PORT, Buffer); } else { Buffer->Linkptr = 0; ReleaseBuffer(Buffer); } } } VOID L2TimerProc() { int i = MAXLINKS; struct _LINKTABLE * LINK = LINKS; struct PORTCONTROL * PORT = PORTTABLE; while (i--) { if (LINK->LINKCALL[0] == 0) { LINK++; continue; } // CHECK FOR TIMER EXPIRY OR BUSY CLEARED PORT = LINK->LINKPORT; if (PORT == NULL) { LINK++; continue; // just ion case!! } if (LINK->L2TIMER) { LINK->L2TIMER--; if (LINK->L2TIMER == 0) { L2TIMEOUT(LINK, PORT); LINK++; continue; } } else { // TIMER NOT RUNNING - MAKE SURE STATE NOT BELOW 5 - IF // IT IS, SOMETHING HAS GONE WRONG, AND LINK WILL HANG FOREVER if (LINK->L2STATE < 5 && LINK->L2STATE != 2 && LINK->L2STATE != 1) // 2 = CONNECT - PROBABLY TO CQ LINK->L2TIMER = 2; // ARBITRARY VALUE } // TEST FOR RNR SENT, AND NOT STILL BUSY if (LINK->L2FLAGS & RNRSENT) { // Was busy if (RR_OR_RNR(LINK) != RNR) // SEE IF STILL BUSY { // Not still busy - tell other end // Just sending RR will hause a hang of RR is missed, and other end does not poll on Busy // Try sending RR CP, so we will retry if not acked LINK->L2ACKREQ = 0; // CLEAR ANY DELAYED ACK TIMER if (LINK->L2RETRIES == 0) // IF RR(P) OUTSTANDING WILl REPORT ANYWAY { SendSupervisCmd(LINK); LINK++; continue; } } } else { // NOT BUSY if (LINK->L2ACKREQ) // DELAYED ACK TIMER { if (LINK->L2RETRIES == 0) // DONT SEND RR RESPONSE WHILEST RR(P) OUTSTANDING { LINK->L2ACKREQ--; if (LINK->L2ACKREQ == 0) { SEND_RR_RESP(LINK, 0); // NO F BIT LINK++; continue; } } } } // CHECK FOR REJ TIMEOUT if (LINK->REJTIMER) { LINK->REJTIMER--; if (LINK->REJTIMER == 0) // {REJ HAS TIMED OUT (THIS MUST BE A VERSION 1 SESSION) { // CANCEL REJ STATE if (LINK->L2STATE == 6) // REJ? LINK->L2STATE = 5; // CLEAR REJ } } // See if time for link validation poll if (LINK->L2SLOTIM) { LINK->L2SLOTIM--; if (LINK->L2SLOTIM == 0) // Time to poll { SendSupervisCmd(LINK); LINK++; continue; } } // See if idle too long LINK->KILLTIMER++; if (L2KILLTIME && LINK->KILLTIMER > L2KILLTIME) { // CIRCUIT HAS BEEN IDLE TOO LONG - SHUT IT DOWN LINK->KILLTIMER = 0; LINK->L2TIMER = 1; // TO FORCE DISC LINK->L2STATE = 4; // DISCONNECTING // TELL OTHER LEVELS InformPartner(LINK, NORMALCLOSE); } LINK++; } } VOID SendSupervisCmd(struct _LINKTABLE * LINK) { // Send Super Command RR/RNR/REJ(P) UCHAR CTL; if (LINK->VER1FLAG == 1) { // VERSION 1 TIMEOUT // RESET TO RESEND I FRAMES LINK->LINKNS = LINK->LINKOWS; SDETX(LINK); // PREVENT FRMR (I HOPE) } // SEND RR COMMAND - EITHER AS LINK VALIDATION POLL OR FOLLOWING TIMEOUT LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED CTL = RR_OR_RNR(LINK); // MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK' CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS CTL |= PFBIT; LINK->L2FLAGS |= POLLSENT; L2SENDCOMMAND(LINK, CTL); LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY } void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF) { UCHAR CTL; CTL = RR_OR_RNR(LINK); // MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK' CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS CTL |= PF; L2SENDRESPONSE(LINK, CTL); ACKMSG(LINK); // SEE IF STILL WAITING FOR ACK } VOID ACKMSG(struct _LINKTABLE * LINK) { // RELEASE ANY ACKNOWLEDGED FRAMES while (LINK->LINKOWS != LINK->LINKWS) // is OLD WINDOW START EQUAL TO NEW WINDOW START? { // No, so frames to ack if (LINK->FRAMES[LINK->LINKOWS]) ReleaseBuffer(Q_REM(&LINK->FRAMES[LINK->LINKOWS])); else { char Call1[12], Call2[12]; Call1[ConvFromAX25(LINK->LINKCALL, Call1)] = 0; Call2[ConvFromAX25(LINK->OURCALL, Call2)] = 0; Debugprintf("Missing frame to ack Seq %d Calls %s %s", LINK->LINKOWS, Call1, Call2); } LINK->LINKOWS++; // INCREMENT OLD WINDOW START LINK->LINKOWS &= 7; // MODULO 8 // SOMETHING HAS BEEN ACKED - RESET RETRY COUNTER if (LINK->L2RETRIES) LINK->L2RETRIES = 1; // MUSTN'T SET TO ZERO - COULD CAUSE PREMATURE RETRANSMIT } if (LINK->LINKWS != LINK->LINKNS) // IS N(S) = NEW WINDOW START? { // NOT ALL I-FRAMES HAVE BEEN ACK'ED - RESTART TIMER LINK->L2TIMER = LINK->L2TIME; return; } // ALL FRAMES HAVE BEEN ACKED - CANCEL TIMER UNLESS RETRYING // IF RETRYING, MUST ONLY CANCEL WHEN RR(F) RECEIVED if (LINK->VER1FLAG == 1 || LINK->L2RETRIES == 0) // STOP TIMER IF LEVEL 1 or not retrying { LINK->L2TIMER = 0; LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) SET (IN CASE TALKING TO OLD BPQ!) } // IF DISCONNECT REQUEST OUTSTANDING, AND NO FRAMES ON TX QUEUE, SEND DISC if (LINK->L2FLAGS & DISCPENDING && LINK->TX_Q == 0) { LINK->L2FLAGS &= ~DISCPENDING; LINK->L2TIMER = 1; // USE TIMER TO SEND DISC LINK->L2STATE = 4; // DISCONNECTING } } VOID CONNECTFAILED(); VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT) { // TIMER EXPIRED // IF LINK UP (STATE 5 OR ABOVE) SEND RR/RNR AS REQUIRED // IF S2, REPEAT SABM // IF S3, REPEAT FRMR // IF S4, REPEAT DISC PORT->L2TIMEOUTS++; // FOR STATS if (LINK->L2STATE == 0) return; if (LINK->L2STATE == 1) { // XID LINK->L2RETRIES++; if (LINK->L2RETRIES >= PORT->PORTN2) { // RETRIED N2 TIMES - Give up CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); CLEAROUTLINK(LINK); return; } L2SENDXID(LINK); return; } if (LINK->L2STATE == 2) { // CONNECTING LINK->L2RETRIES++; if (LINK->L2RETRIES >= PORT->PORTN2) { // RETRIED N2 TIMES - Give up CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); CLEAROUTLINK(LINK); return; } SENDSABM(LINK); return; } if (LINK->L2STATE == 4) { // DISCONNECTING LINK->L2RETRIES++; if (LINK->L2RETRIES >= PORT->PORTN2) { // RETRIED N2 TIMES - JUST CLEAR OUT LINK if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) DetachKISSHF(PORT); CLEAROUTLINK(LINK); return; } L2SENDCOMMAND(LINK, DISC | PFBIT); return; } if (LINK->L2STATE == 3) { // FRMR LINK->L2RETRIES++; if (LINK->L2RETRIES >= PORT->PORTN2) { // RETRIED N2 TIMES - RESET LINK LINK->L2RETRIES = 0; LINK->L2STATE = 2; SENDSABM(LINK); return; } } // STATE 5 OR ABOVE // SEND RR(P) UP TO N2 TIMES LINK->L2RETRIES++; if (LINK->L2RETRIES >= PORT->PORTN2) { // RETRIED N TIMES SEND A COUPLE OF DISCS AND THEN CLOSE InformPartner(LINK, RETRIEDOUT); // TELL OTHER END ITS GONE LINK->L2RETRIES -= 1; // Just send one DISC LINK->L2STATE = 4; // CLOSING L2SENDCOMMAND(LINK, DISC | PFBIT); return; } SendSupervisCmd(LINK); } VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT) { PORT->L2FRMRTX++; LINK->L2STATE = 3; // ENTER FRMR STATE LINK->L2TIMER = LINK->L2TIME; //SET TIMER SENDFRMR(LINK); } VOID SENDFRMR(struct _LINKTABLE * LINK) { // RESEND FRMR struct PORTCONTROL * PORT; MESSAGE * Buffer; UCHAR * ptr; Buffer = SETUPL2MESSAGE(LINK, FRMR); if (Buffer == NULL) return; Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE ptr = &Buffer->PID; *(ptr++) = LINK->SDRBYTE; // MOVE REJECT C-BYTE *(ptr++) = LINK->LINKNR << 5 | LINK->LINKNS << 1; *(ptr++) = LINK->SDREJF; // MOVE REJECT FLAGS Buffer->LENGTH += 3; PORT = LINK->LINKPORT; Buffer->PORT = PORT->PORTNUMBER; if (PORT) PUT_ON_PORT_Q(PORT, Buffer); else ReleaseBuffer(Buffer); return; } VOID CLEAROUTLINK(struct _LINKTABLE * LINK) { CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS memset(LINK, 0, sizeof(struct _LINKTABLE)); } VOID L2SENDXID(struct _LINKTABLE * LINK) { // Set up and send XID struct PORTCONTROL * PORT; UCHAR * ptr; unsigned int xidval; MESSAGE * Buffer; if (LINK->LINKPORT == 0) return; //??? has been zapped Buffer = SETUPL2MESSAGE(LINK, XID | PFBIT); if (Buffer == NULL) { // NO BUFFERS - SET TIMER TO FORCE RETRY LINK->L2TIMER = 10*3; // SET TIMER return; } Buffer->DEST[6] |= 0x80; // SET COMMAND ptr = &Buffer->PID; // Set up default XID Mod 8 *ptr++ = 0x82; // FI *ptr++ = 0x80; // GI *ptr++ = 0x0; *ptr++ = 0x10; // Length 16 *ptr++ = 0x02; // Classes of Procedures *ptr++ = 0x02; // Length *ptr++ = 0x00; // *ptr++ = 0x21; // ABM Half Duplex // We offer REJ, SREJ and SREJ Multiframe *ptr++ = 0x03; // Optional Functions *ptr++ = 0x03; // Len // Sync TX, SREJ Multiframe 16 bit FCS, Mod 8, TEST, // Extended Addressing, REJ, SREJ xidval = OPMustHave | OPSREJ | OPSREJMult | OPREJ | OPMod8; *ptr++ = xidval >> 16; *ptr++ = xidval >> 8; *ptr++ = xidval; *ptr++ = 0x06; // RX Packet Len *ptr++ = 0x02; // Len *ptr++ = 0x08; // *ptr++ = 0x00; // 2K bits (256) Bytes *ptr++ = 0x08; // RX Window *ptr++ = 0x01; // Len *ptr++ = 0x07; // 7 Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND Buffer->Linkptr = LINK; PORT = LINK->LINKPORT; if (PORT) { Buffer->PORT = PORT->PORTNUMBER; PUT_ON_PORT_Q(PORT, Buffer); } else { Buffer->Linkptr = 0; ReleaseBuffer(Buffer); } } VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD) { // SEND COMMAND IN CMD struct PORTCONTROL * PORT; MESSAGE * Buffer; if (LINK->LINKPORT == 0) return; //??? has been zapped Buffer = SETUPL2MESSAGE(LINK, CMD); if (Buffer == NULL) { // NO BUFFERS - SET TIMER TO FORCE RETRY if (CMD & PFBIT) // RESPONSE EXPECTED? LINK->L2TIMER = 10*3; // SET TIMER return; } Buffer->DEST[6] |= 0x80; // SET COMMAND if (CMD & PFBIT) // RESPONSE EXPECTED? { LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND Buffer->Linkptr = LINK; } PORT = LINK->LINKPORT; if (PORT) { Buffer->PORT = PORT->PORTNUMBER; PUT_ON_PORT_Q(PORT, Buffer); } else { Buffer->Linkptr = 0; ReleaseBuffer(Buffer); } } VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD) { // SEND Response IN CMD struct PORTCONTROL * PORT; MESSAGE * Buffer; Buffer = SETUPL2MESSAGE(LINK, CMD); if (Buffer == NULL) { // NO BUFFERS - SET TIMER TO FORCE RETRY if (CMD & PFBIT) // RESPONSE EXPECTED? LINK->L2TIMER = 10*3; // SET TIMER return; } Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY PORT = LINK->LINKPORT; Buffer->PORT = PORT->PORTNUMBER; if (PORT) PUT_ON_PORT_Q(PORT, Buffer); else ReleaseBuffer(Buffer); } MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD) { MESSAGE * Buffer; UCHAR * ptr; Buffer = GetBuff(); if (Buffer == NULL) return NULL; ptr = SETUPADDRESSES(LINK, Buffer); // copy addresses // ptr NOW POINTS TO COMMAND BYTE *(ptr)++ = CMD; Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH return Buffer; } VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason); VOID InformPartner(struct _LINKTABLE * LINK, int Reason) { // LINK IS DISCONNECTING - IF THERE IS A CROSSLINK, SEND DISC TO IT if (LINK->LINKTYPE == 3) { L3LINKCLOSED(LINK, Reason); return; } if (LINK->CIRCUITPOINTER) { CloseSessionPartner(LINK->CIRCUITPOINTER); CLEARSESSIONENTRY(LINK->CIRCUITPOINTER); } } UINT RR_OR_RNR(struct _LINKTABLE * LINK) { UCHAR Temp; TRANSPORTENTRY * Session; LINK->L2FLAGS &= ~RNRSENT; // SET UP APPROPRIATE SUPER COMMAND if (LINK->LINKTYPE == 3) // Node to Node - only busy if short of buffers goto CHKBUFFS; // UP OR DOWN LINK - SEE IF SESSION IS BUSY if (LINK->CIRCUITPOINTER == 0) goto CHKBUFFS; // NOT CONNECTED Session = LINK->CIRCUITPOINTER; // TO CIRCUIT ENTRY Temp = CHECKIFBUSYL2(Session); //TARGET SESSION BUSY? if (Temp & L4BUSY) goto SENDRNR; // BUSY CHKBUFFS: if (QCOUNT < 20) goto SENDRNR; // NOT ENOUGH // SEND REJ IF IN REJ STATE if (LINK->L2STATE == 6) { // We may have the needed frame in RXFRAMES CheckNSLoop2: if (LINK->RXFRAMES[LINK->LINKNR]) { // We have the first missing frame. Process it. struct PORTCONTROL * PORT = LINK->LINKPORT; MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); Debugprintf("L2 about to send REJ - process saved Frame %d", LINK->LINKNR); PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer // NR has been updated. // Clear REJ if we have no more saved if (LINK->Ver2point2) // Using SREJ? { // see if any frames saved. int i; for (i = 0; i < 8; i++) { if (LINK->RXFRAMES[i]) goto stayinREJ2; } // Drop through if no stored frames } LINK->L2STATE = 5; LINK->L2FLAGS &= ~REJSENT; stayinREJ2: LINK->L2ACKREQ = 0; // Cancel Resptime (Set by PROC_I_FRAME) goto CheckNSLoop2; // See if OK or we have another saved frame } if (LINK->L2STATE == 6) // if we support SREJ send that instesd or REJ if (LINK->Ver2point2) // We only allow 2.2 with SREJ Multi return SREJ; else return REJ; } return RR; SENDRNR: LINK->L2FLAGS |= RNRSENT; // REMEMBER return RNR; } VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg); VOID CONNECTFAILED(struct _LINKTABLE * LINK) { ConnectFailedOrRefused(LINK, "Failure with"); } VOID CONNECTREFUSED(struct _LINKTABLE * LINK) { ConnectFailedOrRefused(LINK, "Busy from"); } VOID L3CONNECTFAILED(); VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg) { // IF DOWNLINK, TELL PARTNER // IF CROSSLINK, TELL ROUTE CONTROL struct DATAMESSAGE * Buffer; UCHAR * ptr1; char Normcall[10]; TRANSPORTENTRY * Session; TRANSPORTENTRY * InSession; if (LINK->LINKTYPE == 3) { L3CONNECTFAILED(LINK); // REPORT TO LEVEL 3 return; } if (LINK->CIRCUITPOINTER == 0) // No crosslink?? return; Buffer = GetBuff(); if (Buffer == NULL) return; // SET UP HEADER Buffer->PID = 0xf0; ptr1 = SetupNodeHeader(Buffer); Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; ptr1 += sprintf(ptr1, "%s %s\r", Msg, Normcall); Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION CLEARSESSIONENTRY(Session); if (InSession) { InSession->L4CROSSLINK = NULL; // CLEAR REVERSE LINK C_Q_ADD(&InSession->L4TX_Q, Buffer); PostDataAvailable(InSession); } else ReleaseBuffer(Buffer); } VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK) { // LINK SETUP COMPLETE struct DATAMESSAGE * Buffer; UCHAR * ptr1; char Normcall[10]; TRANSPORTENTRY * Session; TRANSPORTENTRY * InSession; if (LINK->LINKTYPE == 3) return; // UP/DOWN LINK if (LINK->CIRCUITPOINTER == 0) // No crosslink?? return; Buffer = GetBuff(); if (Buffer == NULL) return; // SET UP HEADER Buffer->PID = 0xf0; ptr1 = SetupNodeHeader(Buffer); Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; ptr1 += sprintf(ptr1, "Connected to %s\r", Normcall); Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY Session->L4STATE = 5; InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION if (InSession) { C_Q_ADD(&InSession->L4TX_Q, Buffer); PostDataAvailable(InSession); } } TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK) { TRANSPORTENTRY * NewSess = L4TABLE; int Index = 0; while (Index < MAXCIRCUITS) { if (NewSess->L4USER[0] == 0) { // Got One LINK->CIRCUITPOINTER = NewSess; // SETUP LINK-CIRCUIT CONNECTION memcpy(NewSess->L4USER, LINK->LINKCALL, 7); memcpy(NewSess->L4MYCALL, MYCALL, 7); // ALWAYS USE _NODE CALL NewSess->CIRCUITINDEX = Index; //OUR INDEX NewSess->CIRCUITID = NEXTID; NEXTID++; if (NEXTID == 0) NEXTID++; // kEEP nON-ZERO NewSess->L4TARGET.LINK = LINK; NewSess->L4CIRCUITTYPE = L2LINK | UPLINK; NewSess->L4STATE = 5; // SET LINK ACTIVE NewSess->SESSPACLEN = LINK->LINKPORT->PORTPACLEN; NewSess->SESSIONT1 = L4T1; // Default NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; return NewSess; } Index++; NewSess++; } return NULL; } VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly) // Digi it (if enabled) { // WE MAY HAVE DISABLED DIGIPEAT ALTOGETHER, (DIGIFLAG=0), // OR ALLOW ALLOW ONLY UI FRAMES TO BE DIGIED (DIGIFLAG=-1) // toPort and UIOnly are used for Cross Port digi feature int n; if (PORT->DIGIFLAG == 0 && toPort == 0) { ReleaseBuffer(Buffer); return; } OurCall[6] |= 0x80; // SET HAS BEEN REPEATED // SEE IF UI FRAME - scan forward for end of address bit n = 8; while ((OurCall[6] & 1) == 0) { OurCall += 7; if ((OurCall - &Buffer->CTL) > 56) { // Run off end before findin end of address ReleaseBuffer(Buffer); return; } } if (toPort) // Cross port digi { if (((OurCall[7] & ~PFBIT) == 3) || UIOnly == 0) { // UI or Digi all Buffer->PORT = toPort; // update port no in header PORT = GetPortTableEntryFromPortNum(toPort); if (PORT == NULL) ReleaseBuffer(Buffer); else PUT_ON_PORT_Q(PORT, Buffer); return; } else { ReleaseBuffer(Buffer); return; } } if ((OurCall[7] & ~PFBIT) == 3) { // UI // UI FRAME. IF DIGIMASK IS NON-ZERO, SEND TO ALL PORTS SET, OTHERWISE SEND TO DIGIPORT PORT->L2DIGIED++; if (toPort) { // Cross port digi PORT = GetPortTableEntryFromPortNum(toPort); Buffer->PORT = PORT->DIGIPORT; // update port no in header if (PORT == NULL) ReleaseBuffer(Buffer); else PUT_ON_PORT_Q(PORT, Buffer); return; } if (PORT->DIGIMASK == 0) { if (PORT->DIGIPORT) // Cross Band Digi? { Buffer->PORT = PORT->DIGIPORT; // update port no in header PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT); if (PORT == NULL) { ReleaseBuffer(Buffer); return; } } PUT_ON_PORT_Q(PORT, Buffer); } else { DigiToMultiplePorts(PORT, Buffer); ReleaseBuffer(Buffer); } return; } // Not UI - Only Digi if Digiflag not -1 if (PORT->DIGIFLAG == -1) { ReleaseBuffer(Buffer); return; } PORT->L2DIGIED++; if (PORT->DIGIPORT) // Cross Band Digi? { Buffer->PORT = PORT->DIGIPORT; // update port no in header PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT); if (PORT == NULL) { ReleaseBuffer(Buffer); return; } } PUT_ON_PORT_Q(PORT, Buffer); } BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg) { TRANSPORTENTRY * L4 = L4TABLE; struct DATAMESSAGE * Buffer; int i = MAXCIRCUITS; UCHAR * ptr; while (i--) { if ((CountBits64(L4->LISTEN) == 1) && ((1 << ((Msg->PORT & 0x7f) - 1) && L4->LISTEN))) { // See if he is calling our call UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted memcpy(ourcall, L4->L4USER, 7); ourcall[6] ^= 0x1e; // Flip SSID if (CompareCalls(Msg->DEST, ourcall)) { // Get Session Entry for Downlink TRANSPORTENTRY * NewSess = L4TABLE; struct _LINKTABLE * LINK; char Normcall[10]; int Index = 0; while (Index < MAXCIRCUITS) { if (NewSess->L4USER[0] == 0) { // Got One L4->L4CROSSLINK = NewSess; NewSess->L4CROSSLINK = L4; memcpy(NewSess->L4USER, L4->L4USER, 7); memcpy(NewSess->L4MYCALL, L4->L4USER, 7); NewSess->CIRCUITINDEX = Index; //OUR INDEX NewSess->CIRCUITID = NEXTID; NewSess->L4STATE = 5; NewSess->L4CIRCUITTYPE = L2LINK+UPLINK; NEXTID++; if (NEXTID == 0) NEXTID++; // kEEP nON-ZERO NewSess->SESSIONT1 = L4->SESSIONT1; NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; // SET UP NEW SESSION (OR RESET EXISTING ONE) FindLink(Msg->ORIGIN, ourcall, PORT->PORTNUMBER, &LINK); if (LINK == NULL) return FALSE; memcpy(LINK->LINKCALL, Msg->ORIGIN, 7); LINK->LINKCALL[6] &= 0xFE; memcpy(LINK->OURCALL, ourcall, 7); LINK->LINKPORT = PORT; LINK->L2TIME = PORT->PORTT1; /* // Copy Digis n = 7; ptr = &LINK->DIGIS[0]; while (axcalls[n]) { memcpy(ptr, &axcalls[n], 7); n += 7; ptr += 7; LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI } */ LINK->LINKTYPE = 2; // DOWNLINK LINK->LINKWINDOW = PORT->PORTWINDOW; RESET2(LINK); // RESET ALL FLAGS LINK->L2STATE = 5; // CONNECTED LINK->CIRCUITPOINTER = NewSess; NewSess->L4TARGET.LINK = LINK; if (PORT->PORTPACLEN) NewSess->SESSPACLEN = L4->SESSPACLEN = PORT->PORTPACLEN; L2SENDUA(PORT, Msg, Msg); // RESET OF DOWN/CROSSLINK L4->LISTEN = FALSE; // Take out of listen mode // Tell User Buffer = GetBuff(); if (Buffer == NULL) return TRUE; // SET UP HEADER Buffer->PID = 0xf0; ptr = &Buffer->L2DATA[0]; Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; ptr += sprintf(ptr, "Incoming call from %s\r", Normcall); Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); C_Q_ADD(&L4->L4TX_Q, Buffer); PostDataAvailable(L4); return TRUE; } Index++; NewSess++; } return FALSE; } } L4++; } return FALSE; }