⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lapb.c

📁 ppp协议实现源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * This code shows a simple LAP-B implementation for use with PPP * Numbered Mode (RFC 1663).  It assumes a system interface like the * ahdlc.c module provided with this example. * * It is also assumed that packets sent and received pass through * callback functions and are simple buffers.  Buffers are acquired * through "buffer_fetch" and released back to the system by a * "buffer_release" function. * * The user of this module is provided a simple queue interface using * lapp_send() and lapb_receive(). * * This code may be used for any purpose as long as the author's * copyright is cited in any source code distributed.  This code is * also available electronically from the author's web site. * * http://people.ne.mediaone.net/carlson/ppp * * http://www.workingcode.com/ppp * * Copyright 1999 by James Carlson and Working Code. */#include <stdlib.h>#include <assert.h>#include "sysdep.h"#include "lapb.h"/* * Theoretically, at least, the operation mode (basic, extended, or * super) could be negotiated and could be different in each direction * as could be the window size.  This is not done in standard LAP-B. * This code optionally supports both features. */#ifdef DEBUG_LAPB#include <stdio.h>#define DBG(x)	printf x#else#define DBG(x)	/* */#endifenum lapb_phase {    phQueue, phQueueDrain, phInitial, phData, phDisconnecting,    phDisconnected};#ifdef DEBUG_LAPBstatic const char *phstring[] = {    "queue", "qdrain", "initial", "data", "disconnecting",    "disconnected",};#endif#define DEF_T1PERIOD	1000#define DEF_T2MAX	5/* Retransmission and reassembly queues */struct lapb_queue {    octet *buffer;    uint16 length;    uint16 qflags;};#define QF_SENDING	0x0001		/* Frame is being sent */#define QF_ACKED	0x0002		/* Frame has been acked */#define QF_SEND		0x0004		/* Needs to be sent */struct lapb_state {    void *userstate;    void (*rcvnonempty)(void *userstate);    void (*xmthasroom)(void *userstate);    enum lapb_phase phase;    int t1period;		/* Length of timer T1 in milliseconds */    uint16 t2max,t2ctr;		/* T2; max T1 timeouts */    octet mode;			/* 0=basic, 1=extended, 2=super */    octet cmdaddr;		/* Address to use in sending commands */    octet crabit;		/* Bit for sending responses or rcv command */    octet flags;		/* Configuration flags */    uint16 vs,vr;		/* Send/receive state variables */    uint16 lastnr;		/* Last received NR value (ack number) */    uint16 txqsize;		/* Transmit sequence number modulus */    uint16 rxqsize;		/* Receive sequence number modulus */    uint16 txkval;		/* Window size "k" for transmit */    uint16 rxkval;		/* Window size "k" for receive */    uint16 tsending;		/* Item currently being sent */    uint16 tnext;		/* Next item to send */    uint16 rtail,rhead;		/* Queue pointers (modulo k) */    uint16 ttail,thead;		/* Queue pointers (modulo k) */    uint16 statflag;		/* Status code transmission flags */    octet *discarding;		/* Buffer to discard */    struct lapb_queue txpend;	/* Pending transmit */    struct lapb_queue *rxq;	/* Receive queue (length k+1) */    struct lapb_queue *txq;	/* Transmit queue (length k+1) */};/* Test if given NS value is inside the window. */#define WINDOWOFFS(ns,vr,mod)	(((ns)+(mod)-(vr))%(mod))#define INWINDOW(ns,vr,mod,k)	(WINDOWOFFS(ns,vr,mod) < (k))#define ALLSET(fl,bits)	(((fl)&(bits)) == (bits))/* * Command and response definitions.  CRU_SABM is for basic (mod 8) * mode only, CRU_SABME is for extended (mod 128) mode only, and * CRU_SM is for super (mod 32768) mode only.  CRU_SREJ is not used * with basic mode.  All other commands and responses are always * valid. */#define CRI_I		0x00	/* Information; command only */#define CRS_RR		0x01	/* Receive Ready */#define CRS_RNR		0x05	/* Receive Not Ready */#define CRS_REJ		0x09	/* Reject */#define CRS_SREJ	0x0D	/* Selective Reject; response only */#define CRU_SABM	0x2F	/* Set Asynchronous Balanced Mode; cmd only */#define CRU_SABME	0x6F	/* Set Asynchronous Balanced Mode Extended */#define CRU_SM		0xC3	/* Set Mode; command only */#define CRU_DISC	0x43	/* Disconnect; command only */#define CRU_DM		0x0F	/* Disconnect Mode; response only */#define CRU_UA		0x63	/* Unnumbered Acknowledgement; response only */#define CRU_FRMR	0x87	/* Frame Reject -- response only */#define IS_I_TYPE(x)	(((x)&1) == 0)#define IS_S_TYPE(x)	(((x)&3) == 1)#define IS_U_TYPE(x)	(((x)&3) == 3)#define ITYPE_MASK	0x01#define STYPE_MASK	0x0F#define UTYPE_MASK	0xEF/* Internal flags */#define LBF_PEERREADY	0x10	/* Peer is ready for receive */#define LBF_IMREADY	0x20	/* I'm ready to receive (internal) */#define LBF_PERMISSIVE	0x40	/* Permissive set-mode operation */#define LBF_USERREADY	0x80	/* User is ready *//* Flags for statflag */#define STF_NEEDSTATUS	0x0001	/* Need to transmit status frame */#define STF_PFBIT	0x0002	/* Status frame should have PF bit set */#define STF_XMITRUN	0x0004	/* Transmit is running */#define STF_NEEDUA	0x0008	/* Need to transmit UA frame */#define STF_UAFBIT	0x0010	/* UA frame should have F bit set */#define STF_NEEDSABM	0x0020	/* Need to send SABM now */#define STF_XMITFULL	0x0040	/* User filled transmit queue */#define STF_NEEDREJ	0x0080	/* Sequence number error detected */#define STF_NEEDVR	0x0100	/* Need to give new VR number to peer */#define STF_T1RUNNING	0x0200	/* Timer T1 is running */#define STF_NEEDDM	0x0400	/* Need to send DM */#define STF_NEEDDISC	0x0800	/* Need to send DISC */static int mode_modulus[] = { 8, 128, 32768, 32768 };static void t1handler(void *state);static voidinit_values(struct lapb_state *ls, struct lapb_queue *qp,	    int mode, int txkval, int rxkval, int flags){    memset(ls,'\0',sizeof(*ls));    memset(qp,'\0',(txkval+rxkval+2)*sizeof(struct lapb_queue));    ls->rxq = qp;    ls->txq = qp+rxkval+1;    if (mode == MODE_PERMISSIVE)	ls->mode = MODE_SUPER, flags |= LBF_PERMISSIVE;    else	ls->mode = mode;    if (mode == MODE_SUPER)	flags |= LBF_TXSREJ | LBF_RXSREJ;    else if (mode == MODE_BASIC)	flags &= ~LBF_TXSREJ & ~LBF_RXSREJ;    if (flags & LBF_MULTILINK) {	ls->cmdaddr = 0x0F;	ls->crabit = 0x08;    } else {	ls->cmdaddr = 0x03;	ls->crabit = 0x02;    }    ls->flags = flags | LBF_PEERREADY | LBF_IMREADY | LBF_USERREADY;    if (flags & LBF_ISDTE)	ls->cmdaddr ^= ls->crabit;    ls->phase = phQueue;    ls->txkval = txkval;    ls->rxkval = rxkval;    ls->txqsize = ls->rxqsize = mode_modulus[mode];}/* * Create a LAP-B session in plain queue mode.  Returns a state * structure pointer. */void *lapb_create(int mode, int txkval, int rxkval, int t1period, int t2max,	    int flags){    struct lapb_state *ls;    int len;    struct lapb_queue *qp;    if (mode < MODE_BASIC || mode > MODE_PERMISSIVE)	return NULL;    len = mode_modulus[mode];    if (txkval >= len || rxkval >= len)	return NULL;    if (txkval <= 0)	txkval = 1;    if (rxkval <= 0)	rxkval = 1;    ls = (struct lapb_state *)malloc(sizeof(*ls));    if (ls == NULL)	return NULL;    len = (txkval+rxkval+2)*sizeof(struct lapb_queue);    qp = (struct lapb_queue *)malloc(len);    if (qp == NULL) {	free(ls);	return NULL;    }    init_values(ls,qp,mode,txkval,rxkval,flags);    if (t1period <= 0)	t1period = DEF_T1PERIOD;    ls->t1period = t1period;    if (t2max <= 0)	t2max = DEF_T2MAX;    ls->t2max = t2max;    return (void *)ls;}static voidfree_all_packets(struct lapb_state *ls){    int idx;    DBG(("freeing all packets on state %p\n",ls));    if (!(ls->txpend.qflags & QF_SENDING) && ls->txpend.buffer != NULL) {	buffer_release(ls->txpend.buffer);	ls->txpend.buffer = NULL;	ls->txpend.qflags = 0;    }    /* Note that "k" in the LAP-B spec is modulus - 1, thus use <= here. */    for (idx = 0; idx <= ls->txkval; idx++) {	if (ls->txq[idx].qflags & QF_SENDING) {	    ls->txpend = ls->txq[idx];	    DBG(("moved packet from TX queue to pending.\n"));	} else if (ls->txq[idx].buffer != NULL)	    buffer_release(ls->txq[idx].buffer);	ls->txq[idx].buffer = NULL;	ls->txq[idx].qflags = 0;    }    for (idx = 0; idx <= ls->rxkval; idx++)	if (ls->rxq[idx].buffer != NULL) {	    buffer_release(ls->rxq[idx].buffer);	    ls->rxq[idx].buffer = NULL;	    ls->rxq[idx].qflags = 0;	}    ls->rtail = ls->rhead = 0;    ls->ttail = ls->thead = 0;}voidlapb_destroy(void *statep){    struct lapb_state *ls = (struct lapb_state *)statep;    DBG(("destroying state structure %p\n",ls));    if (ls != NULL) {	free_all_packets(ls);	free(ls->rxq);	free(ls);    }}voidlapb_handlers(void *statep, void *userstate,	      void (*rcvnonempty)(void *userstate),	      void (*xmthasroom)(void *userstate)){    struct lapb_state *ls = (struct lapb_state *)statep;    DBG(("set handlers on %p\n",ls));    ls->userstate = userstate;    ls->rcvnonempty = rcvnonempty;    ls->xmthasroom = xmthasroom;}/* * This is called when we need an upcall from the HDLC driver below * us.  As long as he's been told, we don't need to tell him again. */static voidneed_transmit(struct lapb_state *ls, int flags){    ls->statflag |= flags;    if (!(ls->statflag & STF_XMITRUN)) {	ls->statflag |= STF_XMITRUN;	enable_transmit();    }}/* * Handle received NR value.  As with TCP this acks all transmitted * frames up through NR-1. */static voidhandle_nr(struct lapb_state *ls, int nr){    int lastnr,tptr,tptrn,tnext;    /* Check for duplicate */    lastnr = ls->lastnr;    if (lastnr == nr)	return;    /* Cancel T1 timeout */    if (ls->statflag & STF_T1RUNNING) {	ls->t2ctr = 0;	ls->statflag &= ~STF_T1RUNNING;	untimeout(t1handler,(void *)ls);    }    ls->lastnr = nr;    DBG(("handle nr %d on %p; lastnr %d\n",nr,ls,lastnr));    tptr = ls->ttail;    tnext = ls->tnext;    /* Loop through acked buffers and free them. */    while (lastnr != nr) {	/* Special handling in case HDLC is busy with this frame. */	if (ls->txq[tptr].qflags & QF_SENDING) {	    DBG(("buffer %p in transit marked as acked; queue position %d\n",		 ls->txq[tptr].buffer,tptr));	    ls->discarding = ls->txq[tptr].buffer;	} else if (ls->txq[tptr].buffer != NULL) {	    DBG(("freeing buffer on ack; queue position %d\n",tptr));	    buffer_release(ls->txq[tptr].buffer);	}	ls->txq[tptr].buffer = NULL;	ls->txq[tptr].qflags = 0;	if ((tptrn = tptr+1) > ls->txkval)	    tptrn = 0;	if (tptr == tnext)	    tnext = tptrn;	tptr = tptrn;	lastnr = (lastnr + 1) % ls->txqsize;    }    ls->tnext = tnext;    /*     * If we now have more transmit room and the sender above us had     * stopped because we ran out of room, then give him an upcall     * to tell him that he can start again.     */    if (tptr != ls->ttail) {	ls->ttail = tptr;	if (ls->statflag & STF_XMITFULL) {	    ls->statflag &= ~STF_XMITFULL;	    if (ls->phase == phData && ls->xmthasroom != NULL)		(*ls->xmthasroom)(ls->userstate);	    else		DBG(("no room signaled; phase %s\n",phstring[ls->phase]));	}    }}/* * Mark given sequence numbers in transmit queue for retransmission. * * This is intentionally not highly optimized. */static voidmark_retransmit(struct lapb_state *ls, int nrstart, int nrend){    int tptr,tnext,lastnr;    /* Loop through packets, find matches, and retransmit those */    lastnr = ls->lastnr;    tnext = ls->tnext;    tptr = ls->ttail;    while (tptr != ls->thead) {	if (tptr == tnext)	    tnext = -1;	if (nrstart < 0 || lastnr == nrstart) {	    if (nrstart >= 0 && tnext >= 0)		ls->tnext = tptr;	    nrstart = -1;	    if (ls->txq[tptr].buffer == NULL)		DBG(("ignoring retransmit request of unsent NS %d\n",lastnr));	    else {		DBG(("marked retransmit of NS %d\n",lastnr));		ls->txq[tptr].qflags |= QF_SEND;		need_transmit(ls,0);	    }	}	if (lastnr == nrend)	    break;	if (++tptr > ls->txkval)	    tptr = 0;	if (++lastnr >= ls->txqsize)	    lastnr = 0;    }}/* * This is called by the operating system dependent timer library. * It handles a time-out on T1. */static voidt1handler(void *state){    struct lapb_state *ls = (struct lapb_state *)state;    int ptr;    DBG(("T1 timeout on %p\n",ls));    ls->statflag &= ~STF_T1RUNNING;    if (++ls->t2ctr >= ls->t2max) {	ls->phase = phDisconnected;	DBG(("T2 expired; disconnected.\n"));    }    switch (ls->phase) {    case phInitial:	DBG(("timeout requesting SABM\n"));	need_transmit(ls,STF_NEEDSABM);	break;    case phData:	/* Retransmit on timeout. */	for (ptr = ls->rtail; ptr != ls->rhead;) {	    if (ls->rxq[ptr].buffer == NULL) {		DBG(("timeout requesting REJ/SREJ\n"));		need_transmit(ls,STF_NEEDREJ);		break;	    }	    if (++ptr > ls->rxkval)		ptr = 0;	}	if (ls->thead != ls->ttail) {	    ptr = (ls->lastnr+ls->tnext-ls->ttail+2*ls->txqsize-1)%ls->txqsize;	    DBG(("timeout requesting I retransmit of NS %d\n",ptr));	    mark_retransmit(ls,ptr,ptr);	}	break;    case phDisconnecting:	need_transmit(ls,STF_NEEDDM);	DBG(("timeout requesting DM\n"));	break;    default:	return;    }}/* * The application should call this routine if it wishes to block or * unblock input.  The current state (0 for blocked, 1 for unblocked) * is returned.  If the flag is <0, then the current state is not * modified. */intlapb_ready_state(void *statep, int flag){    struct lapb_state *ls = (struct lapb_state *)statep;    if (flag == 0)	ls->flags &= ~LBF_USERREADY;    else if (flag > 0)	ls->flags |= LBF_USERREADY;    return (ls->flags & LBF_USERREADY) ? 1 : 0;}/* * This routine is called from the function that receives data from * the HDLC receive queue.  It processes the data according to the * LAP-B protocol and sends output frames to the tx_queue for output * processing by the HDLC transmit routines. */voidlapb_input(void *statep, octet *buffer, int count){    struct lapb_state *ls = (struct lapb_state *)statep;    int iscmd,ns,nr,crcode,pfbit,errbits,ilen,woffs;    octet *bp;    if (ls == NULL || buffer == NULL || count < 2) {	/* Error; missing address and control */	if (buffer != NULL)	    buffer_release(buffer);	return;    }    if (ls->phase == phQueue)	goto simple_queue;    /* First examine the address */    if (buffer[0] == ls->cmdaddr) {	/* This is a response from the peer */	iscmd = 0;    } else if (buffer[0] == (ls->cmdaddr^ls->crabit)) {	/* This is a command from the peer */	iscmd = 1;    } else if (buffer[0] == 0xFF && buffer[1] == 0x03) {	/* This is a non-LAP-B PPP frame; peer may have restarted. */	if (ls->phase != phQueue) {	    ls->phase = phQueue;	    free_all_packets(ls);	}    simple_queue:	DBG(("simple queue on %p\n",ls));	woffs = ls->rhead;	nr = woffs + 1;	if (nr > ls->rxkval)	    nr = 0;	if (nr == ls->rtail)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -