in net/ip/lwip_base/src/netif/ppp/ipv6cp.c [887:1061]
static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
u_char *cip, *next; /* Pointer to current and next CIs */
u_short cilen, citype; /* Parsed len, type */
#ifdef IPV6CP_COMP
u_short cishort; /* Parsed short value */
#endif /* IPV6CP_COMP */
eui64_t ifaceid; /* Parsed interface identifier */
int rc = CONFACK; /* Final packet return code */
int orc; /* Individual option return code */
u_char *p; /* Pointer to next char to parse */
u_char *ucp = inp; /* Pointer to current output char */
int l = *len; /* Length left */
/*
* Reset all his options.
*/
BZERO(ho, sizeof(*ho));
/*
* Process all his options.
*/
next = 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? */
IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
orc = CONFREJ; /* Reject bad CI */
cilen = l; /* Reject till end of packet */
l = 0; /* Don't loop again */
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_IFACEID:
IPV6CPDEBUG(("ipv6cp: received interface identifier "));
if (!ao->neg_ifaceid ||
cilen != CILEN_IFACEID) { /* Check CI length */
orc = CONFREJ; /* Reject CI */
break;
}
/*
* If he has no interface identifier, or if we both have same
* identifier then NAK it with new idea.
* In particular, if we don't know his identifier, but he does,
* then accept it.
*/
eui64_get(ifaceid, p);
IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
orc = CONFREJ; /* Reject CI */
break;
}
if (!eui64_iszero(wo->hisid) &&
!eui64_equals(ifaceid, wo->hisid) &&
eui64_iszero(go->hisid)) {
orc = CONFNAK;
ifaceid = wo->hisid;
go->hisid = ifaceid;
DECPTR(sizeof(ifaceid), p);
eui64_put(ifaceid, p);
} else
if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
orc = CONFNAK;
if (eui64_iszero(go->hisid)) /* first time, try option */
ifaceid = wo->hisid;
while (eui64_iszero(ifaceid) ||
eui64_equals(ifaceid, go->ourid)) /* bad luck */
eui64_magic(ifaceid);
go->hisid = ifaceid;
DECPTR(sizeof(ifaceid), p);
eui64_put(ifaceid, p);
}
ho->neg_ifaceid = 1;
ho->hisid = ifaceid;
break;
#ifdef IPV6CP_COMP
case CI_COMPRESSTYPE:
IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
if (!ao->neg_vj ||
(cilen != CILEN_COMPRESS)) {
orc = CONFREJ;
break;
}
GETSHORT(cishort, p);
IPV6CPDEBUG(("(%d)", cishort));
if (!(cishort == IPV6CP_COMP)) {
orc = CONFREJ;
break;
}
ho->neg_vj = 1;
ho->vj_protocol = cishort;
break;
#endif /* IPV6CP_COMP */
default:
orc = CONFREJ;
break;
}
endswitch:
IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
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? */
orc = CONFREJ; /* Get tough if so */
else {
if (rc == CONFREJ) /* Rejecting prior CI? */
continue; /* Don't send this one */
if (rc == CONFACK) { /* Ack'd all prior CIs? */
rc = CONFNAK; /* Not anymore... */
ucp = inp; /* Backup */
}
}
}
if (orc == CONFREJ && /* Reject this CI */
rc != CONFREJ) { /* but no prior ones? */
rc = CONFREJ;
ucp = inp; /* Backup */
}
/* Need to move CI? */
if (ucp != cip)
MEMCPY(ucp, cip, cilen); /* Move it */
/* Update output pointer */
INCPTR(cilen, ucp);
}
/*
* If we aren't rejecting this packet, and we want to negotiate
* their identifier and they didn't send their identifier, then we
* send a NAK with a CI_IFACEID option appended. We assume the
* input buffer is long enough that we can append the extra
* option safely.
*/
if (rc != CONFREJ && !ho->neg_ifaceid &&
wo->req_ifaceid && !reject_if_disagree) {
if (rc == CONFACK) {
rc = CONFNAK;
ucp = inp; /* reset pointer */
wo->req_ifaceid = 0; /* don't ask again */
}
PUTCHAR(CI_IFACEID, ucp);
PUTCHAR(CILEN_IFACEID, ucp);
eui64_put(wo->hisid, ucp);
}
*len = ucp - inp; /* Compute output length */
IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
return (rc); /* Return final code */
}