nr4.c
资源名称:export.zip [点击查看]
上传用户:hepax88
上传日期:2007-01-03
资源大小:1101k
文件大小:21k
源码类别:
TCP/IP协议栈
开发平台:
Visual C++
- /* net/rom level 4 (transport) protocol implementation
- * Copyright 1989 by Daniel M. Frank, W9NK. Permission granted for
- * non-commercial distribution only.
- * Ported to NOS by SM0RGV, 890525.
- */
- #include <stdio.h>
- #include "global.h"
- #include "mbuf.h"
- #include "timer.h"
- #include "ax25.h"
- #include "lapb.h"
- #include "netrom.h"
- #include "nr4.h"
- #include <ctype.h>
- #undef NR4DEBUG
- /* Globals: */
- /* The circuit table */
- struct nr4circp Nr4circuits[NR4MAXCIRC];
- /* Various limits */
- unsigned short Nr4window = 4; /* Max window to negotiate */
- unsigned short Nr4retries = 10; /* Max retries */
- unsigned short Nr4qlimit = 2048; /* Max bytes on receive queue */
- /* Timers */
- int32 Nr4irtt = 15000; /* Initial round trip time */
- int32 Nr4acktime = 3000; /* ACK delay timer */
- int32 Nr4choketime = 180000; /* CHOKEd state timeout */
- static void nr4ackours(struct nr4cb *, unsigned, int);
- static void nr4choke(struct nr4cb *);
- static void nr4gotnak(struct nr4cb *, unsigned);
- static void nr4rframe(struct nr4cb *, unsigned, struct mbuf **);
- /* This function is called when a net/rom layer four frame */
- /* is discovered inside a datagram addressed to us */
- void
- nr4input(hdr,bpp)
- struct nr4hdr *hdr;
- struct mbuf **bpp;
- {
- struct nr4hdr rhdr;
- struct nr4cb *cb, *cb2;
- int op;
- unsigned window;
- int acceptc; /* indicates that connection should be accepted */
- int newconn; /* indicates that this is a new incoming */
- /* connection. You'll see. */
- int gotchoke; /* The choke flag was set in this packet */
- int i;
- op = hdr->opcode & NR4OPCODE; /* Mask off flags */
- if(op == NR4OPCONRQ){ /* process connect request first */
- acceptc = 1;
- newconn = 0;
- /* These fields are sent regardless of success */
- rhdr.yourindex = hdr->u.conreq.myindex;
- rhdr.yourid = hdr->u.conreq.myid;
- /* Check to see if we have already received a connect */
- /* request for this circuit. */
- if((cb = match_n4circ(hdr->u.conreq.myindex,
- hdr->u.conreq.myid,hdr->u.conreq.user,hdr->u.conreq.node))
- == NULL){ /* No existing circuit if NULL */
- /* Try to get a new circuit */
- if((cb = new_n4circ()) == NULL)
- acceptc = 0;
- /* See if we have any listening sockets */
- for(i = 0; i < NR4MAXCIRC; i++){
- if((cb2 = Nr4circuits[i].ccb) == NULL)
- continue;/* not an open circuit */
- if(cb2->state == NR4STLISTEN)
- /* A listener was found */
- break;
- }
- if(i == NR4MAXCIRC){ /* We are refusing connects */
- acceptc = 0;
- free_n4circ(cb);
- }
- if(acceptc){
- /* Load the listeners settings */
- cb->clone = cb2->clone;
- cb->user = cb2->user;
- cb->t_upcall = cb2->t_upcall;
- cb->s_upcall = cb2->s_upcall;
- cb->r_upcall = cb2->r_upcall;
- ASSIGN(cb->local,cb2->local);
- /* Window is set to min of the offered
- * and local windows
- */
- window = hdr->u.conreq.window > Nr4window ?
- Nr4window : hdr->u.conreq.window;
- if(init_nr4window(cb, window) == -1){
- free_n4circ(cb);
- acceptc = 0;
- } else {
- /* Set up control block */
- cb->yournum = hdr->u.conreq.myindex;
- cb->yourid = hdr->u.conreq.myid;
- memcpy(cb->remote.user,
- hdr->u.conreq.user,AXALEN);
- memcpy(cb->remote.node,
- hdr->u.conreq.node,AXALEN);
- /* Default round trip time */
- cb->srtt = Nr4irtt;
- /* set up timers, window pointers */
- nr4defaults(cb);
- cb->state = NR4STDISC;
- newconn = 1;
- } /* End if window successfully allocated */
- } /* End if new circuit available */
- } /* End if no existing circuit matching parameters */
- /* Now set up response */
- if(!acceptc){
- rhdr.opcode = NR4OPCONAK | NR4CHOKE;/* choke means reject */
- rhdr.u.conack.myindex = 0;
- rhdr.u.conack.myid = 0;
- rhdr.u.conack.window = 0;
- } else {
- rhdr.opcode = NR4OPCONAK;
- rhdr.u.conack.myindex = cb->mynum;
- rhdr.u.conack.myid = cb->myid;
- rhdr.u.conack.window = cb->window;
- }
- nr4sframe(hdr->u.conreq.node, &rhdr, NULL);
- /* Why, you ask, do we wait until now for the state change
- * upcall? Well, it's like this: if the state change triggers
- * something like the mailbox to send its banner, the banner
- * would have gone out *before* the conn ack if we'd done this
- * in the code above. This is what happens when you don't plan
- * too well. Learn from my mistakes :-)
- */
- if(newconn)
- nr4state(cb, NR4STCON);/* connected (no 3-way handshake) */
- free_p(bpp);
- return;
- } /* end connect request code */
- /* validate circuit number */
- if((cb = get_n4circ(hdr->yourindex, hdr->yourid)) == NULL){
- free_p(bpp);
- return;
- }
- /* Check for choke flag */
- if(hdr->opcode & NR4CHOKE)
- gotchoke = 1;
- else
- gotchoke = 0;
- /* Here's where the interesting stuff gets done */
- switch(cb->state){
- case NR4STCPEND:
- switch(op){
- case NR4OPCONAK:
- /* Save the round trip time for later use */
- i = dur_timer(&cb->tcd) - read_timer(&cb->tcd);
- stop_timer(&cb->tcd);
- if(gotchoke){ /* connect rejected */
- cb->dreason = NR4RREFUSED;
- nr4state(cb, NR4STDISC);
- break;
- }
- cb->yournum = hdr->u.conack.myindex;
- cb->yourid = hdr->u.conack.myid;
- window = hdr->u.conack.window > Nr4window ?
- Nr4window : hdr->u.conack.window;
- if(init_nr4window(cb, window) == -1){
- cb->dreason = NR4RRESET;
- nr4state(cb, NR4STDISC);
- } else {
- nr4defaults(cb); /* set up timers, window pointers */
- if(cb->cdtries == 1) /* No retries */
- /* Use measured rtt */
- cb->srtt = i;
- else
- /* else use default */
- cb->srtt = Nr4irtt;
- nr4state(cb, NR4STCON);
- nr4output(cb); /* start sending anything on the txq */
- }
- break;
- default:
- /* We can't respond to anything else without
- * Their ID and index
- */
- free_p(bpp);
- return;
- }
- break;
- case NR4STCON:
- switch(op){
- case NR4OPDISRQ:
- /* format reply packet */
- rhdr.opcode = NR4OPDISAK;
- rhdr.yourindex = cb->yournum;
- rhdr.yourid = cb->yourid;
- nr4sframe(cb->remote.node,&rhdr,NULL);
- cb->dreason = NR4RREMOTE;
- nr4state(cb, NR4STDISC);
- break;
- case NR4OPINFO:
- /* Do receive frame processing */
- nr4rframe(cb, hdr->u.info.txseq, bpp);
- /* Reset the choke flag if no longer choked. Processing
- * the ACK will kick things off again.
- */
- if(cb->choked && !gotchoke){
- stop_timer(&cb->tchoke);
- cb->choked = 0;
- }
- /* We delay processing the receive sequence number until
- * now, because the ACK might pull more off the txq and send
- * it, and we want the implied ACK in those frames to be right
- *
- * Only process NAKs if the choke flag is off. It appears
- * that NAKs should never be sent with choke on, by the way,
- * but you never know, considering that there is no official
- * standard for this protocol
- */
- if(hdr->opcode & NR4NAK && !gotchoke)
- nr4gotnak(cb, hdr->u.info.rxseq);
- /* We always do ACK processing, too, since the NAK of one
- * packet may be the implied ACK of another. The gotchoke
- * flag is used to prevent sending any new frames, since
- * we are just going to purge them next anyway if this is
- * the first time we've seen the choke flag. If we are
- * already choked, this call will return immediately.
- */
- nr4ackours(cb, hdr->u.info.rxseq, gotchoke);
- /* If we haven't seen the choke flag before, purge the
- * send window and set the timer and the flag.
- */
- if(!cb->choked && gotchoke)
- nr4choke(cb);
- break;
- case NR4OPACK:
- if(cb->choked && !gotchoke){
- /* clear choke if appropriate */
- stop_timer(&cb->tchoke);
- cb->choked = 0;
- }
- if(hdr->opcode & NR4NAK && !gotchoke)
- nr4gotnak(cb, hdr->u.ack.rxseq); /* process NAKs */
- nr4ackours(cb, hdr->u.ack.rxseq, gotchoke); /* and ACKs */
- if(!cb->choked && gotchoke) /* First choke seen */
- nr4choke(cb); /* Set choke status */
- break;
- }
- break;
- case NR4STDPEND:
- switch(op){
- case NR4OPDISAK:
- cb->dreason = NR4RNORMAL;
- nr4state(cb, NR4STDISC);
- break;
- case NR4OPINFO:
- /* We can still do receive frame processing until
- * the disconnect acknowledge arrives, but we won't
- * bother to process ACKs, since we've flushed our
- * transmit buffers and queue already.
- */
- nr4rframe(cb, hdr->u.info.txseq, bpp);
- break;
- }
- } /* End switch(state) */
- }
- /* Send a net/rom layer 4 frame. *bpp should be NULL unless the frame
- * type is info.
- */
- void
- nr4sframe(
- uint8 *dest,
- struct nr4hdr *hdr,
- struct mbuf **bpp
- ){
- struct mbuf *n4b;
- if((n4b = htonnr4(hdr)) == NULL){
- free_p(bpp);
- return;
- } else {
- append(&n4b, bpp);
- nr3output(dest, &n4b);
- }
- }
- /* Receive frame processing */
- static void
- nr4rframe(
- struct nr4cb *cb,
- unsigned rxseq,
- struct mbuf **bpp
- ){
- struct nr4hdr rhdr;
- unsigned window = cb->window;
- unsigned rxbuf = rxseq % window;
- unsigned newdata = 0; /* whether to upcall */
- #ifdef NR4DEBUG
- printf("Processing received infon");
- #endif
- /* If we're choked, just reset the ACK timer to blast out
- * another CHOKE indication after the ackdelay
- */
- if(cb->qfull){
- start_timer(&cb->tack);
- return;
- }
- /* If frame is out of sequence, it is either due to a lost frame
- * or a retransmission of one seen earlier. We do not want to NAK
- * the latter, as the far end would see this as a requirement to
- * retransmit the expected frame, which is probably already in the
- * pipeline. This in turn would cause another out-of-sequence
- * condition, another NAK, and the process would repeat indefinitely.
- * Therefore, if the frame is out-of-sequence, but within the last
- * 'n' frames by sequence number ('n' being the window size), just
- * accept it and discard it. Else, NAK it if we haven't already.
- * (Modified by Rob Stampfli, kd8wk, 9 Jan 1990)
- */
- if(rxseq != cb->rxpected && !cb->naksent){
- #ifdef NR4DEBUG
- printf("Frame out of sequence -- expected %u, got %u.n",
- cb->rxpected, rxseq);
- #endif
- if(nr4between(cb->rxpected,
- (rxseq + window) & NR4SEQMASK, cb->rxpastwin))
- /* just a repeat of old frame -- queue ack for
- * expected frame
- */
- start_timer(&cb->tack);
- else { /* really bogus -- a NAKable frame */
- rhdr.opcode = NR4OPACK | NR4NAK;
- rhdr.yourindex = cb->yournum;
- rhdr.yourid = cb->yourid;
- rhdr.u.ack.rxseq = cb->rxpected;
- nr4sframe(cb->remote.node,&rhdr,NULL);
- /* Now make sure we don't send any more of these until
- * we see some good data. Otherwise full window
- * retransmissions would result in a flurry of NAKs
- */
- cb->naksent = 1;
- }
- }
- /* If this is a new frame, within the window, buffer it,
- * then see what we can deliver
- */
- if(nr4between(cb->rxpected,rxseq,cb->rxpastwin)
- && !cb->rxbufs[rxbuf].occupied){
- #ifdef NR4DEBUG
- printf("Frame within windown");
- #endif
- cb->rxbufs[rxbuf].occupied = 1;
- cb->rxbufs[rxbuf].data = *bpp;
- *bpp = NULL;
- for(rxbuf = cb->rxpected % window; cb->rxbufs[rxbuf].occupied;
- rxbuf = cb->rxpected % window){
- #ifdef NR4DEBUG
- printf("Removing frame from buffer %dn", rxbuf);
- #endif
- newdata = 1;
- cb->rxbufs[rxbuf].occupied = 0;
- append(&cb->rxq,&cb->rxbufs[rxbuf].data);
- cb->rxbufs[rxbuf].data = NULL;
- cb->rxpected = (cb->rxpected + 1) & NR4SEQMASK;
- cb->rxpastwin = (cb->rxpastwin + 1) & NR4SEQMASK;
- }
- if(newdata){
- cb->naksent = 0; /* OK to send NAKs again */
- if(cb->r_upcall != NULL)
- (*cb->r_upcall)(cb,len_p(cb->rxq));
- /* Now that our upcall has had a shot at the queue, */
- /* see if it's past the queue length limit. If so, */
- /* go into choked mode (i.e. flow controlled). */
- if(len_p(cb->rxq) > Nr4qlimit){
- cb->qfull = 1;
- nr4ackit((void *)cb); /* Tell `em right away */
- } else
- start_timer(&cb->tack);
- }
- } else /* It's out of the window or we've seen it already */
- free_p(bpp);
- }
- /* Send the transmit buffer whose sequence number is seq */
- void
- nr4sbuf(cb, seq)
- struct nr4cb *cb;
- unsigned seq;
- {
- struct nr4hdr hdr;
- struct mbuf *bufbp, *bp;
- unsigned bufnum = seq % cb->window;
- struct timer *t;
- /* sanity check */
- if(bufnum >= cb->window){
- #ifdef NRDEBUG
- printf("sbuf: buffer number %u beyond windown",bufnum);
- #endif
- return;
- }
- /* Stop the ACK timer, since our sending of the frame is
- * an implied ACK.
- */
- stop_timer(&cb->tack);
- /* Duplicate the mbuf, since we have to keep it around
- * until it is acknowledged
- */
- bufbp = cb->txbufs[bufnum].data;
- /* Notice that we use copy_p instead of dup_p. This is because
- * a frame can still be sitting on the AX.25 send queue when it
- * get acknowledged, and we don't want to deallocate its data
- * before it gets sent!
- */
- if((bp = copy_p(bufbp, len_p(bufbp))) == NULL){
- free_mbuf(&bp);
- return;
- }
- /* Prepare the header */
- if(cb->qfull) /* are we choked? */
- hdr.opcode = NR4OPINFO | NR4CHOKE;
- else
- hdr.opcode = NR4OPINFO;
- hdr.yourindex = cb->yournum;
- hdr.yourid = cb->yourid;
- hdr.u.info.txseq = (unsigned char)(seq & NR4SEQMASK);
- hdr.u.info.rxseq = cb->rxpected;
- /* Send the frame, then set and start the timer */
- nr4sframe(cb->remote.node, &hdr, &bp);
- t = &cb->txbufs[bufnum].tretry;
- set_timer(t, (1 << cb->blevel) * (4 * cb->mdev + cb->srtt));
- start_timer(t);
- }
- /* Check to see if any of our frames have been ACKed */
- static void
- nr4ackours(cb, seq, gotchoke)
- struct nr4cb *cb;
- unsigned seq;
- int gotchoke; /* The choke flag is set in the received frame */
- {
- unsigned txbuf;
- struct timer *t;
- /* If we are choked, there is nothing in the send window
- * by definition, so we can just return.
- */
- if(cb->choked)
- return;
- /* Adjust seq to point to the frame being ACK'd, not the one
- * beyond it, which is how it arrives.
- */
- seq = (seq - 1) & NR4SEQMASK;
- /* Free up all the ack'd frames, and adjust the round trip
- * timing stuff
- */
- while (nr4between(cb->ackxpected, seq, cb->nextosend)){
- #ifdef NR4DEBUG
- printf("Sequence # %u acknowledgedn", seq);
- #endif
- cb->nbuffered--;
- txbuf = cb->ackxpected % cb->window;
- free_mbuf(&cb->txbufs[txbuf].data);
- cb->txbufs[txbuf].data = NULL;
- cb->ackxpected = (cb->ackxpected + 1) & NR4SEQMASK;
- /* Round trip time estimation, cribbed from TCP */
- if(cb->txbufs[txbuf].retries == 0){
- /* We only sent this one once */
- int32 rtt;
- int32 abserr;
- t = &cb->txbufs[txbuf].tretry;
- /* get our rtt in msec */
- rtt = dur_timer(t) - read_timer(t);
- abserr = (rtt > cb->srtt) ? rtt - cb->srtt : cb->srtt - rtt;
- cb->srtt = (cb->srtt * 7 + rtt) >> 3;
- cb->mdev = (cb->mdev * 3 + abserr) >> 2;
- /* Reset the backoff level */
- cb->blevel = 0;
- }
- stop_timer(&cb->txbufs[txbuf].tretry);
- }
- /* Now we recalculate tmax, the maximum number of retries for
- * any frame in the window. tmax is used as a baseline to
- * determine when the window has reached a new high in retries.
- * We don't want to increment blevel for every frame that times
- * out, since that would lead to us backing off too fast when
- * all the frame timers expired at around the same time.
- */
- cb->txmax = 0;
- for(seq = cb->ackxpected;
- nr4between(cb->ackxpected, seq, cb->nextosend);
- seq = (seq + 1) & NR4SEQMASK)
- if(cb->txbufs[seq % cb->window].retries > cb->txmax)
- cb->txmax = cb->txbufs[seq % cb->window].retries;
- /* This is kind of a hack. This function is called under
- * three different conditions: either we are choked, in
- * which case we return immediately, or we are not choked,
- * in which case we proceed normally to keep the send
- * window full, or we have seen the choke flag for the first
- * time. In the last case, gotchoke is true while cb->choked
- * is false. We want to process any acknowledgments of existing
- * frames in the send window before we purge it, while at the
- * same time we don't want to take anything else off the txq
- * or send it out. So, in the third case we listed, we return
- * now since we've processed the ACK.
- */
- if(gotchoke)
- return;
- nr4output(cb); /* yank stuff off txq and send it */
- /* At this point, either the send window is full, or
- * nr4output() didn't find enough on the txq to fill it.
- * If the window is not full, then the txq must be empty,
- * and we'll make a tx upcall
- */
- if(cb->nbuffered < cb->window && cb->t_upcall != NULL)
- (*cb->t_upcall)(cb, (uint16)((cb->window - cb->nbuffered) * NR4MAXINFO));
- }
- /* If the send window is open and there are frames on the txq,
- * move as many as possible to the transmit buffers and send them.
- * Return the number of frames sent.
- */
- int
- nr4output(cb)
- struct nr4cb *cb;
- {
- int numq, i;
- struct mbuf *bp;
- struct nr4txbuf *tp;
- /* Are we in the proper state? */
- if(cb->state != NR4STCON || cb->choked)
- return 0; /* No sending if not connected */
- /* or if choked */
- /* See if the window is open */
- if(cb->nbuffered >= cb->window)
- return 0;
- numq = len_q(cb->txq);
- #ifdef NR4DEBUG
- printf("nr4output: %d packets on txqn", numq);
- #endif
- for(i = 0; i < numq; i++){
- bp = dequeue(&cb->txq);
- #ifdef NR4DEBUG
- if(len_p(bp) > NR4MAXINFO){ /* should be checked higher up */
- printf("Upper layers queued too big a buffern");
- continue;
- }
- #endif
- /* Set up and send buffer */
- tp = &cb->txbufs[cb->nextosend % cb->window];
- tp->retries = 0;
- tp->data = bp;
- nr4sbuf(cb, cb->nextosend);
- /* Update window and buffered count */
- cb->nextosend = (cb->nextosend + 1) & NR4SEQMASK;
- if(++cb->nbuffered >= cb->window)
- break;
- }
- return i;
- }
- void
- nr4state(cb, newstate)
- struct nr4cb *cb;
- int newstate;
- {
- int i;
- int oldstate = cb->state;
- cb->state = newstate;
- switch(cb->state){
- case NR4STDPEND:
- stop_timer(&cb->tchoke);
- /* When we request a disconnect, we lose the contents of
- * our transmit queue and buffers, but we retain our ability
- * to receive any packets in transit until a disconnect
- * acknowledge arrives
- */
- free_q(&cb->txq);
- for(i = 0; i < cb->window; i++){
- free_mbuf(&cb->txbufs[i].data);
- cb->txbufs[i].data = NULL;
- stop_timer(&cb->txbufs[i].tretry);
- }
- /* Tidy up stats: roll the top window pointer back
- * and reset nbuffered to reflect this. Not really
- * necessary, but leads to a bit more truth telling
- * in the status displays.
- */
- cb->nextosend = cb->ackxpected;
- cb->nbuffered = 0;
- break;
- case NR4STDISC:
- stop_timer(&cb->tchoke);
- stop_timer(&cb->tack);
- stop_timer(&cb->tcd);
- /* We don't clear the rxq, since the state change upcall
- * may pull something off of it at the last minute.
- */
- free_q(&cb->txq);
- /* The following loop will only be executed if the
- * window was set, since when the control block is
- * calloc'd the window field gets a 0 in it. This
- * protects us from dereferencing an unallocated
- * window buffer pointer
- */
- for(i = 0; i < cb->window; i++){
- free_mbuf(&cb->rxbufs[i].data);
- cb->rxbufs[i].data = NULL;
- free_mbuf(&cb->txbufs[i].data);
- cb->txbufs[i].data = NULL;
- stop_timer(&cb->txbufs[i].tretry);
- }
- break;
- }
- if(oldstate != newstate && cb->s_upcall != NULL)
- (*cb->s_upcall)(cb, oldstate, newstate);
- /* We take responsibility for deleting the circuit
- * descriptor. Don't do this anywhere else!
- */
- if(newstate == NR4STDISC)
- free_n4circ(cb);
- }
- /* Process NAKs. seq indicates the next frame expected by the
- * NAK'ing station.
- */
- static void
- nr4gotnak(cb, seq)
- struct nr4cb *cb;
- unsigned seq;
- {
- if(nr4between(cb->ackxpected, seq, cb->nextosend))
- nr4sbuf(cb, seq);
- }
- /* This is called when we first get a CHOKE indication from the
- * remote. It purges the send window and sets the choke timer.
- */
- static void
- nr4choke(cb)
- struct nr4cb *cb;
- {
- unsigned seq;
- struct mbuf *q, *bp;
- struct nr4txbuf *t;
- q = cb->txq;
- /* We purge the send window, returning the buffers to the
- * txq in the proper order.
- */
- for(seq = (cb->nextosend - 1) & NR4SEQMASK;
- nr4between(cb->ackxpected, seq, cb->nextosend);
- seq = (seq - 1) & NR4SEQMASK){
- t = &cb->txbufs[seq % cb->window];
- stop_timer(&t->tretry);
- bp = t->data;
- t->data = NULL;
- enqueue(&bp, &q); /* prepend this packet to the queue */
- q = bp;
- }
- cb->nextosend = cb->ackxpected; /* close the window */
- cb->nbuffered = 0; /* nothing in the window */
- cb->txq = q; /* Replace the txq with the one that has */
- /* the purged packets prepended */
- cb->choked = 1; /* Set the choked flag */
- start_timer(&cb->tchoke);
- }