/* Link Access Procedures Balanced (LAPB) - with changes for rational * behavior over packet radio */ #include "global.h" #include "mbuf.h" #include "timer.h" #include "ax25.h" #include "lapb.h" int conok = 1; /* DG2KK */ extern struct tcb; #define NULLTCB (struct tcb *)0 /* DG2KK */ /* Process incoming frames */ lapb_input(axp,cmdrsp,bp) struct ax25_cb *axp; /* Link control structure */ char cmdrsp; /* Command/response flag */ struct mbuf *bp; /* Rest of frame, starting with ctl */ { int16 ftype(); char control; char frej = 0; /* FRMR reject reason code */ char class; /* General class (I/S/U) of frame */ int16 type; /* Specific type (I/RR/RNR/etc) of frame */ char pf; /* extracted poll/final bit */ char polled = 0; char remote[10]; /* DG2KK */ if(bp == NULLBUF || axp == NULLAX25){ free_p(bp); return; } control = pullchar(&bp); type = ftype(control); class = type & 0x3; pf = control & PF; /* Check for polls */ if(cmdrsp == COMMAND && pf) polled = YES; /* While we're disconnected, ignore all except SABM and DISC */ if(axp->state == DISCONNECTED && type != SABM && type != DISC){ if(polled) axp->response = DM; goto done; } /* Process implicit acknowledgements in all but U-frames */ if(class != U && ackours(axp,(control >> 5) & MMASK) == -1) frej = Z; /* Out of range sequence number */ if(type != I && type != FRMR && len_mbuf(bp) != 0) frej |= X|W; /* I-field not allowed */ /* Process final bit if a poll was outstanding */ if(axp->waitack && class != U && pf && cmdrsp == RESPONSE){ axp->waitack = NO; axp->retries = 0; stop_timer(&axp->t1); /* Pick up retransmission at proper point */ axp->vs -= axp->unack; axp->vs &= MMASK; axp->unack = 0; } /* Resend FRMR for all except certain U while in error state */ if(axp->state == FRAMEREJECT && class != U){ goto done; } switch(type){ case SABM: /* Initialize or reset link */ /* we arrive here when we receive a SABM directed to us. */ /* put remote callsign in 'remote' */ pax25(remote, &axp->addr.dest); /* DG2KK */ log(NULLTCB,"Connect request from %s (%d)", remote, conok); if (conok != 0 || axp->state != DISCONNECTED) { /* DG2KK */ switch(axp->state){ case DISCONNECTED: case FRAMEREJECT: case CONNECTED: /* note fall-through */ lapbstate(axp,CONNECTED);/* Resets state counters */ axp->response = UA; break; case DISCPENDING: axp->response = DM; break; case SETUP: axp->response = UA; break; } } else { polled = NO; /* DG2KK */ } break; case UA: axp->retries = 0; switch(axp->state){ case CONNECTED: lapbstate(axp,SETUP); sendctl(axp,COMMAND,SABM|PF); break; case SETUP: stop_timer(&axp->t1); lapbstate(axp,CONNECTED); break; case DISCPENDING: lapbstate(axp,DISCONNECTED); break; /* note - ignored if DISCONNECTED or in FRAMEREJECT state */ } break; case DISC: switch(axp->state){ case SETUP: lapbstate(axp,DISCONNECTED); axp->response = DM; break; case DISCPENDING: axp->response = UA; break; default: axp->response = UA; lapbstate(axp,DISCONNECTED); break; } break; case RR: axp->remotebusy = NO; break; case RNR: axp->remotebusy = YES; axp->retries = 0; start_timer(&axp->t1); /* Probe as long as necessary */ break; case REJ: /* Crank back V(s) to start of queue */ axp->vs -= axp->unack; axp->vs &= MMASK; axp->unack = 0; break; case I: if(len_mbuf(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. */ axp->response = RNR; free_p(bp); bp = NULLBUF; break; } /* Reject or ignore I-frames with receive sequence number errors */ if(((control>>1) & MMASK) != axp->vr){ if(axp->proto == V1 || !axp->rejsent){ axp->rejsent = YES; axp->response = REJ; } break; } axp->rejsent = NO; axp->vr = (axp->vr+1) & MMASK; axp->response = len_mbuf(axp->rxq) >= axp->window ? RNR : RR; procdata(axp,bp); bp = NULLBUF; break; case FRMR: if(axp->state == FRAMEREJECT || axp->state == CONNECTED){ lapbstate(axp,SETUP); sendctl(axp,COMMAND,SABM|PF); } break; case DM: switch(axp->state){ case SETUP: case DISCPENDING: lapbstate(axp,DISCONNECTED); break; default: lapbstate(axp,SETUP); sendctl(axp,COMMAND,SABM|PF); } break; default: frej |= W; break; } done: free_p(bp); /* In case anything's left */ /* Check if we have to make some sort of response to this frame */ if(frej){ /* Frame reject state, respond only with FRMR */ frmr(axp,control,frej); return; } /* If we're being polled or have a U-frame response, respond * immediately */ if(polled || (axp->response & 0x3) == U){ if(axp->response == 0) axp->response = len_mbuf(axp->rxq) >= axp->window ? RNR : RR; sendctl(axp,RESPONSE,axp->response|PF); stop_timer(&axp->t2); /* No need for delayed response */ axp->response = 0; } /* See if we can send some data, perhaps piggybacking an ack. * If successful, lapb_output will clear axp->response. */ lapb_output(axp); if(axp->response){ /* We STILL owe him an ack. Try to delay it, but if T2 is * disabled, send it right away */ if(axp->t2.start != 0){ start_timer(&axp->t2); } else { sendctl(axp,RESPONSE,axp->response); axp->response = 0; } } /* Restart timer T1 if there are still unacknowledged I-frames or * a poll outstanding. */ if(axp->state != DISCONNECTED && (axp->unack !=0 || axp->waitack)) start_timer(&axp->t1); /* Start timer T3 (the idle poll timer) under AX.25 V2.0 * whenever T1 is stopped, unless we are disconnected */ if(axp->state != DISCONNECTED && axp->t1.state != TIMER_RUN && axp->t3.start != 0 && axp->proto == V2) start_timer(&axp->t3); else stop_timer(&axp->t3); /* Empty the trash */ if(axp->state == DISCONNECTED) del_ax25(axp); } /* Handle incoming acknowledgements for frames we've sent. * Free frames being acknowledged. * Return -1 to cause a frame reject if number is bad, 0 otherwise */ static ackours(axp,n) struct ax25_cb *axp; char n; { struct mbuf *bp; int acked = 0; /* Count of frames acked by this ACK */ int oldest; /* Seq number of oldest unacked I-frame */ /* Free up acknowledged frames by purging frames from the I-frame * transmit queue. Start at the remote end's last reported V(r) * and keep going until we reach the new sequence number. * If we try to free a null pointer, * then we have a frame reject condition. * Stop the T1 timer if at least one frame is being acknowledged; * it will be restarted again if not all frames were acknowledged. */ oldest = (axp->vs - axp->unack) & MMASK; while(axp->unack != 0 && oldest != n){ if((bp = dequeue(&axp->txq)) == NULLBUF){ /* Acking unsent frame */ return -1; } free_p(bp); axp->unack--; acked++; stop_timer(&axp->t1); axp->retries = 0; oldest = (oldest + 1) & MMASK; } /* If user has set a transmit upcall, indicate how many frames * may be queued */ if(acked != 0 && axp->t_upcall != NULLVFP) (*axp->t_upcall)(axp,axp->paclen * (axp->maxframe - axp->unack)); return 0; } /* Generate Frame Reject (FRMR) response * If reason != 0, this is the initial error frame * If reason == 0, resend the last error frame */ frmr(axp,control,reason) register struct ax25_cb *axp; char control; char reason; { struct mbuf *frmrinfo; register char *cp; if(reason != 0){ cp = axp->frmrinfo; *cp++ = control; *cp++ = axp->vr << 5 || axp->vs << 1; *cp = reason; } lapbstate(axp,FRAMEREJECT); if((frmrinfo = alloc_mbuf(3)) == NULLBUF) return; /* No memory */ frmrinfo->cnt = 3; memcpy(frmrinfo->data,axp->frmrinfo,3); sendframe(axp,RESPONSE,FRMR|(control&PF),frmrinfo); } /* Send S or U frame to currently connected station */ sendctl(axp,cmdrsp,cmd) struct ax25_cb *axp; char cmdrsp,cmd; { int16 ftype(); if((ftype(cmd) & 0x3) == S) /* Insert V(R) if S frame */ cmd |= (axp->vr << 5); sendframe(axp,cmdrsp,cmd,NULLBUF); } /* Start data transmission on link, if possible * Return number of frames sent */ int lapb_output(axp) register struct ax25_cb *axp; { register struct mbuf *bp; struct mbuf *tbp; char control; int sent = 0; int i; if(axp == NULLAX25 || axp->state != CONNECTED || axp->remotebusy) return 0; /* Dig into the send queue for the first unsent frame */ bp = axp->txq; for(i = 0; i < axp->unack; i++){ if(bp == NULLBUF) break; /* Nothing to do */ bp = bp->anext; } /* Start at first unsent I-frame, stop when either the * number of unacknowledged frames reaches the maxframe limit, * or when there are no more frames to send */ while(bp != NULLBUF && axp->unack < axp->maxframe){ control = I | (axp->vs++ << 1) | (axp->vr << 5); axp->vs &= MMASK; dup_p(&tbp,bp,0,len_mbuf(bp)); if(tbp == NULLBUF) return sent; /* Probably out of memory */ /* We're implicitly acking any data he's sent, so stop any * delayed ack */ axp->response = 0; stop_timer(&axp->t2); axp->unack++; sent++; bp = bp->anext; sendframe(axp,COMMAND,control,tbp); } return sent; } /* Set new link state * This is a major upheaval, so reset the world, * flush the I-queue. If the new state is disconnected, * also free the link control block */ lapbstate(axp,s) struct ax25_cb *axp; int s; { int oldstate; oldstate = axp->state; axp->state = s; axp->rejsent = NO; axp->remotebusy = NO; axp->waitack = NO; axp->unack = axp->vr = axp->vs = 0; axp->retries = 0; if(s == DISCONNECTED){ stop_timer(&axp->t1); stop_timer(&axp->t2); stop_timer(&axp->t3); free_q(&axp->txq); } if(axp->s_upcall != NULLVFP) (*axp->s_upcall)(axp,oldstate,axp->state); } /* Process a valid incoming I frame */ static procdata(axp,bp) struct ax25_cb *axp; struct mbuf *bp; { char pid; void ip_route(),nr_route(); /* DG2KK */ /* Extract level 3 PID */ if(pullup(&bp,&pid,1) != 1) return; /* No PID */ switch(pid & (PID_FIRST|PID_LAST)){ case PID_FIRST: /* "Shouldn't happen", but flush any accumulated frags */ free_p(axp->rxasm); axp->rxasm = NULLBUF; case 0: /* Note fall-thru */ /* Beginning or middle of message, just accumulate */ append(&axp->rxasm,bp); return; case PID_LAST: /* Last frame of multi-frame message; extract it */ append(&axp->rxasm,bp); bp = axp->rxasm; axp->rxasm = NULLBUF; break; case PID_FIRST|PID_LAST: /* Do nothing with reassembly queue, allowing single-frame * messages to be interspersed with fragments of multi-frame * messages */ break; } /* Last frame in sequence; kick entire message upstairs */ switch(pid & PID_PID){ case PID_IP: /* DoD Internet Protocol */ ip_route(bp,0); break; case PID_NO_L3: /* Enqueue for application */ append(&axp->rxq,bp); if(axp->r_upcall != NULLVFP) (*axp->r_upcall)(axp,len_mbuf(axp->rxq)); break; case PID_NETROM: /* DG2KK: from netrom version */ nr_route(bp); break; default: /* Note: ARP is invalid here */ free_p(bp); break; } }