1369 lines
61 KiB
C
Executable File
1369 lines
61 KiB
C
Executable File
/************************************************************************/
|
|
/* */
|
|
/* ***** ***** */
|
|
/* ***** ***** */
|
|
/* ***** ***** */
|
|
/* ***** ***** */
|
|
/* *************** *************** */
|
|
/* ***************** ***************** */
|
|
/* *************** *************** */
|
|
/* ***** ***** TheNetNode */
|
|
/* ***** ***** Portable */
|
|
/* ***** ***** Network */
|
|
/* ***** ***** Software */
|
|
/* */
|
|
/* File src/l4.c (maintained by: ???) */
|
|
/* */
|
|
/* This file is part of "TheNetNode" - Software Package */
|
|
/* */
|
|
/* Copyright (C) 1998 - 2008 NORD><LINK e.V. Braunschweig */
|
|
/* */
|
|
/* This program is free software; you can redistribute it and/or modify */
|
|
/* it under the terms of the NORD><LINK ALAS (Allgemeine Lizenz fuer */
|
|
/* Amateurfunk Software) as published by Hans Georg Giese (DF2AU) */
|
|
/* on 13/Oct/1992; either version 1, or (at your option) any later */
|
|
/* version. */
|
|
/* */
|
|
/* This program is distributed WITHOUT ANY WARRANTY only for further */
|
|
/* development and learning purposes. See the ALAS (Allgemeine Lizenz */
|
|
/* fuer Amateurfunk Software). */
|
|
/* */
|
|
/* You should have received a copy of the NORD><LINK ALAS (Allgemeine */
|
|
/* Lizenz fuer Amateurfunk Software) along with this program; if not, */
|
|
/* write to NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig */
|
|
/* */
|
|
/* Dieses Programm ist PUBLIC DOMAIN, mit den Einschraenkungen durch */
|
|
/* die ALAS (Allgemeine Lizenz fuer Amateurfunk Software), entweder */
|
|
/* Version 1, veroeffentlicht von Hans Georg Giese (DF2AU), */
|
|
/* am 13.Oct.1992, oder (wenn gewuenscht) jede spaetere Version. */
|
|
/* */
|
|
/* Dieses Programm wird unter Haftungsausschluss vertrieben, aus- */
|
|
/* schliesslich fuer Weiterentwicklungs- und Lehrzwecke. Naeheres */
|
|
/* koennen Sie der ALAS (Allgemeine Lizenz fuer Amateurfunk Software) */
|
|
/* entnehmen. */
|
|
/* */
|
|
/* Sollte dieser Software keine ALAS (Allgemeine Lizenz fuer Amateur- */
|
|
/* funk Software) beigelegen haben, wenden Sie sich bitte an */
|
|
/* NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
#include "tnn.h"
|
|
|
|
/* Layer 4 parameter ---------------------------------------------------*/
|
|
|
|
#define L4_ACKDEL 2 /* L4 Acknowledge Wartezeit fuer Circuit*/
|
|
#define L4_BSYDEL 180 /* L4 Busy-Wartezeit in Sekunden */
|
|
|
|
UWORD l4_beta1 = 3; /* RETRANS-TIMER (T1) = SRTT * BETA1 */
|
|
UWORD l4_beta2 = 1; /* ACK-TIMER (T2) = SRTT * BETA2 */
|
|
UWORD l4_beta3 = 20; /* BUSY/REQ-TIMEOUT (T3) = SRTT * BETA3 */
|
|
#define L4_BETA1 (l4_beta1)
|
|
#define L4_BETA2 (l4_beta2)
|
|
#define L4_BETA3 (l4_beta3)
|
|
|
|
#define L4_ALPHA1 2 /* Faktor fuer steigenden L4RTT */
|
|
#define L4_ALPHA2 3 /* Faktor fuer fallenden L4RTT */
|
|
#define L4_IRTT 1000U /* Startwert SRTT (IRTT) */
|
|
|
|
#define L4_RETRY 3 /* Anzahl der maximalen Retries */
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
LHEAD l4frel; /* freie L4-Controllbloecke */
|
|
LHEAD l4actl; /* benutzte L4-Controllebloecke */
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
static void l4rtt(CIRBLK *, int);
|
|
static void l4setT1(CIRBLK *, MBHEAD *);
|
|
static void l4setT2(CIRBLK *);
|
|
static void l4setT3(CIRBLK *, int);
|
|
static void l4clrT3(CIRBLK *);
|
|
static void l4newstate(UWORD);
|
|
static void clr4rx(BOOLEAN);
|
|
static void chksts(void);
|
|
static void takfrm(MBHEAD *);
|
|
static void sndfrm(int, MBHEAD *);
|
|
static void l4nsta(WORD);
|
|
static void endcir(void);
|
|
static void clrcir(void);
|
|
static void sconrq(void);
|
|
static void sdisrq(void);
|
|
#ifdef NEW_L4
|
|
static void spidchg(UBYTE);
|
|
#endif
|
|
static void sndack(void);
|
|
static void ackhdr(void);
|
|
static void itol3(MBHEAD *);
|
|
static void kilfra(void);
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void initl4(void) /* Level 4 initialisieren */
|
|
{
|
|
int i; /* Sratch Zaehler */
|
|
|
|
inithd(&l4rxfl); /* Liste fuer empfangene Frames */
|
|
inithd(&l4frel); /* freie L4-Controllbloecke */
|
|
inithd(&l4actl); /* benutzte L4-Controllebloecke */
|
|
|
|
for (i = 0, cirpoi = cirtab; /* gesamte Circuit Tabelle */
|
|
i < NUMCIR; /* bearbeiten */
|
|
++i, ++cirpoi)
|
|
{
|
|
cirpoi->state = L4SDSCED; /* Eintrag ist leer */
|
|
cirpoi->numrx = 0; /* keine Frames empfangen */
|
|
cirpoi->numtx = 0; /* keine Frames zu senden */
|
|
cirpoi->ll4txNR = 0; /* last l4 ack sent */
|
|
cirpoi->fragme = NULL; /* kein Frame Fragment da */
|
|
inithd(&cirpoi->mbhdrx); /* Empfangskette ist leer */
|
|
inithd(&cirpoi->mbhdtx); /* Sendekette ist leer */
|
|
inithd(&cirpoi->mbhdos); /* Kette "ausser Reihenfolge" */
|
|
clrcir(); /* Rest initialisieren */
|
|
relink((LEHEAD *)cirpoi, /* in die Liste fuer unbenutzte */
|
|
(LEHEAD *)l4frel.tail); /* Controllbloecke */
|
|
}
|
|
nmbcir = nmbcir_max = 0; /* Circuit-Zaehler */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
#define START 0
|
|
#define STOP 1
|
|
#define CLEAR 2
|
|
static void l4rtt(CIRBLK *cirp, int what)
|
|
{
|
|
UWORD RTT, SRTT;
|
|
|
|
switch (what) {
|
|
case START : cirp->RTT = 1;
|
|
cirp->RTTvs = cirp->l4vs;
|
|
break;
|
|
|
|
case STOP : RTT = cirp->RTT;
|
|
SRTT = cirp->SRTT;
|
|
|
|
if (RTT > SRTT)
|
|
SRTT = (L4_ALPHA1 * SRTT + RTT * 100) / (L4_ALPHA1 + 1);
|
|
else
|
|
SRTT = (L4_ALPHA2 * SRTT + RTT * 100) / (L4_ALPHA2 + 1);
|
|
if (SRTT < L4_IRTT / 10)
|
|
SRTT = L4_IRTT / 10;
|
|
if (SRTT > L4_IRTT * 10)
|
|
SRTT = L4_IRTT * 10;
|
|
|
|
cirp->SRTT = SRTT;
|
|
|
|
case CLEAR : cirp->RTT =
|
|
cirp->RTTvs = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* RETRY TIMER (T1) */
|
|
/* */
|
|
/* Der T1 wird fuer jedes Frame in der Sendewarteschlange verwaltet und */
|
|
/* bei jeder Aussendung dieses Frames gestartet. Wenn er ablaeuft, wird */
|
|
/* das Frame erneut ausgesendet. L4_BETA1 muss so gewaehlt werden, dass */
|
|
/* der T1 nicht zu frueh ablaeuft und damit das Frame doppelt geschickt */
|
|
/* wird. */
|
|
/* */
|
|
/************************************************************************/
|
|
static void l4setT1(CIRBLK *cirp, MBHEAD *fbp) {
|
|
if (fbp->l4trie > 1)
|
|
fbp->l4time = (cirp->SRTT/100 + 1) * 2 * L4_BETA1;
|
|
else
|
|
fbp->l4time = (cirp->SRTT/100 + 1) * L4_BETA1;
|
|
if (fbp->l4time < 30) /* nicht unter 30s */
|
|
fbp->l4time = 30;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* ACKNOWLEDGE TIMER (T2) */
|
|
/* */
|
|
/* Der T2 bestimmt die maximale Verzoegerung, bis ein empfangenes Frame */
|
|
/* bestaetigt wird. L4_BETA sollte so gewaehlt werden, dass Frames */
|
|
/* moeglichst schnell bestaetigt werden, aber nicht zuviele ACK-Frames */
|
|
/* generiert werden. */
|
|
/* */
|
|
/************************************************************************/
|
|
static void l4setT2(CIRBLK *cirp) {
|
|
UWORD frack;
|
|
|
|
frack = (cirp->SRTT/100 + 1) * L4_BETA2;
|
|
|
|
if (frack < 10)
|
|
frack = 10;
|
|
|
|
if ( cirp->acktim > frack
|
|
&& cirp->numrx < (cirp->window / 2))
|
|
cirp->acktim = frack;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* CONREQ/BUSY TIMEOUT (T3) */
|
|
/* */
|
|
/* Der T3 bestimmt die Wartezeit zwischen der Wiederholung von CONACK/ */
|
|
/* DISREQ und der Linkkontrolle bei Busy. */
|
|
/* Der T3 sollte nicht zu gering gewaehlt werden. */
|
|
/* */
|
|
/************************************************************************/
|
|
static void l4setT3(CIRBLK *cirp, int what) {
|
|
#define L4TCREQ 0
|
|
#define L4TDREQ 1
|
|
#define L4TBUSY 2
|
|
switch (what) {
|
|
case L4TCREQ :
|
|
case L4TDREQ : cirp->traout = (cirp->SRTT/100 + 1) * L4_BETA3;
|
|
if (cirp->traout < 30)
|
|
cirp->traout = 30;
|
|
break;
|
|
case L4TBUSY : cirp->traout = L4_BSYDEL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void l4clrT3(CIRBLK *cirp) {
|
|
cirp->traout = 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void l4newstate(UWORD state)
|
|
{
|
|
if (state) { /* Verbindung steht */
|
|
if (cirpoi->state == L4SDSCED) { /* ein neuer Connect */
|
|
ulink((LEHEAD *)cirpoi); /* Circuit aus der Freiliste */
|
|
relink((LEHEAD *)cirpoi, /* in die Liste fuer benutzte */
|
|
(LEHEAD *)l4actl.tail); /* Controllbloecke */
|
|
if (++nmbcir > nmbcir_max) /* Maximalanzahl der Circuits */
|
|
nmbcir_max = nmbcir;
|
|
}
|
|
} else { /* Verbindungsabbruch */
|
|
if (cirpoi->state != L4SDSCED) { /* Circuit wird inaktiv */
|
|
ulink((LEHEAD *)cirpoi); /* Circuit aus der Aktivliste */
|
|
relink((LEHEAD *)cirpoi, /* in die Liste fuer unbenutzte */
|
|
(LEHEAD *)l4frel.tail); /* Controllbloecke */
|
|
nmbcir--; /* einen aktiven Circuit weniger */
|
|
}
|
|
}
|
|
#ifdef __WIN32__
|
|
cirpoi->state = (char)state; /* neuen Status setzen */
|
|
#else
|
|
cirpoi->state = state; /* neuen Status setzen */
|
|
#endif /* WIN32 */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
void l4tx(void) /* Frames senden */
|
|
{
|
|
UWORD unack; /* unbestaetigte Frames */
|
|
UWORD isweg; /* schon gesendete Frames */
|
|
MBHEAD *fbp; /* naechstes Frame */
|
|
CIRBLK *nxtcir; /* Zeiger auf naechsten CIRBLK */
|
|
|
|
for (cirpoi = (CIRBLK *) l4actl.head; /* alle aktiven Circuits */
|
|
cirpoi != (CIRBLK *) &l4actl.head; /* durchgehen */
|
|
cirpoi = nxtcir) /* und zum naechsten Circuit */
|
|
{
|
|
nxtcir = (CIRBLK *) cirpoi->head; /* wegen l4newstate()... */
|
|
|
|
if ((cirpoi->state == L4SIXFER) && /* Eintrag hat L4-Verbindung */
|
|
(!(cirpoi->l4flag & L4FPBUSY))) { /* und Partner nicht choked */
|
|
|
|
unack = (cirpoi->l4vs - cirpoi->l4rxvs) & 0x7F;
|
|
if ((unack < cirpoi->numtx) /* nur wenn Fenstergroesse */
|
|
&& (unack < cirpoi->window)) { /* noch nicht erreicht */
|
|
|
|
fbp = (MBHEAD *) cirpoi->mbhdtx.head; /* Anfang der Sendeliste */
|
|
for (isweg = 0; isweg < unack; ++isweg)/* schon gesendete offene*/
|
|
fbp = (MBHEAD *) fbp->nextmh; /* Frames, die auf Be- */
|
|
/* staetigung warten, */
|
|
/* uebergehen */
|
|
do {
|
|
fbp->l4trie = 0; /* noch kein Versuch */
|
|
sndfrm(cirpoi->l4vs++, fbp); /* naechstes Frame senden */
|
|
|
|
fbp = (MBHEAD *) fbp->nextmh; /* naechstes Frame */
|
|
} while ((++unack < cirpoi->numtx) /* bis keine Frames mehr */
|
|
&&(unack < cirpoi->window)); /* o.Fenstergroesse ueber-*/
|
|
/* schritten */
|
|
if (!cirpoi->RTT) /* keine Messung unterwegs*/
|
|
l4rtt(cirpoi, START); /* dann neue starten */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Diese Routine prueft, ob das aktuelle Frame ein Circuit von uns sein */
|
|
/* koennte. Das ist ziemlich unsauber, eigentlich sollte nur im L4 */
|
|
/* landen, was auch direkt an uns addressiert ist. Das Lokal-Konzept */
|
|
/* von alten NET/ROMs zwingt uns aber, auch "fremde" Frames zu durch- */
|
|
/* suchen. */
|
|
BOOLEAN l4istome(char *srcid, char *dstid)
|
|
{
|
|
if (l4opco != L4CONREQ) /* es ist kein Connect Request */
|
|
{
|
|
if ( (l4hdr0 < NUMCIR) /* Index im richtigen Bereich? */
|
|
/* die Verbindung haben wir */
|
|
&& (cirtab[l4hdr0].state != L4SDSCED)
|
|
/* und die ID stimmt */
|
|
&& (cirtab[l4hdr0].ideige == l4hdr1))
|
|
{
|
|
cirpoi = &cirtab[l4hdr0]; /* CIRCUIT-Eintrag ist gueltig! */
|
|
if (l4opco != L4CONACK) /* CONACK kann vom Host kommen */
|
|
if (!cmpid(srcid, cirpoi->l3node))
|
|
return(FALSE); /* Absender muss stimmen */
|
|
if (!cmpid(dstid, myid)) /* nicht direkt an uns ? */
|
|
if (!cmpid(dstid, cirpoi->destca))
|
|
return(FALSE); /* Ziel muss stimmen */
|
|
return(TRUE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
void l4rx(NODE *srcnod, NODE *dstnod, MBHEAD *fbp) /* Frame empfangen */
|
|
{
|
|
char usrcall[L2IDLEN]; /* Call des Users */
|
|
char orgnod[L2IDLEN]; /* Call des Absender Knotens */
|
|
UWORD fenste; /* Fenstergroesse */
|
|
int i; /* Scratch Zaehler */
|
|
MBHEAD *antwor; /* Antwort auf das empfange Frame */
|
|
CIRBLK *cirent; /* Eintrag des Users in der CIRTAB */
|
|
CIRBLK nocir; /* Platzhalter wenn kein CIRCUIT */
|
|
char *viapoi; /* Zeiger fuer VIA-Liste */
|
|
char upno[L2IDLEN]; /* Uplink Knoten */
|
|
char upnov[L2VLEN+1]; /* L2-via zu dem Uplink Knoten */
|
|
|
|
/* Hier gab es frueher oefters Abstuerze, da itol3() immer einen */
|
|
/* gueltigen cirpoi erwartet, aber wir haben eventuell keinen, weil wir */
|
|
/* die Verbindung nicht kennen. Dies betrifft spaete Versionen von TN */
|
|
/* und einige fruehe Versionen von TNN. */
|
|
|
|
cirpoi = &nocir;
|
|
cpyid(cirpoi->l3node, srcnod->id);
|
|
|
|
if (l4opco != L4CONREQ) /* es ist kein Connect Request */
|
|
{
|
|
if ( (l4hdr0 < NUMCIR) /* Index im richtigen Bereich? */
|
|
&& (cirtab[l4hdr0].state != L4SDSCED) /* Verbindung haben wir */
|
|
&& (cirtab[l4hdr0].ideige == l4hdr1)) /* und die ID stimmt */
|
|
{
|
|
cirpoi = &cirtab[l4hdr0]; /* CIRCUIT-Eintrag ist gueltig! */
|
|
l4pidx = cirpoi->idxpar; /* Partner-Index uebernehmen */
|
|
l4pcid = cirpoi->idpart; /* Partner-ID uebernehmen */
|
|
}
|
|
else
|
|
{
|
|
if ( (l4opco != L4CONACK) /* alles ausser CONACK = Muell */
|
|
&& !(l4hdr4 & L4CCHOKE)
|
|
&& !(l4opco & L4DISREQ))
|
|
{
|
|
l4pidx = l4hdr2; /* Partner Circuit-Index */
|
|
l4pcid = l4hdr3; /* und -ID fuer Antwort */
|
|
l4ahd2 =
|
|
l4ahd3 = 0;
|
|
l4aopc = L4DISREQ; /* Disconnect Request als Antwort */
|
|
itol3(gennhd()); /* Antwort &nocir->l3node=despoi */
|
|
}
|
|
dealmb(fbp); /* schlechter Header, entsorgen */
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (l4opco) /* Ueber Opcode verzweigen */
|
|
{
|
|
case L4CONREQ: /* Connect-Request oder ein */
|
|
if (((fbp->mbpc - fbp->mbgc) >= 15) && /* Frame lang genug ? */
|
|
((fenste = getchr(fbp) & 0x7F) != 0) && /* und Fenster da */
|
|
(getfid(usrcall, fbp) == TRUE) && /* gueltiges Usercall */
|
|
(getfid(orgnod, fbp) == TRUE)) /* gueltiger Absender */
|
|
{
|
|
/*****************************************************************************\
|
|
* *
|
|
* Protokollerweiterung: Beim Connect-Request-Frame wird im Anschluss an die *
|
|
* Fenstergroesse der Uplinkknoten und die Via-Liste beim Uplink uebertragen *
|
|
* (nullterminiert), also maximal 64 Bytes zusaetzlich. Diese Erweiterung ist *
|
|
* kompatibel zur bisherigen Software, da laengere Frames nicht weiter unter- *
|
|
* sucht werden. Um auch hier spaetere Erweiterungen zu ermoeglichen, wird *
|
|
* der Rest des Frames nicht untersucht. Ist kein Uplinkknoten im Frame *
|
|
* enthalten, also bei aelterer Software, wird das Call des Absenderknotens *
|
|
* als Uplinkknoten eingesetzt. *
|
|
* *
|
|
\*****************************************************************************/
|
|
if ( !(fbp->mbpc - fbp->mbgc >= L2IDLEN) /* neue Software? */
|
|
|| (!getfid(upno,fbp))) /* kein Uplinkknoten? */
|
|
{
|
|
cpyid(upno,orgnod); /* Absenderknoten als Uplinkknoten annehmen */
|
|
*upnov = '\0'; /* und dort keine Digikette */
|
|
}
|
|
else /* neue Software */
|
|
{
|
|
for (viapoi = upnov; /* Vialiste holen */
|
|
viapoi < upnov + L2VLEN; /* max. 8 Calls */
|
|
viapoi = viapoi + L2IDLEN) /* viapoi -> naechstes Call */
|
|
{
|
|
if (!getfid(viapoi, fbp)) break; /* Ende Vialiste? */
|
|
}
|
|
*viapoi = '\0'; /* Ende markieren */
|
|
}
|
|
|
|
l4pidx = l4hdr0; /* Index */
|
|
l4pcid = l4hdr1; /* und ID des Parnters merken */
|
|
cirent = NULL;
|
|
for (i = 0, cirpoi = cirtab; /* Circuit Tabelle absuchen */
|
|
i < NUMCIR;++i, ++cirpoi)
|
|
{
|
|
if (cirpoi->state != L4SDSCED) { /* Verbingung besteht */
|
|
if ( (cirpoi->idxpar == l4hdr0) /* PartnerIndex stimmt*/
|
|
&& (cirpoi->idpart == l4hdr1) /* Parner-ID stimmt */
|
|
&& cmpid(usrcall, cirpoi->upcall)/* UserCall stimmt */
|
|
&& cmpid(orgnod, cirpoi->downca))/* Absender stimmt */
|
|
break; /* wir haben ihn gefunden ! */
|
|
}
|
|
else /* sonst merken wir uns den freien */
|
|
if (cirent == NULL) /* Platz in der Circuit Table */
|
|
cirent = cirpoi;
|
|
}
|
|
if (i == NUMCIR) { /* Eintrag war nicht in der Liste */
|
|
if ((cirent != NULL) && /* wenn wir noch ein Plaetzchen */
|
|
(fvalca(usrcall) == YES)) /* haben und Call ok */
|
|
{
|
|
cirpoi = cirent; /* dann Eintrag nehmen */
|
|
cpyid(cirpoi->upcall, usrcall); /* Usercall setzen */
|
|
cpyid(cirpoi->downca, orgnod); /* Absenderknoten */
|
|
cpyid(cirpoi->upnod, upno); /* Uplink Knoten */
|
|
cpyidl(cirpoi->upnodv, upnov); /* Uplink via's */
|
|
|
|
cirpoi->idxpar = l4hdr0; /* Partner-Index merken */
|
|
cirpoi->idpart = l4hdr1; /* Parner-ID merken */
|
|
|
|
cirpoi->ideige = rand() % 256; /* eigene Zufalls-ID */
|
|
cpyid(cirpoi->l3node, srcnod->id);
|
|
if (fbp->l3_typ == L3LOCAL) /* ein Lokal? */
|
|
cpyid(cirpoi->destca, dstnod->id);
|
|
else
|
|
cpyid(cirpoi->destca, myid);
|
|
|
|
cirpoi->tranoa = ininat; /* Timeout setzen */
|
|
}
|
|
else
|
|
{ /* kein Platz oder ungueltiges Call */
|
|
l4ahd2 = /* Antwort aufbauen */
|
|
l4ahd3 = 0;
|
|
l4aopc = L4CONACK | L4CCHOKE; /* Antwortframe aufbauen */
|
|
antwor = gennhd();
|
|
antwor->l2link = NULL;
|
|
cpyid(antwor->destcall, srcnod->id);
|
|
relink((LEHEAD *)antwor,(LEHEAD *) l3txl.tail);
|
|
break; /* und in die Sendekette haengen. */
|
|
}
|
|
} /* den Eintrag gibt es schon */
|
|
|
|
#ifdef CONL3LOCAL
|
|
/* Unser Nachbar ist in der Liste und */
|
|
/* schickt ein weiteres statusflag CONREQ.*/
|
|
if (i != NUMCIR)
|
|
/* damit brechen wir hier ab. */
|
|
break;
|
|
#endif
|
|
/* Vom Partner vorgeschlagene Fenstergroesse uebernehmen */
|
|
/* wenn sie nicht groesser als unsere maximal-Groesse ist. */
|
|
cirpoi->window = (fenste > trawir) ? trawir : fenste;
|
|
clrcir(); /* Eintrag initialisieren */
|
|
#ifdef CONL3LOCAL
|
|
/* Statusflag CONACK NUR fuer das eigene Node Mycall */
|
|
/* vorbereiten/verschicken. */
|
|
if (fbp->l3_typ != L3LOCAL)
|
|
{
|
|
#endif
|
|
l4ahd2 = (UBYTE) (cirpoi - cirtab); /* Unseren Index setzen */
|
|
l4ahd3 = cirpoi->ideige; /* Unsere (Zufalls-) ID */
|
|
l4aopc = L4CONACK; /* Connect Acknowledge */
|
|
putchr(cirpoi->window, (antwor = gennhd())); /* endgueltige */
|
|
/* Fenstergroesse zurueck an den Partner */
|
|
itol3(antwor); /* senden */
|
|
#ifdef CONL3LOCAL
|
|
}
|
|
#endif
|
|
switch (cirpoi->state) {
|
|
case L2SDSCED :
|
|
case L2SLKSUP :
|
|
#ifdef CONL3LOCAL
|
|
if (fbp->l3_typ != L3LOCAL)
|
|
l4newstate(L4SIXFER); /* STATUS = connected */
|
|
else
|
|
/* Es gibt noch keine Verbindung! */
|
|
l4newstate(L4SLKSUP); /* STATUS = link setup */
|
|
#else
|
|
l4newstate(L4SIXFER); /* STATUS = connected */
|
|
#endif
|
|
l2tol7(L4MCONNT, cirpoi, L4_USER);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case L4CONACK:
|
|
if (cirpoi->state == L4SLKSUP) { /* nur wenn Connect von uns */
|
|
if (!(l4hdr4 & L4CCHOKE)) { /* verlangt war und Partner */
|
|
if (fbp->mbgc < fbp->mbpc) { /* nicht choked. */
|
|
cirpoi->window = getchr(fbp); /* Entgueltige Fenstergr. */
|
|
cirpoi->idpart = l4hdr3; /* Partner ID */
|
|
cirpoi->idxpar = l4hdr2; /* Partner Index */
|
|
clrcir(); /* Eintrag initialisieren */
|
|
cirpoi->tranoa = ininat; /* Timeout setzen */
|
|
l4newstate(L4SIXFER); /* Status = connected */
|
|
/**********************************************************/
|
|
/* Protokollerweiterung nach DB7KG, 23.11.1996 */
|
|
/* Bei jedem eingehenden Connect-ACK setzen wir den Ziel- */
|
|
/* Node auf den Absender, von dem das Frame kam. Damit */
|
|
/* soll bei einem Connect an einen Local der tatsaechliche*/
|
|
/* L3 Partner als Ziel gewaehlt werden, damit es keine */
|
|
/* Probleme beim umrouten dieser Verbindungen gibt. */
|
|
/* (Ein umrouten ist dann nur bis zum Partner moeglich, */
|
|
/* da kuenftig Frames mit dieser Addresse gesendet werden)*/
|
|
/* HINWEIS: Impelementierung hier so nur moeglich, weil */
|
|
/* l4rx() direkt aus dem L3 aufgerufen wird! (fuer DF2AU) */
|
|
/**********************************************************/
|
|
cpyid(cirpoi->l3node, srcnod->id);
|
|
l2tol7(L4MCONNT, cirpoi, L4_USER); /* L7 melden */
|
|
}
|
|
}
|
|
else /* Partner ist choked (busy) */
|
|
l4nsta(L4MBUSYF); /* an L7 melden */
|
|
}
|
|
break;
|
|
|
|
case L4DISREQ: /* Disconnect Request */
|
|
l4ahd2 =
|
|
l4ahd3 = 0; /* Antwort aufbauen */
|
|
l4aopc = L4DISACK; /* Opcode: Disconnect Ackn. */
|
|
itol3(gennhd()); /* Frame senden */
|
|
clr4rx(1); /* restliche Infos senden */
|
|
l4nsta(L4MDISCF); /* an L7 melden */
|
|
break;
|
|
|
|
case L4DISACK: /* Disconnect Acknowledge */
|
|
if (cirpoi->state == L4SDSCRQ) /* Hatten wir Disc gesendet ?*/
|
|
l4nsta(L4MDISCF); /* wenn ja dann dem L7 melden*/
|
|
break;
|
|
|
|
case L4INFTRA: /* Infoframe */
|
|
if (cirpoi->state != L4SIXFER) /* nur wenn connected */
|
|
break;
|
|
chksts(); /* Status Info auswerten */
|
|
/* TEST DG9OBU */
|
|
if (((fbp->l4seq = /* passt Frame ins Fenster */
|
|
(l4hdr2 - cirpoi->l4vr) & 0x7f) < cirpoi->window)
|
|
&& !(cirpoi->l4flag & L4FBUSY))
|
|
{ /* und Partner nicht busy */
|
|
fbp->morflg = (l4hdr4 & L4CMORE) != 0; /* Fragmentiert ? */
|
|
if (fbp->l4seq == 0) { /* passt Sequenz? */
|
|
takfrm(fbp); /* Frame uebernehmen */
|
|
/* Frames, die ausser der Reihe kamen ueberpruefen */
|
|
for (i = 1, antwor = (MBHEAD *) cirpoi->mbhdos.head;
|
|
(MBHEAD *) &(cirpoi->mbhdos) != antwor;
|
|
antwor = (MBHEAD *) antwor->nextmh)
|
|
{
|
|
if ((antwor->l4seq -= i) == 0) { /* passt das Frame ? */
|
|
fbp = (MBHEAD *) antwor->prevmh; /* ja, nehmen und */
|
|
takfrm((MBHEAD *) ulink((LEHEAD *)antwor));/*naechstes*/
|
|
antwor = fbp; /* Frame ueberpruefen */
|
|
++i; /* Offset eins weiter */
|
|
}
|
|
}
|
|
cirpoi->l4rs = 0; /* Antwort: ACK (Bestaetigung) */
|
|
|
|
/* Dynamisches ACKDEL */
|
|
|
|
i = cirpoi->numrx - (cirpoi->window /2);
|
|
if (i < 1) i = 1;
|
|
cirpoi->acktim = i * L4_ACKDEL ;
|
|
}
|
|
else
|
|
{ /* Sequenz passt nicht */
|
|
for (antwor = (MBHEAD *) cirpoi->mbhdos.head;;) {
|
|
/* Wenn die Kette dort zuende ist, dann dort einhaengen */
|
|
if ((MBHEAD *) &(cirpoi->mbhdos) == antwor) {
|
|
relink((LEHEAD *)fbp, (LEHEAD *)cirpoi->mbhdos.tail);
|
|
break;
|
|
}
|
|
/* Wenn das Frame schoneinmal gekommen ist -> weg damit */
|
|
if (antwor->l4seq == fbp->l4seq) {
|
|
dealmb(fbp);
|
|
break;
|
|
}
|
|
/* sonst an der passenden Stelle einhaengen */
|
|
if (antwor->l4seq > fbp->l4seq) {
|
|
relink((LEHEAD *)fbp, (LEHEAD *)antwor->prevmh);
|
|
break;
|
|
}
|
|
antwor = (MBHEAD *) antwor->nextmh; /* ein Frame weiter */
|
|
}
|
|
|
|
if (cirpoi->l4rs == 0) { /* Wenn ACK gesendet werden soll */
|
|
cirpoi->l4rs = 1; /* dann nun einen N(ot)AK senden */
|
|
cirpoi->acktim = L4_ACKDEL; /* ACK-Wartezeit setzen */
|
|
}
|
|
}
|
|
return; /* Frame verarbeitet */
|
|
}
|
|
else /* ungueltiges Frame oder wir haben */
|
|
cirpoi->acktim = L4_ACKDEL; /* zuwenig Buffers (choked), dann */
|
|
break; /* Antwort verzoegern. */
|
|
|
|
case L4INFACK: /* Info Acknowledge */
|
|
if (cirpoi->state == L4SIXFER)/* nur wenn connected */
|
|
chksts(); /* Statusinformation auswerten */
|
|
|
|
#ifdef NEW_L4
|
|
break;
|
|
|
|
case L4PIDCHG: /* PID-Change */
|
|
cirpoi->pid = l4hdr3; /* neue PID uebernehmen */
|
|
#endif
|
|
}
|
|
dealmb(fbp); /* hier landen wir bei ungueltigen */
|
|
/* Opcodes oder wenn die Bearbeitung*/
|
|
|
|
/* fertig ist, auf jeden Fall Frame */
|
|
} /* wegwerfen. */
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
void l4rest(void) /* sonstige L4-Funktionen */
|
|
{
|
|
UWORD rx_unack;
|
|
UBYTE w2;
|
|
CIRBLK *nxtcir;
|
|
|
|
for (cirpoi = (CIRBLK *) l4actl.head; /* alle aktiven Circuits */
|
|
cirpoi != (CIRBLK *) &l4actl.head; /* durchgehen */
|
|
cirpoi = nxtcir) {
|
|
nxtcir = (CIRBLK *) cirpoi->head; /* vorher schon merken */
|
|
if ((cirpoi->l4flag & L4FDIMM)) /* sofortiger Abwurf */
|
|
{
|
|
l4nsta(L4MDISCF); /* Disconnect melden */
|
|
}
|
|
else
|
|
if (cirpoi->state == L4SIXFER) {/* nur fuer connectete Eintraege */
|
|
if ( (cirpoi->l4flag & L4FDSLE) /* Abwurf gefordert */
|
|
&& (cirpoi->numtx == 0)) /* und alle Infos gesendet */
|
|
endcir(); /* Eintrag loeschen */
|
|
else
|
|
{
|
|
clr4rx(0); /* sonst Info senden */
|
|
|
|
/* Fruehes Ack */
|
|
rx_unack = ((cirpoi->l4vr | 0x100) - cirpoi->ll4txNR) & 0x7F;
|
|
w2 = cirpoi->window/2;
|
|
|
|
if (!(cirpoi->l4flag & L4FBUSY)) { /* wir sind nicht busy */
|
|
if (nmbfre < 30) { /* Aber zu wenig Platz */
|
|
cirpoi->l4flag |= L4FBUSY; /* Dann sind wir nun Busy ! */
|
|
cirpoi->l4rs = 0; /* Antwort sofort senden */
|
|
sndack(); /* ACK senden */
|
|
}
|
|
else { /* ich bin nicht busy */
|
|
if ( cirpoi->acktim > 1
|
|
&& rx_unack >= w2
|
|
&& cirpoi->numrx < w2) /* und es ist noch Platz */
|
|
sndack();
|
|
}
|
|
}
|
|
else { /* im Moment sind wir Busy */
|
|
if ( nmbfre > 62 /* wieder genug Platz ? */
|
|
&& cirpoi->numrx < w2) { /* Und nicht zuviel ? */
|
|
cirpoi->l4flag &= ~L4FBUSY; /* Busy aufheben */
|
|
sndack(); /* und ACK senden */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef L4TIMEOUTAUSGABE
|
|
/************************************************************************/
|
|
/* */
|
|
/* Vor dem Disconect eine Info-Meldung an den User senden. */
|
|
/* */
|
|
/************************************************************************/
|
|
static void l4DiscInfo(void)
|
|
{
|
|
MBHEAD *mbp;
|
|
|
|
if ((mbp = (MBHEAD *) allocb(ALLOC_MBHEAD)) != NULL) /* Buffer besorgen. */
|
|
{
|
|
putchr('\r', mbp);
|
|
putalt(alias, mbp);
|
|
putid(myid, mbp);
|
|
|
|
putstr("> Timeout (", mbp);
|
|
putnum(ininat, mbp); /* Timeout in s ausgeben */
|
|
putstr("s) run off.\r", mbp);
|
|
rwndmb(mbp);
|
|
|
|
sndfrm(cirpoi->l4vs++, mbp); /* Info senden */
|
|
dealmb(mbp); /* Buffer entsorgen. */
|
|
}
|
|
}
|
|
#endif /* L4TIMEOUTAUSGABE */
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
void trasrv(void) /* Timerservice fuer den L4 */
|
|
{
|
|
UWORD actsts; /* Status des aktuellen Eintrages */
|
|
UWORD fropen; /* Zahl der unbestaetigten Frames */
|
|
UWORD tosend; /* Zahl der zu sendenden Frames */
|
|
MBHEAD *fbp; /* aktuelles Frame */
|
|
UWORD rx_unack;
|
|
CIRBLK *nxtcir;
|
|
char txvs;
|
|
|
|
for (cirpoi = (CIRBLK *) l4actl.head; /* alle aktiven Circuits */
|
|
cirpoi != (CIRBLK *) &l4actl.head; /* durchgehen */
|
|
cirpoi = nxtcir) {
|
|
nxtcir = (CIRBLK *) cirpoi->head; /* vorher schon merken */
|
|
if ((actsts = cirpoi->state) != L4SDSCED) { /* nur fuer aktive Verb.*/
|
|
if (cirpoi->RTT) /* RTT-Messung */
|
|
cirpoi->RTT++;
|
|
if (cirpoi->traout != 0) { /* Timeout noch nicht abgelaufen? */
|
|
if (--cirpoi->traout == 0) {/* Timeout nun abgelaufen ? */
|
|
if (actsts == L4SIXFER) { /* nur fuer connectete Eintraege */
|
|
cirpoi->l4flag &= ~L4FPBUSY; /* nichts mehr senden */
|
|
} else {
|
|
if (++cirpoi->l4try < L4_RETRY) /* nochmal Versuchen ? */
|
|
{
|
|
if (actsts == L4SLKSUP) /* CON REQ kam nicht an, nochmal */
|
|
sconrq();
|
|
else
|
|
sdisrq(); /* DISC REQ kam nicht an, nochmal */
|
|
} else
|
|
l4nsta(L4MFAILW); /* Fehler an L7 melden */
|
|
}
|
|
}
|
|
}
|
|
else { /* Timeout ist abgelaufen */
|
|
if ((actsts == L4SIXFER) /* connected und Frames unbestaetigt ? */
|
|
&& ((fropen = (cirpoi->l4vs - cirpoi->l4rxvs) & 0x7F) != 0)) {
|
|
/* Frames wiederholen */
|
|
for (tosend = 0, fbp = (MBHEAD *) cirpoi->mbhdtx.head;
|
|
tosend < fropen;
|
|
++tosend, fbp = (MBHEAD *) fbp->nextmh) {
|
|
if (--fbp->l4time == 0) { /* wenn Timeout um */
|
|
if (++fbp->l4trie < L4_RETRY) {/* und noch Versuch frei */
|
|
txvs = fbp->l4seq; /* Framenummer holen */
|
|
if (cirpoi->RTTvs == txvs) /* bei Wiederholung RTT- */
|
|
l4rtt(cirpoi, CLEAR); /* Messung verwerfen */
|
|
sndfrm(txvs, fbp); /* Frames senden */
|
|
} else {
|
|
l4nsta(L4MFAILW); /* sonst L7 failure melden */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (actsts == L4SIXFER) { /* connected ? */
|
|
if ( (cirpoi->acktim != 0) /* ACK noch nicht gesendet */
|
|
&& (--cirpoi->acktim == 0)) /* aber Timer laeuft gerade aus */
|
|
{
|
|
/* schnelle Flow-control */
|
|
rx_unack = ((cirpoi->l4vr | 0x100) - cirpoi->ll4txNR) & 0x7F;
|
|
|
|
if ( cirpoi->numrx > (cirpoi->window / 2)
|
|
&& rx_unack >= cirpoi->window)
|
|
{
|
|
cirpoi->l4flag |= L4FBUSY;
|
|
cirpoi->l4rs = 0;
|
|
}
|
|
sndack(); /* dann ein ACK senden */
|
|
}
|
|
|
|
if ( (cirpoi->tranoa != 0) /* No-activity Timeout laeuft aus */
|
|
&& (--cirpoi->tranoa == 0))
|
|
#ifndef L4TIMEOUTAUSGABE
|
|
endcir(); /* Verbindung trennen */
|
|
#else
|
|
{
|
|
l4DiscInfo();/* Vor Trennung, Timeout-Meldung zum User senden.*/
|
|
endcir(); /* Verbindung trennen */
|
|
}
|
|
#endif /* L4TIMEOUTAUSGABE */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
void newcir(void) /* neuen Circuit-Eintrag aufbauen */
|
|
{
|
|
clrcir(); /* Eintrag erstmal loeschen */
|
|
cirpoi->ideige = rand() % 256; /* eigene ID zufaellig erzeugen */
|
|
cirpoi->l4try = 0; /* noch keine Sende-Versuche gemacht*/
|
|
sconrq(); /* Connect Request senden */
|
|
l4newstate(L4SLKSUP); /* neuer Status: Verbindungsaufbau */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
void discir(void) /* Circuit aufloesen */
|
|
{
|
|
if ((cirpoi->state == L4SLKSUP) ||/* Status Connect Request oder */
|
|
(cirpoi->state == L4SDSCRQ)) /* Disconnect Request ? */
|
|
{
|
|
/* Bis jetzt wurde ein L4 Disconnect nicht nach oben gemeldet, */
|
|
/* wenn der State noch LINK SETUP oder DISCONNECT REQUEST war. */
|
|
/* Ich nehme an, das war wegen der reentranz-Problematik so */
|
|
/* geloest worden. Jetzt wird ein Flag gesetzt, das eigenliche */
|
|
/* Melden passiert in L4rest. */
|
|
cirpoi->l4flag |= L4FDIMM; /* spaeter in l4rest */
|
|
#ifdef CONL3LOCAL
|
|
/* Statusflag CONACK + CHOKE Flag senden */
|
|
/* wenn Verbindungsstatus Setup/Disc ist. */
|
|
bsycir();
|
|
#endif
|
|
}
|
|
else {
|
|
kilfra(); /* Fragmentliste loeschen */
|
|
dealml((LEHEAD *)&cirpoi->mbhdrx); /* Empfangsliste loeschen */
|
|
cirpoi->numrx = 0; /* Empfangszaehler zuruecksetzen */
|
|
cirpoi->l4flag |= L4FDSLE; /* fuer Abwurf markieren */
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* Info vom L7 an Circuit senden */
|
|
/* */
|
|
/************************************************************************/
|
|
BOOLEAN
|
|
itocir(BOOLEAN cflg, MBHEAD *mbp)
|
|
{
|
|
CIRBLK *cblk;
|
|
|
|
cblk = (CIRBLK *)mbp->l2link;
|
|
if (cblk->l4flag & L4FDIMM) /* Circuit ist abgefuellt? */
|
|
{
|
|
dealmb(mbp); /* Info abnehmen (gibt Platz) */
|
|
return (TRUE); /* Info ist weg */
|
|
}
|
|
if ( (cblk->numtx < conctl) /* noch Platz ? */
|
|
|| (cflg == TRUE)) /* oder immer senden */
|
|
{
|
|
/* Infoframe in die TX-Liste des Circuit haengen */
|
|
relink(ulink((LEHEAD *)mbp), (LEHEAD *)cblk->mbhdtx.tail);
|
|
++cblk->numtx; /* Framezaehler erhoehen */
|
|
/* Um ein Abfuellen des Knotens durch einzelne User auch im L4 zu */
|
|
/* verhindern, wird wie im L2 die max. Zahl an Frames auf 150 begrenzt */
|
|
if (cblk->numtx >= 150)
|
|
cblk->l4flag |= L4FDIMM;
|
|
/* Das morflg des Frames wird jetzt im L7 geloescht vor dem Aufruf von */
|
|
/* itocir, falls das Frame nicht von einem L4-Partner kommt. */
|
|
cblk->tranoa = ininat; /* Timeout neu aufziehen */
|
|
return(TRUE); /* ok zurueckmelden */
|
|
}
|
|
return(FALSE); /* kein Platz zurueckmelden */
|
|
}
|
|
|
|
/* Informationstransfer von Layer 4 nach Layer X */
|
|
/* Solange noch empfangene Pakete vorhanden sind, werden diese an */
|
|
/* andere Layer durch Aufruf von fmlink() uebertragen. Bei geseztem */
|
|
/* Ueberfuellungskontroll-Flag (conctl == TRUE) wird die Uebertragung */
|
|
/* abgebrochen, wenn der andere Layer keine weiteren Daten mehr auf- */
|
|
/* aufnehmen kann. */
|
|
/* Nach erfolgter Uebertragung wird die Anzahl der uebertragenen Zeichen*/
|
|
/* fuer die Statistik gezaehlt und der No-Activity-Timer neu gesetzt. */
|
|
static void clr4rx(BOOLEAN conctrl)
|
|
{
|
|
MBHEAD *mbp;
|
|
|
|
while (cirpoi->numrx != 0) {/* solange noch Frames vorhanden */
|
|
mbp = (MBHEAD *) cirpoi->mbhdrx.head; /* ein Frame holen */
|
|
mbp->l2link = (LNKBLK *) cirpoi; /*zugehoerigen Circuit merken*/
|
|
mbp->type = 4; /* User ist Circuit */
|
|
#ifdef NEW_L4
|
|
mbp->l2fflg = cirpoi->pid; /* PID einstellen */
|
|
#else
|
|
mbp->l2fflg = L2CPID; /* L4 immer PID F0 */
|
|
#endif
|
|
if (!fmlink(conctrl, mbp)) break;/* Ende bei Fehler */
|
|
--cirpoi->numrx; /* ein Frame weniger */
|
|
cirpoi->tranoa = ininat; /* Timeout neu setzen */
|
|
l4setT2(cirpoi); /* ACK_TIMER neu setzen */
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void chksts(void) /* Status des Frames auswerten */
|
|
{
|
|
UWORD frofs; /* bestaetigter Offset */
|
|
UWORD fropen; /* unbesteatigter Offset */
|
|
|
|
if ((fropen = (cirpoi->l4vs - cirpoi->l4rxvs) & 0x7F) != 0) {
|
|
/* Frames offen ? */
|
|
if ((frofs = (l4hdr3 - cirpoi->l4rxvs) & 0x7F) != 0) {
|
|
/* neu bestaetigte Frames ? */
|
|
if (frofs <= fropen) { /* Frames wurden bestaetigt */
|
|
while (frofs-- != 0) { /* solange bestaetigte Frames ueber */
|
|
dealmb((MBHEAD *)ulink((LEHEAD *)cirpoi->mbhdtx.head)); /* weg*/
|
|
cirpoi->numtx--; /* einen weniger zu senden */
|
|
if (cirpoi->l4rxvs++ == /* einen mehr bestaetigt */
|
|
cirpoi->RTTvs) /* Messframe bestaetigt? */
|
|
l4rtt(cirpoi, STOP); /* Messung auswerten */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(l4hdr4 & L4CCHOKE)) { /* Partner choked ? */
|
|
cirpoi->l4flag &= ~L4FPBUSY; /* nein, merken */
|
|
l4clrT3(cirpoi); /* Timeout kann nicht kommen */
|
|
if ((l4hdr4 & L4CNAK) && /* NAK-Flag ? */
|
|
(cirpoi->l4vs != cirpoi->l4rxvs)) { /* und noch was offen ? */
|
|
/* dann wiederholen */
|
|
if (cirpoi->RTTvs == cirpoi->l4rxvs)
|
|
l4rtt(cirpoi, CLEAR); /* bei Wiederholung RTT verwerfen */
|
|
sndfrm(cirpoi->l4rxvs, (MBHEAD *)cirpoi->mbhdtx.head);
|
|
}
|
|
}
|
|
else { /* Partner ist choked (busy) */
|
|
cirpoi->l4flag |= L4FPBUSY; /* merken */
|
|
cirpoi->l4vs = cirpoi->l4rxvs; /* keine Frames offen */
|
|
l4setT3(cirpoi, L4TBUSY); /* warten */
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void takfrm(MBHEAD *mbp) /* empfangenes Frame uebernehmen */
|
|
{
|
|
BOOLEAN more; /* morflg des zu verarbeitenden Frames */
|
|
BOOLEAN copy = FALSE; /* umkopieren noetig wegen Fragmentierung */
|
|
MBHEAD *fragmn; /* dieses Fragment wird bearbeitet */
|
|
int max; /* maximale Info-Laenge fuer Weiterleitung */
|
|
int sum = 0; /* Summe vorhandenes Fragment + neues Frame */
|
|
|
|
if (!(cirpoi->l4flag & L4FDSLE)) /* Circuit ist nicht zu beenden */
|
|
{
|
|
more = mbp->morflg;
|
|
|
|
/* Framegroesse fuer vorhandenes Fragment und neues Frame bestimmen. */
|
|
/* Da fuer das Fragment in einen neuen Buffer geschrieben wurde, ist */
|
|
/* (fragmn->mbgc == 0) und muss nicht von fragmn->mbpc subtrahiert */
|
|
/* werden. */
|
|
|
|
if ((fragmn = cirpoi->fragme) != NULL)
|
|
{
|
|
sum += fragmn->mbpc;
|
|
}
|
|
sum += mbp->mbpc - mbp->mbgc;
|
|
|
|
/* Im L7 nachfragen, welche maximale Paketlaenge verarbeitet werden */
|
|
/* kann (CCP / Hostmode / L2 -> 256 Bytes; L4 -> 236 Bytes) */
|
|
|
|
max = ptc_p_max(cirpoi, L4_USER);
|
|
|
|
/* muss umkopiert werden? */
|
|
if ((sum > max) || (fragmn != NULL) || (more))
|
|
copy = TRUE;
|
|
|
|
while (copy)
|
|
{
|
|
/* ggf. neuen Buffer fuer Arbeits-Fragment holen */
|
|
if (fragmn == NULL)
|
|
{
|
|
fragmn = (MBHEAD *) allocb(ALLOC_MBHEAD);
|
|
cirpoi->fragme = fragmn;
|
|
}
|
|
/* erstmal vermuten, dass nach diesem Fragment noch mehr folgt */
|
|
fragmn->morflg = 1;
|
|
/* aus neuem Frame ans Fragment anhaengen, bis max erreicht ist, oder */
|
|
/* neues Frame leer */
|
|
while ((fragmn->mbpc < max) && (mbp->mbpc > mbp->mbgc))
|
|
{
|
|
putchr(getchr(mbp), fragmn);
|
|
}
|
|
/* Fragment fertig - Rest berechnen */
|
|
sum -= fragmn->mbpc;
|
|
if (!sum)
|
|
{
|
|
/* neues Frame ist leer und kann weg */
|
|
dealmb(mbp);
|
|
mbp = NULL;
|
|
copy = FALSE;
|
|
/* wenn nichts mehr folgt, dies im Fragment vermerken */
|
|
if (!more)
|
|
fragmn->morflg = 0;
|
|
}
|
|
/* Fragment weiterreichen, wenn max erreicht oder wenn nix mehr kommt */
|
|
if ((fragmn->mbpc == max) || (fragmn->morflg == 0))
|
|
{
|
|
rwndmb(fragmn);
|
|
relink((LEHEAD *)fragmn, (LEHEAD *) cirpoi->mbhdrx.tail);
|
|
fragmn = NULL;
|
|
cirpoi->fragme = NULL;
|
|
++cirpoi->numrx;
|
|
}
|
|
}
|
|
|
|
if (mbp != NULL) /* Frame noch da? */
|
|
{
|
|
relink((LEHEAD *)mbp,(LEHEAD *) cirpoi->mbhdrx.tail); /* in L4-RX */
|
|
++cirpoi->numrx; /* ein Frame mehr empfangen */
|
|
}
|
|
}
|
|
else /* Verbindung soll abgeworfen werden*/
|
|
dealmb(mbp); /* Frame einfach vernichten */
|
|
++cirpoi->l4vr; /* RX-Sequenz erhoehen */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void sndfrm(int txsequ, MBHEAD *mbp) /* Frame senden */
|
|
{
|
|
char huge *next; /* Pointer auf Ende des Headers */
|
|
MBHEAD *netmhd; /* Netzwerk Header */
|
|
#ifdef __WIN32__
|
|
WORD mtu; /* MTU auf dem Sende-Port */
|
|
#else
|
|
int mtu; /* MTU auf dem Sende-Port */
|
|
#endif
|
|
#ifdef NEW_L4
|
|
if ( (mbp->type == 2) /* nur L2-User */
|
|
&& (mbp->l2fflg != cirpoi->pid) /* andere PID als bisher ? */
|
|
&& (cmpcal(cirpoi->upnod, myid))) /* nur lokale Uplinks beachten */
|
|
{
|
|
spidchg(mbp->l2fflg); /* neue PID mitteilen */
|
|
cirpoi->pid = mbp->l2fflg; /* und selber merken */
|
|
}
|
|
#endif
|
|
|
|
l4ahd2 = /* Sendesequenzzaehler in den Header*/
|
|
mbp->l4seq = txsequ; /* und im Buffer merken */
|
|
l4aopc = L4INFTRA; /* Opcode: Info */
|
|
if (mbp->morflg)
|
|
l4aopc |= L4CMORE;
|
|
ackhdr(); /* Rest des Headers erzeugen */
|
|
next = (netmhd = gennhd()) ->mbbp;/* Buffer holen und Position Opcode */
|
|
/* merken */
|
|
mtu = 256; /* NETROM immer 256 Bytes Frames */
|
|
|
|
#ifdef __WIN32__
|
|
if (splcpy(((short)(mtu - netmhd->mbpc)), netmhd, mbp)) /* Info umkopieren */
|
|
#else
|
|
if (splcpy((mtu - netmhd->mbpc), netmhd, mbp)) /* Info umkopieren */
|
|
#endif /* WIN32 */
|
|
{ /* hat nicht alles reingepasst */
|
|
++cirpoi->numtx; /* ein Frame mehr gesendet */
|
|
mbp->morflg = TRUE; /* markieren, es kommt noch mehr */
|
|
}
|
|
|
|
if (mbp->morflg)
|
|
*(next -1) |= L4CMORE; /* more Flag im Opcode setzen */
|
|
|
|
itol3(netmhd); /* Frame an L3 geben */
|
|
l4setT1(cirpoi, mbp); /* Timeout neu aufziehen */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void l4nsta(WORD frtyp) /* Statusaenderung im L4 */
|
|
{
|
|
l4newstate(L4SDSCED); /* Status: Disconnected */
|
|
clrcir(); /* Eintrag zuruecksetzen */
|
|
dealml((LEHEAD *) &cirpoi->mbhdrx); /* Empfangsliste loeschen */
|
|
dealml((LEHEAD *) &cirpoi->mbhdtx); /* Sendeliste loeschen */
|
|
cirpoi->numrx = /* Zaehler zuruecksetzen */
|
|
cirpoi->numtx = 0;
|
|
l2tol7(frtyp, cirpoi, L4_USER);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void endcir(void) /* Circuit aufloesen */
|
|
{
|
|
clrcir(); /* Eintrag in CIRTAB loeschen */
|
|
cirpoi->l4try = 0; /* Versuche zuruecksetzen */
|
|
sdisrq(); /* Abwurf einleiten */
|
|
l4newstate(L4SDSCRQ); /* neuer Status: DISC-REQ gegeben */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void clrcir(void) /* Eintrag in CIRTAB loeschen */
|
|
{
|
|
kilfra(); /* Fragmente loeschen */
|
|
dealml((LEHEAD *)&cirpoi->mbhdos); /* Messageliste dafuer auch */
|
|
#ifdef __WIN32__
|
|
cirpoi->l4rxvs = 0; /* alle Sequenzen auf 0 */
|
|
cirpoi->l4vs = 0;
|
|
cirpoi->l4vr = 0;
|
|
cirpoi->l4rs = 0; /* ACK-NAK Flag */
|
|
cirpoi->l4try = 0;
|
|
#else
|
|
cirpoi->l4rxvs = /* alle Sequenzen auf 0 */
|
|
cirpoi->l4vs =
|
|
cirpoi->l4vr =
|
|
cirpoi->l4rs = /* ACK-NAK Flag */
|
|
cirpoi->l4try =
|
|
#endif /* WIN32 */
|
|
cirpoi->ll4txNR = 0;
|
|
cirpoi->l4flag = 0; /* niemand choked, kein DISC-REQ */
|
|
cirpoi->acktim = 0;
|
|
cirpoi->SRTT = L4_IRTT; /* SRTT initialisieren */
|
|
#ifdef NEW_L4
|
|
cirpoi->pid = L2CPID; /* standardmaessig PID F0 */
|
|
#endif
|
|
l4clrT3(cirpoi); /* Timer loeschen */
|
|
l4rtt(cirpoi, CLEAR); /* RTT-Messung stoppen und verwerfen */
|
|
resptc(g_uid(cirpoi, L4_USER));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void sconrq(void) /* CON-REQ senden */
|
|
{
|
|
MBHEAD *mbp; /* Buffer fuer Frame */
|
|
char *viapoi; /* Zeiger in Vialiste */
|
|
|
|
l4pidx = cirpoi - &cirtab[0]; /* Index setzen */
|
|
l4pcid = cirpoi->ideige; /* Partner und eigener Index */
|
|
l4ahd2 = /* zwei Bytes leer */
|
|
l4ahd3 = 0;
|
|
l4aopc = L4CONREQ; /* Opcode */
|
|
#ifdef __WIN32__
|
|
putchr ((char)trawir, (mbp = gennhd())); /* Rest des Headers */
|
|
#else
|
|
putchr (trawir, (mbp = gennhd())); /* Rest des Headers */
|
|
#endif /* WIN32 */
|
|
putfid(cirpoi->upcall, mbp); /* beide Calls in das Frame */
|
|
putfid(myid, mbp);
|
|
putfid(cirpoi->upnod, mbp); /* Uplinkknoten dazu */
|
|
viapoi = cirpoi->upnodv; /* Uplink-Vialiste auch */
|
|
while (*viapoi != '\0') /* Ende Vialiste erreicht? */
|
|
{
|
|
putfid(viapoi, mbp); /* Call in Puffer */
|
|
viapoi = viapoi + L2IDLEN; /* zum naechsten Call */
|
|
}
|
|
putchr('\0', mbp); /* 0 markiert Ende Vialiste */
|
|
itol3(mbp); /* an Layer 3 liefern */
|
|
l4setT3(cirpoi, L4TCREQ); /* Timeout setzen */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void sdisrq(void) /* DISQ-REQ senden */
|
|
{
|
|
l4pidx = cirpoi->idxpar; /* Index setzen */
|
|
l4pcid = cirpoi->idpart; /* und ID */
|
|
l4ahd2 = /* 2 Bytes leer */
|
|
l4ahd3 = 0;
|
|
l4aopc = L4DISREQ; /* Opcode */
|
|
itol3(gennhd()); /* Rest des Headers und dann an Layer 3 */
|
|
l4setT3(cirpoi, L4TDREQ); /* Timeout setzen */
|
|
}
|
|
|
|
#ifdef NEW_L4
|
|
/*----------------------------------------------------------------------*/
|
|
static void spidchg(UBYTE pid) /* PID-CHG senden (ohne Timeout) */
|
|
{
|
|
l4pidx = cirpoi->idxpar; /* Index setzen */
|
|
l4pcid = cirpoi->idpart; /* und ID */
|
|
l4ahd2 = 0; /* erste Byte leer */
|
|
l4ahd3 = pid; /* zweite Byte enthaelt die PID */
|
|
l4aopc = L4PIDCHG; /* Opcode */
|
|
itol3(gennhd()); /* Rest des Headers und dann an Layer 3 */
|
|
}
|
|
#endif
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Fast L4 von DB7KG */
|
|
/*----------------------------------------------------------------------*/
|
|
static void sndack(void) /* ACK senden */
|
|
{
|
|
l4ahd2 = 0x00;
|
|
l4aopc = L4INFACK; /* Opcode */
|
|
ackhdr(); /* Rest des Headers */
|
|
itol3(gennhd()); /* schnelle Weiterleitung */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void ackhdr(void) /* ACK Header erzeugen */
|
|
{
|
|
l4pidx = cirpoi->idxpar; /* Partner Index */
|
|
l4pcid = cirpoi->idpart; /* Partner ID */
|
|
l4ahd3 = cirpoi->l4vr; /* RX-Sequenz */
|
|
|
|
cirpoi->ll4txNR = cirpoi->l4vr & 0xFF; /* Letzten ACK senden */
|
|
|
|
if (cirpoi->l4flag & L4FBUSY) /* selbst choked */
|
|
l4aopc |= L4CCHOKE; /* dann Flag setzen */
|
|
else {
|
|
if (cirpoi->l4rs == 1) { /* wird es ein NAK Header? */
|
|
l4aopc |= L4CNAK; /* dann Flag setzen */
|
|
cirpoi->l4rs = 2; /* NAK als gesendet markieren */
|
|
}
|
|
}
|
|
cirpoi->acktim = 0; /* ACK Timer ruecksetzen */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void itol3(MBHEAD *mbp) /* Info an Layer 3 */
|
|
{
|
|
mbp->l2link = NULL; /* erstmal ggf. CRASHEN */
|
|
cpyid(mbp->destcall, cirpoi->l3node);
|
|
relink((LEHEAD *)mbp, (LEHEAD *) l3txl.tail); /* nur umhaengen */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
MBHEAD *gennhd(void) /* Netzwerk Header erzeugen */
|
|
{
|
|
int i;
|
|
MBHEAD *mbp; /* Buffer fuer Info */
|
|
|
|
mbp = (MBHEAD *) allocb(ALLOC_MBHEAD); /* Buffer besorgen */
|
|
for (i = 0; i < 15; ++i) /* die ersten 15 Bytes fuer Header leer */
|
|
putchr(0, mbp);
|
|
putchr(l4pidx, mbp); /* Transport Header schreiben */
|
|
putchr(l4pcid, mbp);
|
|
putchr(l4ahd2, mbp);
|
|
putchr(l4ahd3, mbp);
|
|
putchr(l4aopc, mbp);
|
|
|
|
return(mbp); /* Buffer wird zurueckgegeben */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void kilfra(void) /* Fragmente loeschen */
|
|
{
|
|
if (cirpoi->fragme != NULL) { /* schon leer? */
|
|
dealmb(cirpoi->fragme); /* Fragment loeschen */
|
|
cirpoi->fragme = NULL; /* Eintrag loeschen */
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void l3tol4(NODE *totnod) /* Meldung L3 -> L4: Knoten, auf den despoi */
|
|
{ /* zeigt, wird aus der Nodesliste gestrichen */
|
|
CIRBLK *nxtcir;
|
|
|
|
for (cirpoi = (CIRBLK *) l4actl.head;/* alle aktiven Circuits */
|
|
cirpoi != (CIRBLK *)&l4actl.head;/* durchgehen, Eintraege suchen, */
|
|
cirpoi = nxtcir) { /* die zum Zielknoten despoi gehen */
|
|
nxtcir = (CIRBLK *) cirpoi->head; /* vorher schon merken */
|
|
if (cmpid(cirpoi->l3node, totnod->id)) /* Zielknoten wurde geloescht */
|
|
{
|
|
clr4rx(TRUE); /* empfangene Frames abliefern */
|
|
l4nsta(L4MFAILW); /* Meldung an L7: Failure (Zielknoten ist weg) */
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONL3LOCAL
|
|
/* wartenden L4 circuit bestaetigen, CONACK */
|
|
void ackcir(CIRBLK *cp)
|
|
{
|
|
MBHEAD *antwor;
|
|
|
|
/* Partner Index */
|
|
l4pidx = cp->idxpar;
|
|
/* Partner ID */
|
|
l4pcid = cp->idpart;
|
|
/* Unseren Index setzen */
|
|
l4ahd2 = (UBYTE) (cp - cirtab);
|
|
/* Unsere (Zufalls-) ID */
|
|
l4ahd3 = cp->ideige;
|
|
/* Connect Acknowledge */
|
|
l4aopc = L4CONACK;
|
|
/* endgueltige Fenstergroesse zurueck an den Partner senden */
|
|
putchr(cp->window, (antwor = gennhd()));
|
|
antwor->l2link = NULL;
|
|
/* Call hinzufuegen. */
|
|
cpyid(antwor->destcall, cp->l3node);
|
|
/* Nur umhaengen. */
|
|
relink((LEHEAD *)antwor,(LEHEAD *) l3txl.tail);
|
|
/* Verbindungstatus auf information transfer setzen. */
|
|
cp->state = L4SIXFER;
|
|
}
|
|
|
|
/* fehlgeschlagener L4 circuit bestaetigen, CONACK + CHOKE */
|
|
void bsycir(void)
|
|
{
|
|
|
|
MBHEAD *antwor;
|
|
|
|
/* Partner Index */
|
|
l4pidx = cirpoi->idxpar;
|
|
/* Partner ID */
|
|
l4pcid = cirpoi->idpart;
|
|
/* Unseren Index setzen */
|
|
l4ahd2 = (UBYTE) (cirpoi - cirtab);
|
|
/* Unsere (Zufalls-) ID */
|
|
l4ahd3 = cirpoi->ideige;
|
|
/* Connect Acknowledge */
|
|
l4aopc = L4CONACK | L4CCHOKE;
|
|
/* endgueltige Fenstergroesse zurueck an den Partner senden */
|
|
putchr(cirpoi->window, (antwor = gennhd()));
|
|
antwor->l2link = NULL;
|
|
/* Call hinzufuegen. */
|
|
cpyid(antwor->destcall, cirpoi->l3node);
|
|
/* Nur umhaengen. */
|
|
relink((LEHEAD *)antwor,(LEHEAD *) l3txl.tail);
|
|
}
|
|
#endif
|
|
|
|
#ifdef L4KILL
|
|
/******************************************************************************/
|
|
/* */
|
|
/* L4-User killen (ccpkill kann nur L2). */
|
|
/* */
|
|
/******************************************************************************/
|
|
UWORD KillL4(char *call, char *msg)
|
|
{
|
|
UWORD kill_zaehler = 0,
|
|
i;
|
|
BOOLEAN kill = 0;
|
|
|
|
/* Circuit Tabelle absuchen */
|
|
for (i = 0, cirpoi = cirtab; i < NUMCIR;++i, ++cirpoi)
|
|
{
|
|
if (cirpoi->state != L4SDSCED) /* Verbingung besteht */
|
|
{
|
|
kill = (cmpid(call, cirpoi->upcall) || /* Rufzeichenvergleich. */
|
|
cmpid(call, cirpoi->downca));
|
|
|
|
if (kill) /* Rufzeichen gefunden. */
|
|
{
|
|
if (*msg) /* Mitteilung vom Sysop. */
|
|
{
|
|
MBHEAD *mbp;
|
|
/* Buffer besorgen. */
|
|
if ((mbp = (MBHEAD *) allocb(ALLOC_MBHEAD)) != NULL)
|
|
{
|
|
putchr('\r', mbp);
|
|
|
|
#ifdef SPEECH
|
|
putprintf(mbp, speech_message(207), msg);
|
|
#else
|
|
putprintf(mbp, "\r*** Msg from Sysop: %s ***\r", msg);
|
|
#endif
|
|
rwndmb(mbp); /* Msg zurueckspulen. */
|
|
|
|
sndfrm(cirpoi->l4vs++, mbp); /* Msg senden */
|
|
dealmb(mbp); /* Buffer entsorgen. */
|
|
}
|
|
}
|
|
|
|
kill_zaehler++; /* zaehlen */
|
|
endcir(); /* Circuit aufloesen. */
|
|
}
|
|
}
|
|
}
|
|
|
|
return(kill_zaehler);
|
|
}
|
|
#endif /* L4KILL */
|
|
|
|
/* End of src/l4.c */
|