static int ccp_reqci()

in net/ip/lwip_base/src/netif/ppp/ccp.c [1105:1383]


static int ccp_reqci(fsm *f, u_char *p, int *lenp, int dont_nak) {
    ppp_pcb *pcb = f->pcb;
    ccp_options *ho = &pcb->ccp_hisoptions;
    ccp_options *ao = &pcb->ccp_allowoptions;
    int ret, newret;
#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT
    int res;
    int nb;
#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT */
    u_char *p0, *retp;
    int len, clen, type;
#if MPPE_SUPPORT
    u8_t rej_for_ci_mppe = 1;	/* Are we rejecting based on a bad/missing */
				/* CI_MPPE, or due to other options?       */
#endif /* MPPE_SUPPORT */

    ret = CONFACK;
    retp = p0 = p;
    len = *lenp;

    memset(ho, 0, sizeof(ccp_options));
    ho->method = (len > 0)? p[0]: 0;

    while (len > 0) {
	newret = CONFACK;
	if (len < 2 || p[1] < 2 || p[1] > len) {
	    /* length is bad */
	    clen = len;
	    newret = CONFREJ;

	} else {
	    type = p[0];
	    clen = p[1];

	    switch (type) {
#if MPPE_SUPPORT
	    case CI_MPPE:
		if (!ao->mppe || clen != CILEN_MPPE) {
		    newret = CONFREJ;
		    break;
		}
		MPPE_CI_TO_OPTS(&p[2], ho->mppe);

		/* Nak if anything unsupported or unknown are set. */
		if (ho->mppe & MPPE_OPT_UNSUPPORTED) {
		    newret = CONFNAK;
		    ho->mppe &= ~MPPE_OPT_UNSUPPORTED;
		}
		if (ho->mppe & MPPE_OPT_UNKNOWN) {
		    newret = CONFNAK;
		    ho->mppe &= ~MPPE_OPT_UNKNOWN;
		}

		/* Check state opt */
		if (ho->mppe & MPPE_OPT_STATEFUL) {
		    /*
		     * We can Nak and request stateless, but it's a
		     * lot easier to just assume the peer will request
		     * it if he can do it; stateful mode is bad over
		     * the Internet -- which is where we expect MPPE.
		     */
		   if (pcb->settings.refuse_mppe_stateful) {
			ppp_error("Refusing MPPE stateful mode offered by peer");
			newret = CONFREJ;
			break;
		    }
		}

		/* Find out which of {S,L} are set. */
		if ((ho->mppe & MPPE_OPT_128)
		     && (ho->mppe & MPPE_OPT_40)) {
		    /* Both are set, negotiate the strongest. */
		    newret = CONFNAK;
		    if (ao->mppe & MPPE_OPT_128)
			ho->mppe &= ~MPPE_OPT_40;
		    else if (ao->mppe & MPPE_OPT_40)
			ho->mppe &= ~MPPE_OPT_128;
		    else {
			newret = CONFREJ;
			break;
		    }
		} else if (ho->mppe & MPPE_OPT_128) {
		    if (!(ao->mppe & MPPE_OPT_128)) {
			newret = CONFREJ;
			break;
		    }
		} else if (ho->mppe & MPPE_OPT_40) {
		    if (!(ao->mppe & MPPE_OPT_40)) {
			newret = CONFREJ;
			break;
		    }
		} else {
		    /* Neither are set. */
		    /* We cannot accept this.  */
		    newret = CONFNAK;
		    /* Give the peer our idea of what can be used,
		       so it can choose and confirm */
		    ho->mppe = ao->mppe;
		}

		/* rebuild the opts */
		MPPE_OPTS_TO_CI(ho->mppe, &p[2]);
		if (newret == CONFACK) {
		    int mtu;

		    mppe_init(pcb, &pcb->mppe_comp, ho->mppe);
		    /*
		     * We need to decrease the interface MTU by MPPE_PAD
		     * because MPPE frames **grow**.  The kernel [must]
		     * allocate MPPE_PAD extra bytes in xmit buffers.
		     */
		    mtu = netif_get_mtu(pcb);
		    if (mtu)
			netif_set_mtu(pcb, mtu - MPPE_PAD);
		    else
			newret = CONFREJ;
		}

		/*
		 * We have accepted MPPE or are willing to negotiate
		 * MPPE parameters.  A CONFREJ is due to subsequent
		 * (non-MPPE) processing.
		 */
		rej_for_ci_mppe = 0;
		break;
#endif /* MPPE_SUPPORT */
#if DEFLATE_SUPPORT
	    case CI_DEFLATE:
	    case CI_DEFLATE_DRAFT:
		if (!ao->deflate || clen != CILEN_DEFLATE
		    || (!ao->deflate_correct && type == CI_DEFLATE)
		    || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) {
		    newret = CONFREJ;
		    break;
		}

		ho->deflate = 1;
		ho->deflate_size = nb = DEFLATE_SIZE(p[2]);
		if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
		    || p[3] != DEFLATE_CHK_SEQUENCE
		    || nb > ao->deflate_size || nb < DEFLATE_MIN_WORKS) {
		    newret = CONFNAK;
		    if (!dont_nak) {
			p[2] = DEFLATE_MAKE_OPT(ao->deflate_size);
			p[3] = DEFLATE_CHK_SEQUENCE;
			/* fall through to test this #bits below */
		    } else
			break;
		}

		/*
		 * Check whether we can do Deflate with the window
		 * size they want.  If the window is too big, reduce
		 * it until the kernel can cope and nak with that.
		 * We only check this for the first option.
		 */
		if (p == p0) {
		    for (;;) {
			res = ccp_test(pcb, p, CILEN_DEFLATE, 1);
			if (res > 0)
			    break;		/* it's OK now */
			if (res < 0 || nb == DEFLATE_MIN_WORKS || dont_nak) {
			    newret = CONFREJ;
			    p[2] = DEFLATE_MAKE_OPT(ho->deflate_size);
			    break;
			}
			newret = CONFNAK;
			--nb;
			p[2] = DEFLATE_MAKE_OPT(nb);
		    }
		}
		break;
#endif /* DEFLATE_SUPPORT */
#if BSDCOMPRESS_SUPPORT
	    case CI_BSD_COMPRESS:
		if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) {
		    newret = CONFREJ;
		    break;
		}

		ho->bsd_compress = 1;
		ho->bsd_bits = nb = BSD_NBITS(p[2]);
		if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION
		    || nb > ao->bsd_bits || nb < BSD_MIN_BITS) {
		    newret = CONFNAK;
		    if (!dont_nak) {
			p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits);
			/* fall through to test this #bits below */
		    } else
			break;
		}

		/*
		 * Check whether we can do BSD-Compress with the code
		 * size they want.  If the code size is too big, reduce
		 * it until the kernel can cope and nak with that.
		 * We only check this for the first option.
		 */
		if (p == p0) {
		    for (;;) {
			res = ccp_test(pcb, p, CILEN_BSD_COMPRESS, 1);
			if (res > 0)
			    break;
			if (res < 0 || nb == BSD_MIN_BITS || dont_nak) {
			    newret = CONFREJ;
			    p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION,
						ho->bsd_bits);
			    break;
			}
			newret = CONFNAK;
			--nb;
			p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
		    }
		}
		break;
#endif /* BSDCOMPRESS_SUPPORT */
#if PREDICTOR_SUPPORT
	    case CI_PREDICTOR_1:
		if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) {
		    newret = CONFREJ;
		    break;
		}

		ho->predictor_1 = 1;
		if (p == p0
		    && ccp_test(pcb, p, CILEN_PREDICTOR_1, 1) <= 0) {
		    newret = CONFREJ;
		}
		break;

	    case CI_PREDICTOR_2:
		if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) {
		    newret = CONFREJ;
		    break;
		}

		ho->predictor_2 = 1;
		if (p == p0
		    && ccp_test(pcb, p, CILEN_PREDICTOR_2, 1) <= 0) {
		    newret = CONFREJ;
		}
		break;
#endif /* PREDICTOR_SUPPORT */

	    default:
		newret = CONFREJ;
	    }
	}

	if (newret == CONFNAK && dont_nak)
	    newret = CONFREJ;
	if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) {
	    /* we're returning this option */
	    if (newret == CONFREJ && ret == CONFNAK)
		retp = p0;
	    ret = newret;
	    if (p != retp)
		MEMCPY(retp, p, clen);
	    retp += clen;
	}

	p += clen;
	len -= clen;
    }

    if (ret != CONFACK) {
	if (ret == CONFREJ && *lenp == retp - p0)
	    pcb->ccp_all_rejected = 1;
	else
	    *lenp = retp - p0;
    }
#if MPPE_SUPPORT
    if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) {
	ppp_error("MPPE required but peer negotiation failed");
	lcp_close(pcb, "MPPE required but peer negotiation failed");
    }
#endif /* MPPE_SUPPORT */
    return ret;
}