lcp.c

来自「PPPoE协议在Psos中的实现源代码」· C语言 代码 · 共 1,734 行 · 第 1/3 页

C
1,734
字号
/*
 * lcp.c - PPP Link Control Protocol.
 *
 * Copyright (c) 1989 Carnegie Mellon University.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by Carnegie Mellon University.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * TODO:
 */

#include <string.h>

#include "syslog.h"
#include "ppp.h"
#include "fsm.h"
#include "lcp.h"
#include "magic.h"
#include "chap.h"
#include "upap.h"
#include "ppp_conf.h"

#define BCOPY(p0, p1, n) memmove (p1, p0, n)

/* global vars */
fsm lcp_fsm[NUM_PPP];			/* LCP fsm structure (global)*/
lcp_options lcp_wantoptions[NUM_PPP];	/* Options that we want to request */
lcp_options lcp_gotoptions[NUM_PPP];	/* Options that peer ack'd */
lcp_options lcp_allowoptions[NUM_PPP];	/* Options we allow peer to request */
lcp_options lcp_hisoptions[NUM_PPP];	/* Options that we ack'd */
u_int32_t xmit_accm[NUM_PPP][8];		/* extended transmit ACCM */

static u_int32_t lcp_echos_pending = 0;	/* Number of outstanding echo msgs */
static u_int32_t lcp_echo_number   = 0;	/* ID number of next echo frame */
static u_int32_t lcp_echo_timer_running = 0;  /* TRUE if a timer is running */

static u_char nak_buffer[PPP_MRU];	/* where we construct a nak packet */


/*
 * Callbacks for fsm code.  (CI = Configuration Information)
 */
static void lcp_resetci __P((fsm *));	/* Reset our CI */
static int  lcp_cilen __P((fsm *));		/* Return length of our CI */
static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */
static int  lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
static int  lcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
static int  lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
static int  lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */
static void lcp_up __P((fsm *));		/* We're UP */
static void lcp_down __P((fsm *));		/* We're DOWN */
static void lcp_starting __P((fsm *));	/* We need lower layer up */
static void lcp_finished __P((fsm *));	/* We need lower layer down */
static int  lcp_extcode __P((fsm *, int, int, u_char *, int));
static void lcp_rprotrej __P((fsm *, u_char *, int));

/*
 * routines to send LCP echos to peer
 */

static void lcp_echo_lowerup __P((int));
static void lcp_echo_lowerdown __P((int));
static void LcpEchoTimeout __P((caddr_t));
static void lcp_received_echo_reply __P((fsm *, int, u_char *, int));
static void LcpSendEchoRequest __P((fsm *));
static void LcpLinkFailure __P((fsm *));
static void LcpEchoCheck __P((fsm *));

static fsm_callbacks lcp_callbacks = {	/* LCP callback routines */
    lcp_resetci,		/* Reset our Configuration Information */
    lcp_cilen,			/* Length of our Configuration Information */
    lcp_addci,			/* Add our Configuration Information */
    lcp_ackci,			/* ACK our Configuration Information */
    lcp_nakci,			/* NAK our Configuration Information */
    lcp_rejci,			/* Reject our Configuration Information */
    lcp_reqci,			/* Request peer's Configuration Information */
    lcp_up,			/* Called when fsm reaches OPENED state */
    lcp_down,			/* Called when fsm leaves OPENED state */
    lcp_starting,		/* Called when we want the lower layer up */
    lcp_finished,		/* Called when we want the lower layer down */
    NULL,			/* Called when Protocol-Reject received */
    NULL,			/* Retransmission is necessary */
    lcp_extcode,		/* Called to handle LCP-specific codes */
    "LCP"			/* String name of protocol */
};

int lcp_loopbackfail = DEFLOOPBACKFAIL;

/*
 * Length of each type of configuration option (in octets)
 */
#define CILEN_VOID	2
#define CILEN_SHORT	4	/* CILEN_VOID + sizeof(short) */
#define CILEN_CHAP	5	/* CILEN_VOID + sizeof(short) + 1 */
#define CILEN_LONG	6	/* CILEN_VOID + sizeof(long) */
#define CILEN_LQR	8	/* CILEN_VOID + sizeof(short) + sizeof(long) */

#define CODENAME(x)	((x) == CONFACK ? "ACK" : \
			 (x) == CONFNAK ? "NAK" : "REJ")

/*
 * lcp_init - Initialize LCP.
 */
void
lcp_init(unit)
    int unit;
{
    fsm *f = &lcp_fsm[unit];
    lcp_options *wo = &lcp_wantoptions[unit];
    lcp_options *ao = &lcp_allowoptions[unit];
    struct ppp_cfg *pcp = &pppcfgtab[unit];

    f->unit = unit;
    f->protocol = PPP_LCP;
    f->callbacks = &lcp_callbacks;

    fsm_init(f);

    wo->passive = ((pcp->pppmode == 1) ? 0 : 1);
    wo->silent = ((pcp->pppmode == 1) ? 0 : 1);
    wo->restart = 0;			/* Set to 1 in kernels or multi-line
					   implementations */
    wo->neg_mru = ((pcp->lcp_options & NEGMRU) ? 1 : 0);
    wo->mru = pcp->mru;         
    wo->neg_asyncmap = ((pcp->lcp_options & NEGASYNCMAP) ? 1 : 0);
    wo->asyncmap = pcp->asyncmap;
#if CHAPNEEDED
    wo->neg_chap = ((pcp->auth_options & REQCHAP) ? 1 : 0);
#else
    wo->neg_chap = 0;
#endif
#if UPAPNEEDED
    wo->neg_upap = ((pcp->auth_options & REQUPAP) ? 1 : 0);
#else
    wo->neg_upap = 0;
#endif
    wo->chap_mdtype = CHAP_DIGEST_MD5;
    wo->neg_magicnumber = ((pcp->lcp_options & NEGMAGIC) ? 1 : 0);
    wo->neg_pcompression = ((pcp->lcp_options & NEGPROTOCOMP) ? 1 : 0);
    wo->neg_accompression = ((pcp->lcp_options & NEGACCOMP) ? 1 : 0);
    wo->neg_lqr = 0;			/* no LQR implementation yet */

    ao->neg_mru = ((pcp->lcp_options & NEGMRU) ? 1 : 0);
    ao->mru = MAXMRU;
    ao->neg_asyncmap = ((pcp->lcp_options & NEGASYNCMAP) ? 1 : 0);
    ao->asyncmap = 0;
#if CHAPNEEDED
    ao->neg_chap = ((pcp->auth_options & NOCHAP) ? 0 : 1); 
#else
    ao->neg_chap = 0; 
#endif
#if UPAPNEEDED
    ao->neg_upap = ((pcp->auth_options & NOUPAP) ? 0 : 1); 
#else
    ao->neg_upap = 0; 
#endif
    ao->chap_mdtype =  CHAP_DIGEST_MD5;
    ao->neg_magicnumber = ((pcp->lcp_options & NEGMAGIC) ? 1 : 0);
    ao->neg_pcompression = ((pcp->lcp_options & NEGPROTOCOMP) ? 1 : 0);
    ao->neg_accompression = ((pcp->lcp_options & NEGACCOMP) ? 1 : 0);
    ao->neg_lqr = 0;			/* no LQR implementation yet */

    memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
    xmit_accm[unit][3] = 0x60000000;
}


/*
 * lcp_open - LCP is allowed to come up.
 */
void
lcp_open(unit)
    int unit;
{
    fsm *f = &lcp_fsm[unit];
    lcp_options *wo = &lcp_wantoptions[unit];

    f->flags = 0;
    if (wo->passive)
	f->flags |= OPT_PASSIVE;
    if (wo->silent)
	f->flags |= OPT_SILENT;
    fsm_open(f);
}


/*
 * lcp_close - Take LCP down.
 */
void
lcp_close(unit)
    int unit;
{
    fsm *f = &lcp_fsm[unit];

    if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
	/*
	 * This action is not strictly according to the FSM in RFC1548,
	 * but it does mean that the program terminates if you do a
	 * lcp_close(0) in passive/silent mode when a connection hasn't
	 * been established.
	 */
	f->state = CLOSED;
	lcp_finished(f);

    } else
	fsm_close(&lcp_fsm[unit]);
}


/*
 * lcp_lowerup - The lower layer is up.
 */
void
lcp_lowerup(unit)
    int unit;
{
    sifdown(unit);
    ppp_set_xaccm(unit, xmit_accm[unit]);
    ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0);
    ppp_recv_config(unit, PPP_MRU, 0x00000000, 0, 0);
    peer_mru[unit] = PPP_MRU;
    lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0];

    fsm_lowerup(&lcp_fsm[unit]);
}


/*
 * lcp_lowerdown - The lower layer is down.
 */
void
lcp_lowerdown(unit)
    int unit;
{
    fsm_lowerdown(&lcp_fsm[unit]);
}


/*
 * lcp_input - Input LCP packet.
 */
void
lcp_input(unit, p, len)
    int unit;
    u_char *p;
    int len;
{
    int oldstate;
    fsm *f = &lcp_fsm[unit];
    lcp_options *go = &lcp_gotoptions[f->unit];

    oldstate = f->state;
    fsm_input(f, p, len);
    if (oldstate == REQSENT && f->state == ACKSENT) {
	/*
	 * The peer will probably send us an ack soon and then
	 * immediately start sending packets with the negotiated
	 * options.  So as to be ready when that happens, we set
	 * our receive side to accept packets as negotiated now.
	 */
	ppp_recv_config(f->unit, (int) PPP_MRU,
			(int)(go->neg_asyncmap? go->asyncmap: 0x00000000),
			go->neg_pcompression, go->neg_accompression);
    }
}


/*
 * lcp_extcode - Handle a LCP-specific code.
 */
static int
lcp_extcode(f, code, id, inp, len)
    fsm *f;
    int code, id;
    u_char *inp;
    int len;
{
    u_char *magp;

    switch( code ){
    case PROTREJ:
	lcp_rprotrej(f, inp, len);
	break;
    
    case ECHOREQ:
	if (f->state != OPENED)
	    break;
	LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id));
	magp = inp;
	PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
	fsm_sdata(f, ECHOREP, (u_char)id, inp, len);
	break;
    
    case ECHOREP:
	lcp_received_echo_reply(f, id, inp, len);
	break;

    case DISCREQ:
	break;

    default:
	return 0;
    }
    return 1;
}

    
/*
 * lcp_rprotrej - Receive an Protocol-Reject.
 *
 * Figure out which protocol is rejected and inform it.
 */
static void
lcp_rprotrej(f, inp, len)
    fsm *f;
    u_char *inp;
    int len;
{
    u_short prot;

    LCPDEBUG((LOG_INFO, "lcp_rprotrej."));

    if (len < sizeof (u_short)) {
	LCPDEBUG((LOG_INFO,
		  "lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
	return;
    }

    GETSHORT(prot, inp);

    LCPDEBUG((LOG_INFO,
	      "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!",
	      prot));

    /*
     * Protocol-Reject packets received in any state other than the LCP
     * OPENED state SHOULD be silently discarded.
     */
    if( f->state != OPENED ){
	LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d",
		  f->state));
	return;
    }

    DEMUXPROTREJ(f->unit, prot);	/* Inform protocol */
}


/*
 * lcp_protrej - A Protocol-Reject was received.
 */
/*ARGSUSED*/
void
lcp_protrej(unit)
    int unit;
{
    /*
     * Can't reject LCP!
     */
    LCPDEBUG((LOG_WARNING,
	      "lcp_protrej: Received Protocol-Reject for LCP!"));
    fsm_protreject(&lcp_fsm[unit]);
}


/*
 * lcp_sprotrej - Send a Protocol-Reject for some protocol.
 */
void
lcp_sprotrej(unit, p, len)
    int unit;
    u_char *p;
    int len;
{
    /*
     * Send back the protocol and the information field of the
     * rejected packet.  We only get here if LCP is in the OPENED state.
     */
    p += 2;
    len -= 2;

    fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id,
	      p, len);
}


/*
 * lcp_resetci - Reset our CI.
 */
static void
  lcp_resetci(f)
fsm *f;
{
    lcp_wantoptions[f->unit].magicnumber = magic();
    lcp_wantoptions[f->unit].numloops = 0;
    lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
    peer_mru[f->unit] = PPP_MRU;
}


/*
 * lcp_cilen - Return length of our CI.
 */
static int
lcp_cilen(f)
    fsm *f;
{
    lcp_options *go = &lcp_gotoptions[f->unit];

#define LENCIVOID(neg)	(neg ? CILEN_VOID : 0)
#define LENCICHAP(neg)	(neg ? CILEN_CHAP : 0)
#define LENCISHORT(neg)	(neg ? CILEN_SHORT : 0)
#define LENCILONG(neg)	(neg ? CILEN_LONG : 0)
#define LENCILQR(neg)	(neg ? CILEN_LQR: 0)
    /*
     * NB: we only ask for one of CHAP and UPAP, even if we will
     * accept either.
     */
    return (LENCISHORT(go->neg_mru) +
	    LENCILONG(go->neg_asyncmap) +
	    LENCICHAP(go->neg_chap) +
	    LENCISHORT(!go->neg_chap && go->neg_upap) +
	    LENCILQR(go->neg_lqr) +
	    LENCILONG(go->neg_magicnumber) +
	    LENCIVOID(go->neg_pcompression) +
	    LENCIVOID(go->neg_accompression));
}


/*
 * lcp_addci - Add our desired CIs to a packet.
 */
static void
lcp_addci(f, ucp, lenp)
    fsm *f;
    u_char *ucp;
    int *lenp;
{
    lcp_options *go = &lcp_gotoptions[f->unit];
    u_char *start_ucp = ucp;

#define ADDCIVOID(opt, neg) \
    if (neg) { \
	PUTCHAR(opt, ucp); \
	PUTCHAR(CILEN_VOID, ucp); \
    }
#define ADDCISHORT(opt, neg, val) \
    if (neg) { \
	PUTCHAR(opt, ucp); \
	PUTCHAR(CILEN_SHORT, ucp); \
	PUTSHORT(val, ucp); \
    }
#define ADDCICHAP(opt, neg, val, digest) \
    if (neg) { \
	PUTCHAR(opt, ucp); \
	PUTCHAR(CILEN_CHAP, ucp); \
	PUTSHORT(val, ucp); \
	PUTCHAR(digest, ucp); \
    }
#define ADDCILONG(opt, neg, val) \
    if (neg) { \
	PUTCHAR(opt, ucp); \
	PUTCHAR(CILEN_LONG, ucp); \
	PUTLONG(val, ucp); \
    }
#define ADDCILQR(opt, neg, val) \
    if (neg) { \
	PUTCHAR(opt, ucp); \
	PUTCHAR(CILEN_LQR, ucp); \
	PUTSHORT(PPP_LQR, ucp); \
	PUTLONG(val, ucp); \
    }

    ADDCISHORT(CI_MRU, go->neg_mru, go->mru);
    ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap);
    ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
    ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
    ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
    ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
    ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
    ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);

    if (ucp - start_ucp != *lenp) {
	/* this should never happen, because peer_mtu should be 1500 */
	syslog(LOG_ERR, "Bug in lcp_addci: wrong length");
    }
}


/*
 * lcp_ackci - Ack our CIs.
 * This should not modify any state if the Ack is bad.
 *
 * Returns:
 *	0 - Ack was bad.
 *	1 - Ack was good.
 */
static int
lcp_ackci(f, p, len)
    fsm *f;
    u_char *p;
    int len;
{
    lcp_options *go = &lcp_gotoptions[f->unit];
    u_char cilen, citype, cichar;
    u_short cishort;
    u_int32_t cilong;

    /*
     * CIs must be in exactly the same order that we sent.
     * Check packet length and CI length at each step.
     * If we find any deviations, then this packet is bad.
     */
#define ACKCIVOID(opt, neg) \
    if (neg) { \
	if ((len -= CILEN_VOID) < 0) \
	    goto bad; \
	GETCHAR(citype, p); \
	GETCHAR(cilen, p); \
	if (cilen != CILEN_VOID || \
	    citype != opt) \
	    goto bad; \
    }
#define ACKCISHORT(opt, neg, val) \
    if (neg) { \
	if ((len -= CILEN_SHORT) < 0) \
	    goto bad; \
	GETCHAR(citype, p); \
	GETCHAR(cilen, p); \
	if (cilen != CILEN_SHORT || \
	    citype != opt) \
	    goto bad; \
	GETSHORT(cishort, p); \
	if (cishort != val) \
	    goto bad; \
    }
#define ACKCICHAP(opt, neg, val, digest) \
    if (neg) { \
	if ((len -= CILEN_CHAP) < 0) \
	    goto bad; \
	GETCHAR(citype, p); \
	GETCHAR(cilen, p); \
	if (cilen != CILEN_CHAP || \
	    citype != opt) \
	    goto bad; \
	GETSHORT(cishort, p); \
	if (cishort != val) \
	    goto bad; \
	GETCHAR(cichar, p); \
	if (cichar != digest) \
	  goto bad; \
    }
#define ACKCILONG(opt, neg, val) \
    if (neg) { \
	if ((len -= CILEN_LONG) < 0) \
	    goto bad; \
	GETCHAR(citype, p); \
	GETCHAR(cilen, p); \
	if (cilen != CILEN_LONG || \
	    citype != opt) \
	    goto bad; \
	GETLONG(cilong, p); \
	if (cilong != val) \
	    goto bad; \
    }

⌨️ 快捷键说明

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