static int lcp_reqci()

in net/ip/lwip_base/src/netif/ppp/lcp.c [1820:2284]


static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree) {
    ppp_pcb *pcb = f->pcb;
    lcp_options *go = &pcb->lcp_gotoptions;
    lcp_options *ho = &pcb->lcp_hisoptions;
    lcp_options *ao = &pcb->lcp_allowoptions;
    u_char *cip, *next;		/* Pointer to current and next CIs */
    int cilen, citype, cichar;	/* Parsed len, type, char value */
    u_short cishort;		/* Parsed short value */
    u32_t cilong;		/* Parse long value */
    int rc = CONFACK;		/* Final packet return code */
    int orc;			/* Individual option return code */
    u_char *p;			/* Pointer to next char to parse */
    u_char *rejp;		/* Pointer to next char in reject frame */
    struct pbuf *nakp;          /* Nak buffer */
    u_char *nakoutp;		/* Pointer to next char in Nak frame */
    int l = *lenp;		/* Length left */

    /*
     * Reset all his options.
     */
    BZERO(ho, sizeof(*ho));

    /*
     * Process all his options.
     */
    next = inp;
    nakp = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE);
    if(NULL == nakp)
        return 0;
    if(nakp->tot_len != nakp->len) {
        pbuf_free(nakp);
        return 0;
    }

    nakoutp = (u_char*)nakp->payload;
    rejp = inp;
    while (l) {
	orc = CONFACK;			/* Assume success */
	cip = p = next;			/* Remember begining of CI */
	if (l < 2 ||			/* Not enough data for CI header or */
	    p[1] < 2 ||			/*  CI length too small or */
	    p[1] > l) {			/*  CI length too big? */
	    LCPDEBUG(("lcp_reqci: bad CI length!"));
	    orc = CONFREJ;		/* Reject bad CI */
	    cilen = l;			/* Reject till end of packet */
	    l = 0;			/* Don't loop again */
	    citype = 0;
	    goto endswitch;
	}
	GETCHAR(citype, p);		/* Parse CI type */
	GETCHAR(cilen, p);		/* Parse CI length */
	l -= cilen;			/* Adjust remaining length */
	next += cilen;			/* Step to next CI */

	switch (citype) {		/* Check CI type */
	case CI_MRU:
	    if (!ao->neg_mru ||		/* Allow option? */
		cilen != CILEN_SHORT) {	/* Check CI length */
		orc = CONFREJ;		/* Reject CI */
		break;
	    }
	    GETSHORT(cishort, p);	/* Parse MRU */

	    /*
	     * He must be able to receive at least our minimum.
	     * No need to check a maximum.  If he sends a large number,
	     * we'll just ignore it.
	     */
	    if (cishort < PPP_MINMRU) {
		orc = CONFNAK;		/* Nak CI */
		PUTCHAR(CI_MRU, nakoutp);
		PUTCHAR(CILEN_SHORT, nakoutp);
		PUTSHORT(PPP_MINMRU, nakoutp);	/* Give him a hint */
		break;
	    }
	    ho->neg_mru = 1;		/* Remember he sent MRU */
	    ho->mru = cishort;		/* And remember value */
	    break;

	case CI_ASYNCMAP:
	    if (!ao->neg_asyncmap ||
		cilen != CILEN_LONG) {
		orc = CONFREJ;
		break;
	    }
	    GETLONG(cilong, p);

	    /*
	     * Asyncmap must have set at least the bits
	     * which are set in lcp_allowoptions[unit].asyncmap.
	     */
	    if ((ao->asyncmap & ~cilong) != 0) {
		orc = CONFNAK;
		PUTCHAR(CI_ASYNCMAP, nakoutp);
		PUTCHAR(CILEN_LONG, nakoutp);
		PUTLONG(ao->asyncmap | cilong, nakoutp);
		break;
	    }
	    ho->neg_asyncmap = 1;
	    ho->asyncmap = cilong;
	    break;

	case CI_AUTHTYPE:
	    if (cilen < CILEN_SHORT ||
		!(0
#if PAP_SUPPORT
		|| ao->neg_upap
#endif /* PAP_SUPPORT */
#if CHAP_SUPPORT
		|| ao->neg_chap
#endif /* CHAP_SUPPORT */
#if EAP_SUPPORT
		|| ao->neg_eap
#endif /* EAP_SUPPORT */
		)) {
		/*
		 * Reject the option if we're not willing to authenticate.
		 */
		ppp_dbglog("No auth is possible");
		orc = CONFREJ;
		break;
	    }
	    GETSHORT(cishort, p);

	    /*
	     * Authtype must be PAP, CHAP, or EAP.
	     *
	     * Note: if more than one of ao->neg_upap, ao->neg_chap, and
	     * ao->neg_eap are set, and the peer sends a Configure-Request
	     * with two or more authenticate-protocol requests, then we will
	     * reject the second request.
	     * Whether we end up doing CHAP, UPAP, or EAP depends then on
	     * the ordering of the CIs in the peer's Configure-Request.
             */

#if PAP_SUPPORT
	    if (cishort == PPP_PAP) {
		/* we've already accepted CHAP or EAP */
		if (0
#if CHAP_SUPPORT
		    || ho->neg_chap
#endif /* CHAP_SUPPORT */
#if EAP_SUPPORT
		    || ho->neg_eap
#endif /* EAP_SUPPORT */
		    || cilen != CILEN_SHORT) {
		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
		    orc = CONFREJ;
		    break;
		}
		if (!ao->neg_upap) {	/* we don't want to do PAP */
		    orc = CONFNAK;	/* NAK it and suggest CHAP or EAP */
		    PUTCHAR(CI_AUTHTYPE, nakoutp);
#if EAP_SUPPORT
		    if (ao->neg_eap) {
			PUTCHAR(CILEN_SHORT, nakoutp);
			PUTSHORT(PPP_EAP, nakoutp);
		    } else {
#endif /* EAP_SUPPORT */
#if CHAP_SUPPORT
			PUTCHAR(CILEN_CHAP, nakoutp);
			PUTSHORT(PPP_CHAP, nakoutp);
			PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
#endif /* CHAP_SUPPORT */
#if EAP_SUPPORT
		    }
#endif /* EAP_SUPPORT */
		    break;
		}
		ho->neg_upap = 1;
		break;
	    }
#endif /* PAP_SUPPORT */
#if CHAP_SUPPORT
	    if (cishort == PPP_CHAP) {
		/* we've already accepted PAP or EAP */
		if (
#if PAP_SUPPORT
		    ho->neg_upap ||
#endif /* PAP_SUPPORT */
#if EAP_SUPPORT
		    ho->neg_eap ||
#endif /* EAP_SUPPORT */
		    cilen != CILEN_CHAP) {
		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
		    orc = CONFREJ;
		    break;
		}
		if (!ao->neg_chap) {	/* we don't want to do CHAP */
		    orc = CONFNAK;	/* NAK it and suggest EAP or PAP */
		    PUTCHAR(CI_AUTHTYPE, nakoutp);
		    PUTCHAR(CILEN_SHORT, nakoutp);
#if EAP_SUPPORT
		    if (ao->neg_eap) {
			PUTSHORT(PPP_EAP, nakoutp);
		    } else
#endif /* EAP_SUPPORT */
#if PAP_SUPPORT
		    if(1) {
			PUTSHORT(PPP_PAP, nakoutp);
		    }
		    else
#endif /* PAP_SUPPORT */
		    {}
		    break;
		}
		GETCHAR(cichar, p);	/* get digest type */
		if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) {
		    /*
		     * We can't/won't do the requested type,
		     * suggest something else.
		     */
		    orc = CONFNAK;
		    PUTCHAR(CI_AUTHTYPE, nakoutp);
		    PUTCHAR(CILEN_CHAP, nakoutp);
		    PUTSHORT(PPP_CHAP, nakoutp);
		    PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
		    break;
		}
		ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */
		ho->neg_chap = 1;
		break;
	    }
#endif /* CHAP_SUPPORT */
#if EAP_SUPPORT
	    if (cishort == PPP_EAP) {
		/* we've already accepted CHAP or PAP */
		if (
#if CHAP_SUPPORT
		    ho->neg_chap ||
#endif /* CHAP_SUPPORT */
#if PAP_SUPPORT
		    ho->neg_upap ||
#endif /* PAP_SUPPORT */
		    cilen != CILEN_SHORT) {
		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting..."));
		    orc = CONFREJ;
		    break;
		}
		if (!ao->neg_eap) {	/* we don't want to do EAP */
		    orc = CONFNAK;	/* NAK it and suggest CHAP or PAP */
		    PUTCHAR(CI_AUTHTYPE, nakoutp);
#if CHAP_SUPPORT
		    if (ao->neg_chap) {
			PUTCHAR(CILEN_CHAP, nakoutp);
			PUTSHORT(PPP_CHAP, nakoutp);
			PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
		    } else
#endif /* CHAP_SUPPORT */
#if PAP_SUPPORT
		    if(1) {
			PUTCHAR(CILEN_SHORT, nakoutp);
			PUTSHORT(PPP_PAP, nakoutp);
		    } else
#endif /* PAP_SUPPORT */
		    {}
		    break;
		}
		ho->neg_eap = 1;
		break;
	    }
#endif /* EAP_SUPPORT */

	    /*
	     * We don't recognize the protocol they're asking for.
	     * Nak it with something we're willing to do.
	     * (At this point we know ao->neg_upap || ao->neg_chap ||
	     * ao->neg_eap.)
	     */
	    orc = CONFNAK;
	    PUTCHAR(CI_AUTHTYPE, nakoutp);

#if EAP_SUPPORT
	    if (ao->neg_eap) {
		PUTCHAR(CILEN_SHORT, nakoutp);
		PUTSHORT(PPP_EAP, nakoutp);
	    } else
#endif /* EAP_SUPPORT */
#if CHAP_SUPPORT
	    if (ao->neg_chap) {
		PUTCHAR(CILEN_CHAP, nakoutp);
		PUTSHORT(PPP_CHAP, nakoutp);
		PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
	    } else
#endif /* CHAP_SUPPORT */
#if PAP_SUPPORT
	    if(1) {
		PUTCHAR(CILEN_SHORT, nakoutp);
		PUTSHORT(PPP_PAP, nakoutp);
	    } else
#endif /* PAP_SUPPORT */
	    {}
	    break;

#if LQR_SUPPORT
	case CI_QUALITY:
	    if (!ao->neg_lqr ||
		cilen != CILEN_LQR) {
		orc = CONFREJ;
		break;
	    }

	    GETSHORT(cishort, p);
	    GETLONG(cilong, p);

	    /*
	     * Check the protocol and the reporting period.
	     * XXX When should we Nak this, and what with?
	     */
	    if (cishort != PPP_LQR) {
		orc = CONFNAK;
		PUTCHAR(CI_QUALITY, nakoutp);
		PUTCHAR(CILEN_LQR, nakoutp);
		PUTSHORT(PPP_LQR, nakoutp);
		PUTLONG(ao->lqr_period, nakoutp);
		break;
	    }
	    break;
#endif /* LQR_SUPPORT */

	case CI_MAGICNUMBER:
	    if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
		cilen != CILEN_LONG) {
		orc = CONFREJ;
		break;
	    }
	    GETLONG(cilong, p);

	    /*
	     * He must have a different magic number.
	     */
	    if (go->neg_magicnumber &&
		cilong == go->magicnumber) {
		cilong = magic();	/* Don't put magic() inside macro! */
		orc = CONFNAK;
		PUTCHAR(CI_MAGICNUMBER, nakoutp);
		PUTCHAR(CILEN_LONG, nakoutp);
		PUTLONG(cilong, nakoutp);
		break;
	    }
	    ho->neg_magicnumber = 1;
	    ho->magicnumber = cilong;
	    break;


	case CI_PCOMPRESSION:
	    if (!ao->neg_pcompression ||
		cilen != CILEN_VOID) {
		orc = CONFREJ;
		break;
	    }
	    ho->neg_pcompression = 1;
	    break;

	case CI_ACCOMPRESSION:
	    if (!ao->neg_accompression ||
		cilen != CILEN_VOID) {
		orc = CONFREJ;
		break;
	    }
	    ho->neg_accompression = 1;
	    break;

#ifdef HAVE_MULTILINK
	case CI_MRRU:
	    if (!ao->neg_mrru
		|| !multilink
		|| cilen != CILEN_SHORT) {
		orc = CONFREJ;
		break;
	    }

	    GETSHORT(cishort, p);
	    /* possibly should insist on a minimum/maximum MRRU here */
	    ho->neg_mrru = 1;
	    ho->mrru = cishort;
	    break;
#endif /* HAVE_MULTILINK */

	case CI_SSNHF:
	    if (!ao->neg_ssnhf
#ifdef HAVE_MULTILINK
		|| !multilink
#endif /* HAVE_MULTILINK */
		|| cilen != CILEN_VOID) {
		orc = CONFREJ;
		break;
	    }
	    ho->neg_ssnhf = 1;
	    break;

	case CI_EPDISC:
	    if (!ao->neg_endpoint ||
		cilen < CILEN_CHAR ||
		cilen > CILEN_CHAR + MAX_ENDP_LEN) {
		orc = CONFREJ;
		break;
	    }
	    GETCHAR(cichar, p);
	    cilen -= CILEN_CHAR;
	    ho->neg_endpoint = 1;
	    ho->endpoint.class_ = cichar;
	    ho->endpoint.length = cilen;
	    MEMCPY(ho->endpoint.value, p, cilen);
	    INCPTR(cilen, p);
	    break;

	default:
	    LCPDEBUG(("lcp_reqci: rcvd unknown option %d", citype));
	    orc = CONFREJ;
	    break;
	}

endswitch:
	if (orc == CONFACK &&		/* Good CI */
	    rc != CONFACK)		/*  but prior CI wasnt? */
	    continue;			/* Don't send this one */

	if (orc == CONFNAK) {		/* Nak this CI? */
	    if (reject_if_disagree	/* Getting fed up with sending NAKs? */
		&& citype != CI_MAGICNUMBER) {
		orc = CONFREJ;		/* Get tough if so */
	    } else {
		if (rc == CONFREJ)	/* Rejecting prior CI? */
		    continue;		/* Don't send this one */
		rc = CONFNAK;
	    }
	}
	if (orc == CONFREJ) {		/* Reject this CI */
	    rc = CONFREJ;
	    if (cip != rejp)		/* Need to move rejected CI? */
		MEMCPY(rejp, cip, cilen); /* Move it */
	    INCPTR(cilen, rejp);	/* Update output pointer */
	}
    }

    /*
     * If we wanted to send additional NAKs (for unsent CIs), the
     * code would go here.  The extra NAKs would go at *nakoutp.
     * At present there are no cases where we want to ask the
     * peer to negotiate an option.
     */

    switch (rc) {
    case CONFACK:
	*lenp = next - inp;
	break;
    case CONFNAK:
	/*
	 * Copy the Nak'd options from the nak buffer to the caller's buffer.
	 */
	*lenp = nakoutp - (u_char*)nakp->payload;
	MEMCPY(inp, nakp->payload, *lenp);
	break;
    case CONFREJ:
	*lenp = rejp - inp;
	break;
    default:
	break;
    }

    pbuf_free(nakp);
    LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc)));
    return (rc);			/* Return final code */
}