📄 lapb.c
字号:
/* Link Access Procedures Balanced (LAPB), the upper sublayer of
* AX.25 Level 2.
*/
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "ax25.h"
#include "lapb.h"
#include "ip.h"
#include "netrom.h"
static void handleit(struct ax25_cb *axp,int pid,struct mbuf **bp);
static void procdata(struct ax25_cb *axp,struct mbuf **bp);
static int ackours(struct ax25_cb *axp,uint16 n);
static void clr_ex(struct ax25_cb *axp);
static void enq_resp(struct ax25_cb *axp);
static void inv_rex(struct ax25_cb *axp);
/* Process incoming frames */
int
lapb_input(
struct ax25_cb *axp, /* Link control structure */
int cmdrsp, /* Command/response flag */
struct mbuf **bpp /* Rest of frame, starting with ctl */
){
int control;
int class; /* General class (I/S/U) of frame */
uint16 type; /* Specific type (I/RR/RNR/etc) of frame */
char pf; /* extracted poll/final bit */
char poll = 0;
char final = 0;
uint16 nr; /* ACK number of incoming frame */
uint16 ns; /* Seq number of incoming frame */
uint16 tmp;
if(bpp == NULL || *bpp == NULL || axp == NULL){
free_p(bpp);
return -1;
}
/* Extract the various parts of the control field for easy use */
if((control = PULLCHAR(bpp)) == -1){
free_p(bpp); /* Probably not necessary */
return -1;
}
type = ftype(control);
class = type & 0x3;
pf = control & PF;
/* Check for polls and finals */
if(pf){
switch(cmdrsp){
case LAPB_COMMAND:
poll = YES;
break;
case LAPB_RESPONSE:
final = YES;
break;
}
}
/* Extract sequence numbers, if present */
switch(class){
case I:
case I+2:
ns = (control >> 1) & MMASK;
case S: /* Note fall-thru */
nr = (control >> 5) & MMASK;
break;
}
/* This section follows the SDL diagrams by K3NA fairly closely */
switch(axp->state){
case LAPB_DISCONNECTED:
switch(type){
case SABM: /* Initialize or reset link */
sendctl(axp,LAPB_RESPONSE,UA|pf); /* Always accept */
clr_ex(axp);
axp->unack = axp->vr = axp->vs = 0;
lapbstate(axp,LAPB_CONNECTED);/* Resets state counters */
axp->srt = Axirtt;
axp->mdev = 0;
set_timer(&axp->t1,2*axp->srt);
start_timer(&axp->t3);
break;
case DM: /* Ignore to avoid infinite loops */
break;
default: /* All others get DM */
if(poll)
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
}
break;
case LAPB_SETUP:
switch(type){
case SABM: /* Simultaneous open */
sendctl(axp,LAPB_RESPONSE,UA|pf);
break;
case DISC:
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
case UA: /* Connection accepted */
/* Note: xmit queue not cleared */
stop_timer(&axp->t1);
start_timer(&axp->t3);
axp->unack = axp->vr = axp->vs = 0;
lapbstate(axp,LAPB_CONNECTED);
break;
case DM: /* Connection refused */
free_q(&axp->txq);
stop_timer(&axp->t1);
axp->reason = LB_DM;
lapbstate(axp,LAPB_DISCONNECTED);
break;
default: /* All other frames ignored */
break;
}
break;
case LAPB_DISCPENDING:
switch(type){
case SABM:
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
case DISC:
sendctl(axp,LAPB_RESPONSE,UA|pf);
break;
case UA:
case DM:
stop_timer(&axp->t1);
lapbstate(axp,LAPB_DISCONNECTED);
break;
default: /* Respond with DM only to command polls */
if(poll)
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
}
break;
case LAPB_CONNECTED:
switch(type){
case SABM:
sendctl(axp,LAPB_RESPONSE,UA|pf);
clr_ex(axp);
free_q(&axp->txq);
stop_timer(&axp->t1);
start_timer(&axp->t3);
axp->unack = axp->vr = axp->vs = 0;
lapbstate(axp,LAPB_CONNECTED); /* Purge queues */
break;
case DISC:
free_q(&axp->txq);
sendctl(axp,LAPB_RESPONSE,UA|pf);
stop_timer(&axp->t1);
stop_timer(&axp->t3);
axp->reason = LB_NORMAL;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case DM:
axp->reason = LB_DM;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case UA:
est_link(axp);
lapbstate(axp,LAPB_SETUP); /* Re-establish */
break;
case FRMR:
est_link(axp);
lapbstate(axp,LAPB_SETUP); /* Re-establish link */
break;
case RR:
case RNR:
axp->flags.remotebusy = (control == RNR) ? YES : NO;
if(poll)
enq_resp(axp);
ackours(axp,nr);
break;
case REJ:
axp->flags.remotebusy = NO;
if(poll)
enq_resp(axp);
ackours(axp,nr);
stop_timer(&axp->t1);
start_timer(&axp->t3);
/* This may or may not actually invoke transmission,
* depending on whether this REJ was caused by
* our losing his prior ACK.
*/
inv_rex(axp);
break;
case I:
ackours(axp,nr); /** == -1) */
if(len_p(axp->rxq) >= axp->window){
/* Too bad he didn't listen to us; he'll
* have to resend the frame later. This
* drastic action is necessary to avoid
* deadlock.
*/
if(poll)
sendctl(axp,LAPB_RESPONSE,RNR|pf);
free_p(bpp);
break;
}
/* Reject or ignore I-frames with receive sequence number errors */
if(ns != axp->vr){
if(axp->proto == V1 || !axp->flags.rejsent){
axp->flags.rejsent = YES;
sendctl(axp,LAPB_RESPONSE,REJ | pf);
} else if(poll)
enq_resp(axp);
axp->response = 0;
break;
}
axp->flags.rejsent = NO;
axp->vr = (axp->vr+1) & MMASK;
tmp = len_p(axp->rxq) >= axp->window ? RNR : RR;
if(poll){
sendctl(axp,LAPB_RESPONSE,tmp|PF);
} else {
axp->response = tmp;
}
procdata(axp,bpp);
break;
default: /* All others ignored */
break;
}
break;
case LAPB_RECOVERY:
switch(type){
case SABM:
sendctl(axp,LAPB_RESPONSE,UA|pf);
clr_ex(axp);
stop_timer(&axp->t1);
start_timer(&axp->t3);
axp->unack = axp->vr = axp->vs = 0;
lapbstate(axp,LAPB_CONNECTED); /* Purge queues */
break;
case DISC:
free_q(&axp->txq);
sendctl(axp,LAPB_RESPONSE,UA|pf);
stop_timer(&axp->t1);
stop_timer(&axp->t3);
axp->response = UA;
axp->reason = LB_NORMAL;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case DM:
axp->reason = LB_DM;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case UA:
est_link(axp);
lapbstate(axp,LAPB_SETUP); /* Re-establish */
break;
case FRMR:
est_link(axp);
lapbstate(axp,LAPB_SETUP); /* Re-establish link */
break;
case RR:
case RNR:
axp->flags.remotebusy = (control == RNR) ? YES : NO;
if(axp->proto == V1 || final){
stop_timer(&axp->t1);
ackours(axp,nr);
if(axp->unack != 0){
inv_rex(axp);
} else {
start_timer(&axp->t3);
lapbstate(axp,LAPB_CONNECTED);
}
} else {
if(poll)
enq_resp(axp);
ackours(axp,nr);
/* Keep timer running even if all frames
* were acked, since we must see a Final
*/
if(!run_timer(&axp->t1))
start_timer(&axp->t1);
}
break;
case REJ:
axp->flags.remotebusy = NO;
/* Don't insist on a Final response from the old proto */
if(axp->proto == V1 || final){
stop_timer(&axp->t1);
ackours(axp,nr);
if(axp->unack != 0){
inv_rex(axp);
} else {
start_timer(&axp->t3);
lapbstate(axp,LAPB_CONNECTED);
}
} else {
if(poll)
enq_resp(axp);
ackours(axp,nr);
if(axp->unack != 0){
/* This is certain to trigger output */
inv_rex(axp);
}
/* A REJ that acks everything but doesn't
* have the F bit set can cause a deadlock.
* So make sure the timer is running.
*/
if(!run_timer(&axp->t1))
start_timer(&axp->t1);
}
break;
case I:
ackours(axp,nr); /** == -1) */
/* Make sure timer is running, since an I frame
* cannot satisfy a poll
*/
if(!run_timer(&axp->t1))
start_timer(&axp->t1);
if(len_p(axp->rxq) >= axp->window){
/* Too bad he didn't listen to us; he'll
* have to resend the frame later. This
* drastic action is necessary to avoid
* memory deadlock.
*/
sendctl(axp,LAPB_RESPONSE,RNR | pf);
free_p(bpp);
break;
}
/* Reject or ignore I-frames with receive sequence number errors */
if(ns != axp->vr){
if(axp->proto == V1 || !axp->flags.rejsent){
axp->flags.rejsent = YES;
sendctl(axp,LAPB_RESPONSE,REJ | pf);
} else if(poll)
enq_resp(axp);
axp->response = 0;
break;
}
axp->flags.rejsent = NO;
axp->vr = (axp->vr+1) & MMASK;
tmp = len_p(axp->rxq) >= axp->window ? RNR : RR;
if(poll){
sendctl(axp,LAPB_RESPONSE,tmp|PF);
} else {
axp->response = tmp;
}
procdata(axp,bpp);
break;
default:
break; /* Ignored */
}
break;
}
free_p(bpp); /* In case anything's left */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -