static int lcp_nakci()

in net/ip/lwip_base/src/netif/ppp/lcp.c [1127:1577]


static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
    ppp_pcb *pcb = f->pcb;
    lcp_options *go = &pcb->lcp_gotoptions;
    lcp_options *wo = &pcb->lcp_wantoptions;
    u_char citype, cichar, *next;
    u_short cishort;
    u32_t cilong;
    lcp_options no;		/* options we've seen Naks for */
    lcp_options try_;		/* options to request next time */
    int looped_back = 0;
    int cilen;

    BZERO(&no, sizeof(no));
    try_ = *go;

    /*
     * Any Nak'd 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 NAKCIVOID(opt, neg) \
    if (go->neg && \
	len >= CILEN_VOID && \
	p[1] == CILEN_VOID && \
	p[0] == opt) { \
	len -= CILEN_VOID; \
	INCPTR(CILEN_VOID, p); \
	no.neg = 1; \
	try_.neg = 0; \
    }
#if CHAP_SUPPORT
#define NAKCICHAP(opt, neg, code) \
    if (go->neg && \
	len >= CILEN_CHAP && \
	p[1] == CILEN_CHAP && \
	p[0] == opt) { \
	len -= CILEN_CHAP; \
	INCPTR(2, p); \
	GETSHORT(cishort, p); \
	GETCHAR(cichar, p); \
	no.neg = 1; \
	code \
    }
#endif /* CHAP_SUPPORT */
#define NAKCICHAR(opt, neg, code) \
    if (go->neg && \
	len >= CILEN_CHAR && \
	p[1] == CILEN_CHAR && \
	p[0] == opt) { \
	len -= CILEN_CHAR; \
	INCPTR(2, p); \
	GETCHAR(cichar, p); \
	no.neg = 1; \
	code \
    }
#define NAKCISHORT(opt, neg, code) \
    if (go->neg && \
	len >= CILEN_SHORT && \
	p[1] == CILEN_SHORT && \
	p[0] == opt) { \
	len -= CILEN_SHORT; \
	INCPTR(2, p); \
	GETSHORT(cishort, p); \
	no.neg = 1; \
	code \
    }
#define NAKCILONG(opt, neg, code) \
    if (go->neg && \
	len >= CILEN_LONG && \
	p[1] == CILEN_LONG && \
	p[0] == opt) { \
	len -= CILEN_LONG; \
	INCPTR(2, p); \
	GETLONG(cilong, p); \
	no.neg = 1; \
	code \
    }
#if LQR_SUPPORT
#define NAKCILQR(opt, neg, code) \
    if (go->neg && \
	len >= CILEN_LQR && \
	p[1] == CILEN_LQR && \
	p[0] == opt) { \
	len -= CILEN_LQR; \
	INCPTR(2, p); \
	GETSHORT(cishort, p); \
	GETLONG(cilong, p); \
	no.neg = 1; \
	code \
    }
#endif /* LQR_SUPPORT */
#define NAKCIENDP(opt, neg) \
    if (go->neg && \
	len >= CILEN_CHAR && \
	p[0] == opt && \
	p[1] >= CILEN_CHAR && \
	p[1] <= len) { \
	len -= p[1]; \
	INCPTR(p[1], p); \
	no.neg = 1; \
	try_.neg = 0; \
    }

    /*
     * NOTE!  There must be no assignments to individual fields of *go in
     * the code below.  Any such assignment is a BUG!
     */
    /*
     * We don't care if they want to send us smaller packets than
     * we want.  Therefore, accept any MRU less than what we asked for,
     * but then ignore the new value when setting the MRU in the kernel.
     * If they send us a bigger MRU than what we asked, accept it, up to
     * the limit of the default MRU we'd get if we didn't negotiate.
     */
    if (go->neg_mru && go->mru != PPP_DEFMRU) {
	NAKCISHORT(CI_MRU, neg_mru,
		   if (cishort <= wo->mru || cishort <= PPP_DEFMRU)
		       try_.mru = cishort;
		   );
    }

    /*
     * Add any characters they want to our (receive-side) asyncmap.
     */
    if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) {
	NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
		  try_.asyncmap = go->asyncmap | cilong;
		  );
    }

    /*
     * If they've nak'd our authentication-protocol, check whether
     * they are proposing a different protocol, or a different
     * hash algorithm for CHAP.
     */
    if ((0
#if CHAP_SUPPORT
        || go->neg_chap
#endif /* CHAP_SUPPORT */
#if PAP_SUPPORT
        || go->neg_upap
#endif /* PAP_SUPPORT */
#if EAP_SUPPORT
        || go->neg_eap
#endif /* EAP_SUPPORT */
        )
	&& len >= CILEN_SHORT
	&& p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
	cilen = p[1];
	len -= cilen;
#if CHAP_SUPPORT
	no.neg_chap = go->neg_chap;
#endif /* CHAP_SUPPORT */
#if PAP_SUPPORT
	no.neg_upap = go->neg_upap;
#endif /* PAP_SUPPORT */
#if EAP_SUPPORT
	no.neg_eap = go->neg_eap;
#endif /* EAP_SUPPORT */
	INCPTR(2, p);
	GETSHORT(cishort, p);

#if PAP_SUPPORT
	if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
#if EAP_SUPPORT
	    /* If we were asking for EAP, then we need to stop that. */
	    if (go->neg_eap)
		try_.neg_eap = 0;
	    else
#endif /* EAP_SUPPORT */

#if CHAP_SUPPORT
	    /* If we were asking for CHAP, then we need to stop that. */
	    if (go->neg_chap)
		try_.neg_chap = 0;
	    else
#endif /* CHAP_SUPPORT */

	    /*
	     * If we weren't asking for CHAP or EAP, then we were asking for
	     * PAP, in which case this Nak is bad.
	     */
		goto bad;
	} else
#endif /* PAP_SUPPORT */

#if CHAP_SUPPORT
	if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
	    GETCHAR(cichar, p);
#if EAP_SUPPORT
	    /* Stop asking for EAP, if we were. */
	    if (go->neg_eap) {
		try_.neg_eap = 0;
		/* Try to set up to use their suggestion, if possible */
		if (CHAP_CANDIGEST(go->chap_mdtype, cichar))
		    try_.chap_mdtype = CHAP_MDTYPE_D(cichar);
	    } else
#endif /* EAP_SUPPORT */
	    if (go->neg_chap) {
		/*
		 * We were asking for our preferred algorithm, they must
		 * want something different.
		 */
		if (cichar != CHAP_DIGEST(go->chap_mdtype)) {
		    if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) {
			/* Use their suggestion if we support it ... */
			try_.chap_mdtype = CHAP_MDTYPE_D(cichar);
		    } else {
			/* ... otherwise, try our next-preferred algorithm. */
			try_.chap_mdtype &= ~(CHAP_MDTYPE(try_.chap_mdtype));
			if (try_.chap_mdtype == MDTYPE_NONE) /* out of algos */
			    try_.neg_chap = 0;
		    }
		} else {
		    /*
		     * Whoops, they Nak'd our algorithm of choice
		     * but then suggested it back to us.
		     */
		    goto bad;
		}
	    } else {
		/*
		 * Stop asking for PAP if we were asking for it.
		 */
#if PAP_SUPPORT
		try_.neg_upap = 0;
#endif /* PAP_SUPPORT */
	    }

	} else
#endif /* CHAP_SUPPORT */
	{

#if EAP_SUPPORT
	    /*
	     * If we were asking for EAP, and they're Conf-Naking EAP,
	     * well, that's just strange.  Nobody should do that.
	     */
	    if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap)
		ppp_dbglog("Unexpected Conf-Nak for EAP");

	    /*
	     * We don't recognize what they're suggesting.
	     * Stop asking for what we were asking for.
	     */
	    if (go->neg_eap)
		try_.neg_eap = 0;
	    else
#endif /* EAP_SUPPORT */

#if CHAP_SUPPORT
	    if (go->neg_chap)
		try_.neg_chap = 0;
	    else
#endif /* CHAP_SUPPORT */

#if PAP_SUPPORT
	    if(1)
		try_.neg_upap = 0;
	    else
#endif /* PAP_SUPPORT */
	    {}

	    p += cilen - CILEN_SHORT;
	}
    }

#if LQR_SUPPORT
    /*
     * If they can't cope with our link quality protocol, we'll have
     * to stop asking for LQR.  We haven't got any other protocol.
     * If they Nak the reporting period, take their value XXX ?
     */
    NAKCILQR(CI_QUALITY, neg_lqr,
	     if (cishort != PPP_LQR)
		 try_.neg_lqr = 0;
	     else
		 try_.lqr_period = cilong;
	     );
#endif /* LQR_SUPPORT */

    /*
     * Only implementing CBCP...not the rest of the callback options
     */
    NAKCICHAR(CI_CALLBACK, neg_cbcp,
              try_.neg_cbcp = 0;
              (void)cichar; /* if CHAP support is not compiled, cichar is set but not used, which makes some compilers complaining */
              );

    /*
     * Check for a looped-back line.
     */
    NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
	      try_.magicnumber = magic();
	      looped_back = 1;
	      );

    /*
     * Peer shouldn't send Nak for protocol compression or
     * address/control compression requests; they should send
     * a Reject instead.  If they send a Nak, treat it as a Reject.
     */
    NAKCIVOID(CI_PCOMPRESSION, neg_pcompression);
    NAKCIVOID(CI_ACCOMPRESSION, neg_accompression);

#ifdef HAVE_MULTILINK
    /*
     * Nak for MRRU option - accept their value if it is smaller
     * than the one we want.
     */
    if (go->neg_mrru) {
	NAKCISHORT(CI_MRRU, neg_mrru,
		   if (treat_as_reject)
		       try_.neg_mrru = 0;
		   else if (cishort <= wo->mrru)
		       try_.mrru = cishort;
		   );
    }
#else /* HAVE_MULTILINK */
    LWIP_UNUSED_ARG(treat_as_reject);
#endif /* HAVE_MULTILINK */

    /*
     * Nak for short sequence numbers shouldn't be sent, treat it
     * like a reject.
     */
    NAKCIVOID(CI_SSNHF, neg_ssnhf);

    /*
     * Nak of the endpoint discriminator option is not permitted,
     * treat it like a reject.
     */
    NAKCIENDP(CI_EPDISC, neg_endpoint);

    /*
     * There may be remaining CIs, if the peer is requesting negotiation
     * on an option that we didn't include in our request packet.
     * If we see an option that we requested, or one we've already seen
     * in this packet, then this packet is bad.
     * If we wanted to respond by starting to negotiate on the requested
     * option(s), we could, but we don't, because except for the
     * authentication type and quality protocol, if we are not negotiating
     * an option, it is because we were told not to.
     * For the authentication type, the Nak from the peer means
     * `let me authenticate myself with you' which is a bit pointless.
     * For the quality protocol, the Nak means `ask me to send you quality
     * reports', but if we didn't ask for them, we don't want them.
     * An option we don't recognize represents the peer asking to
     * negotiate some option we don't support, so ignore it.
     */
    while (len >= CILEN_VOID) {
	GETCHAR(citype, p);
	GETCHAR(cilen, p);
	if (cilen < CILEN_VOID || (len -= cilen) < 0)
	    goto bad;
	next = p + cilen - 2;

	switch (citype) {
	case CI_MRU:
	    if ((go->neg_mru && go->mru != PPP_DEFMRU)
		|| no.neg_mru || cilen != CILEN_SHORT)
		goto bad;
	    GETSHORT(cishort, p);
	    if (cishort < PPP_DEFMRU) {
		try_.neg_mru = 1;
		try_.mru = cishort;
	    }
	    break;
	case CI_ASYNCMAP:
	    if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF)
		|| no.neg_asyncmap || cilen != CILEN_LONG)
		goto bad;
	    break;
	case CI_AUTHTYPE:
	    if (0
#if CHAP_SUPPORT
                || go->neg_chap || no.neg_chap
#endif /* CHAP_SUPPORT */
#if PAP_SUPPORT
                || go->neg_upap || no.neg_upap
#endif /* PAP_SUPPORT */
#if EAP_SUPPORT
		|| go->neg_eap || no.neg_eap
#endif /* EAP_SUPPORT */
		)
		goto bad;
	    break;
	case CI_MAGICNUMBER:
	    if (go->neg_magicnumber || no.neg_magicnumber ||
		cilen != CILEN_LONG)
		goto bad;
	    break;
	case CI_PCOMPRESSION:
	    if (go->neg_pcompression || no.neg_pcompression
		|| cilen != CILEN_VOID)
		goto bad;
	    break;
	case CI_ACCOMPRESSION:
	    if (go->neg_accompression || no.neg_accompression
		|| cilen != CILEN_VOID)
		goto bad;
	    break;
#if LQR_SUPPORT
	case CI_QUALITY:
	    if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
		goto bad;
	    break;
#endif /* LQR_SUPPORT */
#ifdef HAVE_MULTILINK
	case CI_MRRU:
	    if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT)
		goto bad;
	    break;
#endif /* HAVE_MULTILINK */
	case CI_SSNHF:
	    if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID)
		goto bad;
	    try_.neg_ssnhf = 1;
	    break;
	case CI_EPDISC:
	    if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR)
		goto bad;
	    break;
	default:
	    break;
	}
	p = next;
    }

    /*
     * OK, the Nak is good.  Now we can update state.
     * If there are any options left we ignore them.
     */
    if (f->state != PPP_FSM_OPENED) {
	if (looped_back) {
	    if (++try_.numloops >= pcb->settings.lcp_loopbackfail) {
		ppp_notice("Serial line is looped back.");
		pcb->err_code = PPPERR_LOOPBACK;
		lcp_close(f->pcb, "Loopback detected");
	    }
	} else
	    try_.numloops = 0;
	*go = try_;
    }

    return 1;

bad:
    LCPDEBUG(("lcp_nakci: received bad Nak!"));
    return 0;
}