/*
* Copyright (c) 2001-2002 Packet Design, LLC.
* All rights reserved.
*
* Subject to the following obligations and disclaimer of warranty,
* use and redistribution of this software, in source or object code
* forms, with or without modifications are expressly permitted by
* Packet Design; provided, however, that:
*
* (i) Any and all reproductions of the source or object code
* must include the copyright notice above and the following
* disclaimer of warranties; and
* (ii) No rights are granted, in any manner or form, to use
* Packet Design trademarks, including the mark "PACKET DESIGN"
* on advertising, endorsements, or otherwise except as such
* appears in the above copyright notice or in the software.
*
* THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
* THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
* OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
* OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
* OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
* RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
* LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
* OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
* DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
* USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Archie Cobbs <archie@freebsd.org>
*/
#include "ppp/ppp_defs.h"
#include "ppp/ppp_fsm_option.h"
#include "ppp/ppp_fsm.h"
#include "ppp/ppp_auth.h"
#include "ppp/ppp_lcp.h"
#include "ppp/ppp_util.h"
/* Memory type */
#define LCP_MTYPE "lcp"
/* LCP configuration options */
enum lcp_option {
LCP_OPT_VENDOR =0, /* Vendor specific */
LCP_OPT_MRU =1, /* Maximum-Receive-Unit */
LCP_OPT_ACCMAP =2, /* Async-Control-Character-Map */
LCP_OPT_AUTH =3, /* Authentication-Protocol */
LCP_OPT_QUAL =4, /* Quality-Protocol */
LCP_OPT_MAGIC =5, /* Magic-Number */
LCP_OPT_PFCMP =7, /* Protocol-Field-Compression */
LCP_OPT_ACFCMP =8, /* Address&Ctrl-Field-Compression */
LCP_OPT_FCSALT =9, /* FCS-Alternatives */
LCP_OPT_SDP =10, /* Self-Dscribing-Padding */
LCP_OPT_NBMODE =11, /* Numbered-Mode */
LCP_OPT_MULTILINK =12, /* Multi-link procedure (?) */
LCP_OPT_CALLBK =13, /* Callback */
LCP_OPT_CTTIME =14, /* Connect time */
LCP_OPT_COMPFR =15, /* Compound-Frames */
LCP_OPT_NDS =16, /* Nominal-Data-Encapsulation */
LCP_OPT_MRRU =17, /* Multi-link MRRU size */
LCP_OPT_SHSEQ =18, /* Short seq number header */
LCP_OPT_EDISC =19, /* Unique endpoint discrimiator */
LCP_OPT_PROPR =20, /* Proprietary */
LCP_OPT_DCEID =21 /* DCE-Identifier */
};
/* Supported and required FSM codes for normal LCP */
#define LCP_SUPPORTED_CODES \
(1 << FSM_CODE_CONFIGREQ) \
| (1 << FSM_CODE_CONFIGACK) \
| (1 << FSM_CODE_CONFIGNAK) \
| (1 << FSM_CODE_CONFIGREJ) \
| (1 << FSM_CODE_TERMREQ) \
| (1 << FSM_CODE_TERMACK) \
| (1 << FSM_CODE_CODEREJ) \
| (1 << FSM_CODE_PROTOREJ) \
| (1 << FSM_CODE_ECHOREQ) \
| (1 << FSM_CODE_ECHOREP) \
| (1 << FSM_CODE_DISCREQ) \
| (1 << FSM_CODE_IDENT) \
| (1 << FSM_CODE_TIMEREM)
#define LCP_REQUIRED_CODES \
(1 << FSM_CODE_CONFIGREQ) \
| (1 << FSM_CODE_CONFIGACK) \
| (1 << FSM_CODE_CONFIGNAK) \
| (1 << FSM_CODE_CONFIGREJ) \
| (1 << FSM_CODE_TERMREQ) \
| (1 << FSM_CODE_TERMACK) \
| (1 << FSM_CODE_CODEREJ) \
| (1 << FSM_CODE_PROTOREJ) \
| (1 << FSM_CODE_ECHOREQ) \
| (1 << FSM_CODE_ECHOREP)
/* Supported and required FSM codes for LCP sent over a multilink bundle */
#define MP_LCP_SUPPORTED_CODES \
(1 << FSM_CODE_CODEREJ) \
| (1 << FSM_CODE_PROTOREJ) \
| (1 << FSM_CODE_ECHOREQ) \
| (1 << FSM_CODE_ECHOREP) \
| (1 << FSM_CODE_DISCREQ) \
| (1 << FSM_CODE_IDENT) \
| (1 << FSM_CODE_TIMEREM)
#define MP_LCP_REQUIRED_CODES \
(1 << FSM_CODE_CODEREJ) \
| (1 << FSM_CODE_PROTOREJ) \
| (1 << FSM_CODE_ECHOREQ) \
| (1 << FSM_CODE_ECHOREP)
struct eid_type {
u_char min;
u_char max;
const char *desc;
};
static const struct eid_type eid_types[PPP_EID_CLASS_MAX] = {
{ 0, 0, "Null" }, /* PPP_EID_CLASS_NULL */
{ 1, PPP_EID_MAXLEN, "Local" }, /* PPP_EID_CLASS_LOCAL */
{ 4, 4, "IP" }, /* PPP_EID_CLASS_IP */
{ 6, 6, "MAC" }, /* PPP_EID_CLASS_MAC */
{ 4, PPP_EID_MAXLEN, "Magic" }, /* PPP_EID_CLASS_MAGIC */
{ 1, PPP_EID_MAXLEN, "E.164" }, /* PPP_EID_CLASS_E164 */
};
static opt_pr_t lcp_pr_eid;
/* FSM options descriptors */
const struct ppp_fsm_optdesc lcp_opt_desc[] = {
{ "Vendor", LCP_OPT_VENDOR, 4, 255, 0, NULL },
{ "MRU", LCP_OPT_MRU, 2, 2, 1, ppp_fsm_pr_int16 },
{ "ACCM", LCP_OPT_ACCMAP, 4, 4, 1, ppp_fsm_pr_hex32 },
{ "Auth", LCP_OPT_AUTH, 2, 255, 1, ppp_auth_print},
{ "Qual", LCP_OPT_QUAL, 0, 255, 0, NULL },
{ "Magic", LCP_OPT_MAGIC, 4, 4, 1, ppp_fsm_pr_hex32 },
{ "PFComp", LCP_OPT_PFCMP, 0, 0, 1, NULL },
{ "ACFComp", LCP_OPT_ACFCMP, 0, 0, 1, NULL },
{ "FCSAlt", LCP_OPT_FCSALT, 1, 1, 0, NULL },
{ "SDP", LCP_OPT_SDP, 1, 1, 0, NULL },
{ "NumMode", LCP_OPT_NBMODE, 0, 255, 0, NULL },
{ "Callback", LCP_OPT_CALLBK, 1, 255, 0, NULL },
{ "CnctTime", LCP_OPT_CTTIME, 0, 255, 0, NULL },
{ "CompFrames", LCP_OPT_COMPFR, 0, 255, 0, NULL },
{ "NDEncap", LCP_OPT_NDS, 0, 255, 0, NULL },
{ "MP-MRRU", LCP_OPT_MRRU, 2, 2, 1, ppp_fsm_pr_int16 },
{ "MP-ShortSq", LCP_OPT_SHSEQ, 0, 0, 1, NULL },
{ "EID", LCP_OPT_EDISC, 1,
1 + PPP_EID_MAXLEN, 1, lcp_pr_eid },
{ "Proprietry", LCP_OPT_PROPR, 0, 255, 0, NULL },
{ "DCE-Ident", LCP_OPT_DCEID, 0, 255, 0, NULL },
{ NULL, 0, 0, 0, 0, NULL }
};
/* Default configuration options */
static /*const*/ u_char lcp_default_mru[2] = {
(LCP_DEFAULT_MRU >> 8), (LCP_DEFAULT_MRU & 0xff)
};
static /*const*/ u_char lcp_default_accm[4] = {
0xff, 0xff, 0xff, 0xff
};
static /*const*/ u_char lcp_default_eid[1] = {
PPP_EID_CLASS_NULL
};
/* Default configuration option list */
static /*const*/ struct ppp_fsm_option lcp_opt_default_list[] = {
{ LCP_OPT_MRU, sizeof(lcp_default_mru), lcp_default_mru },
{ LCP_OPT_ACCMAP, sizeof(lcp_default_accm), lcp_default_accm },
{ LCP_OPT_EDISC, sizeof(lcp_default_eid), lcp_default_eid },
};
static const struct ppp_fsm_options lcp_opt_default = {
sizeof(lcp_opt_default_list) / sizeof(*lcp_opt_default_list),
lcp_opt_default_list
};
/* FSM type for LCP */
static ppp_fsm_type_destroy_t ppp_lcp_destroy;
static ppp_fsm_type_build_conf_req_t ppp_lcp_build_conf_req;
static ppp_fsm_type_recv_conf_req_t ppp_lcp_recv_conf_req;
static ppp_fsm_type_recv_conf_rej_t ppp_lcp_recv_conf_rej;
static ppp_fsm_type_recv_conf_nak_t ppp_lcp_recv_conf_nak;
static ppp_fsm_type_get_magic_t ppp_lcp_get_magic;
const struct ppp_fsm_type ppp_fsm_lcp = {
"LCP",
PPP_PROTO_LCP,
LCP_SUPPORTED_CODES,
LCP_REQUIRED_CODES,
lcp_opt_desc,
&lcp_opt_default,
ppp_lcp_destroy,
ppp_lcp_build_conf_req,
ppp_lcp_recv_conf_req,
ppp_lcp_recv_conf_rej,
ppp_lcp_recv_conf_nak,
ppp_lcp_get_magic,
NULL,
NULL,
NULL
};
const struct ppp_fsm_type ppp_fsm_mp_lcp = {
"LCP",
PPP_PROTO_LCP,
MP_LCP_SUPPORTED_CODES,
MP_LCP_REQUIRED_CODES,
NULL,
NULL,
ppp_lcp_destroy,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
/* LCP instance state */
struct lcp {
struct ppp_lcp_config conf; /* initial config */
struct ppp_lcp_req req; /* current request state */
};
/***********************************************************************
PUBLIC FUNCTIONS
***********************************************************************/
struct ppp_fsm_instance *
ppp_lcp_create(struct ppp_lcp_config *conf)
{
struct ppp_fsm_instance *inst;
struct ppp_lcp_req *req;
struct lcp *lcp = NULL;
int nauth[2];
int i;
/* Construct instance object */
if ((inst = MALLOC(LCP_MTYPE, sizeof(*inst))) == NULL)
return (NULL);
memset(inst, 0, sizeof(*inst));
inst->type = (conf != NULL) ? &ppp_fsm_lcp : &ppp_fsm_mp_lcp;
/* Attach private data */
if ((lcp = MALLOC(LCP_MTYPE, sizeof(*lcp))) == NULL)
goto fail;
memset(lcp, 0, sizeof(*lcp));
inst->arg = lcp;
/* No configuration required for MP LCP's */
if (conf == NULL)
goto no_conf;
/* Sanity check and normalize configuration */
lcp->conf = *conf;
conf = &lcp->conf;
for (i = 0; i < 2; i++) {
if (conf->max_mru[i] == 0)
conf->max_mru[i] = LCP_MAX_MRU;
if (conf->max_mrru[i] == 0)
conf->max_mrru[i] = LCP_MAX_MRRU;
conf->min_mru[i] = MAX(conf->min_mru[i], LCP_MIN_MRU);
conf->max_mru[i] = MIN(conf->max_mru[i], LCP_MAX_MRU);
conf->min_mrru[i] = MAX(conf->min_mrru[i], LCP_MIN_MRRU);
conf->max_mrru[i] = MIN(conf->max_mrru[i], LCP_MAX_MRRU);
}
/* Multilink cannot be both enabled and denied */
if (!conf->multilink[PPP_PEER]) {
conf->shortseq[PPP_SELF] = 0;
conf->shortseq[PPP_PEER] = 0;
if (conf->multilink[PPP_SELF]) {
errno = EINVAL;
goto fail;
}
}
/* Check MRU values are self-consistent */
if (conf->min_mru[PPP_SELF] > conf->max_mru[PPP_SELF]
|| conf->min_mru[PPP_PEER] > conf->max_mru[PPP_PEER]
|| conf->min_mrru[PPP_SELF] > conf->max_mrru[PPP_SELF]
|| conf->min_mrru[PPP_PEER] > conf->max_mrru[PPP_PEER]) {
errno = EINVAL;
goto fail;
}
/* Check MRRU values are self-consistent (if multilink is possible) */
if (conf->multilink[PPP_PEER]
&& (conf->min_mrru[PPP_SELF] > conf->max_mrru[PPP_SELF]
|| conf->min_mrru[PPP_PEER] > conf->max_mrru[PPP_PEER])) {
errno = EINVAL;
goto fail;
}
/* Sanity check EID */
if (conf->eid.class >= PPP_EID_CLASS_MAX
|| conf->eid.length < eid_types[conf->eid.class].min
|| conf->eid.length > eid_types[conf->eid.class].max) {
errno = EINVAL;
goto fail;
}
/*
* At least one type of authentication (including "none")
* must be enabled in each direction.
*/
nauth[PPP_SELF] = 0;
nauth[PPP_PEER] = 0;
for (i = 0; i < PPP_AUTH_MAX; i++) {
if (conf->auth[PPP_SELF][i] != 0)
nauth[PPP_SELF]++;
if (conf->auth[PPP_PEER][i] != 0)
nauth[PPP_PEER]++;
}
if (nauth[PPP_SELF] == 0 || nauth[PPP_PEER] == 0) {
errno = EINVAL;
goto fail;
}
/* Initialize local request state */
req = &lcp->req;
req->mru[PPP_SELF] = conf->max_mru[PPP_SELF];
req->accm[PPP_SELF] = conf->accm;
req->magic[PPP_SELF] = random() ^ time(NULL) ^ (getpid() << 16);
req->acfcomp[PPP_SELF] = conf->acfcomp[PPP_SELF];
req->pfcomp[PPP_SELF] = conf->pfcomp[PPP_SELF];
for (i = PPP_AUTH_MAX - 1; i >= 0; i--) {
if (conf->auth[PPP_SELF][i] != 0) {
req->auth[PPP_SELF] = i;
break;
}
}
req->multilink[PPP_SELF] = conf->multilink[PPP_SELF];
req->mrru[PPP_SELF] = conf->max_mrru[PPP_SELF];
req->shortseq[PPP_SELF] = conf->shortseq[PPP_SELF];
req->eid[PPP_SELF] = conf->eid;
no_conf:
/* Done */
return (inst);
fail:
/* Clean up after failure */
if (lcp != NULL)
FREE(LCP_MTYPE, lcp);
FREE(LCP_MTYPE, inst);
return (NULL);
}
/*
* Get LCP request state.
*/
void
ppp_lcp_get_req(struct ppp_fsm *fsm, struct ppp_lcp_req *req)
{
struct ppp_fsm_instance *const inst = ppp_fsm_get_instance(fsm);
struct lcp *const lcp = inst->arg;
assert(inst->type == &ppp_fsm_lcp
|| inst->type == &ppp_fsm_mp_lcp);
memcpy(req, &lcp->req, sizeof(*req));
}
/***********************************************************************
FSM CALLBACKS
***********************************************************************/
static void
ppp_lcp_destroy(struct ppp_fsm_instance *inst)
{
struct lcp *const lcp = inst->arg;
FREE(LCP_MTYPE, lcp);
FREE(LCP_MTYPE, inst);
}
static int
ppp_lcp_build_conf_req(struct ppp_fsm_instance *fsm,
struct ppp_fsm_options *opts)
{
struct lcp *const lcp = (struct lcp *)fsm->arg;
struct ppp_lcp_req *const req = &lcp->req;
u_int16_t val16;
u_int32_t val32;
/* Do MRU, ACCM, ACF compression, PF compression, and magic # */
val16 = htons(req->mru[PPP_SELF]);
if (ppp_fsm_option_add(opts, LCP_OPT_MRU, 2, &val16) == -1)
return (-1);
val32 = htonl(req->accm[PPP_SELF]);
if (ppp_fsm_option_add(opts, LCP_OPT_ACCMAP, 4, &val32) == -1)
return (-1);
if (req->acfcomp[PPP_SELF]
&& ppp_fsm_option_add(opts, LCP_OPT_ACFCMP, 0, NULL) == -1)
return (-1);
if (req->pfcomp[PPP_SELF]
&& ppp_fsm_option_add(opts, LCP_OPT_PFCMP, 0, NULL) == -1)
return (-1);
val32 = htonl(req->magic[PPP_SELF]);
if (ppp_fsm_option_add(opts, LCP_OPT_MAGIC, 4, &val32) == -1)
return (-1);
if (req->auth[PPP_SELF] != PPP_AUTH_NONE) {
const struct ppp_auth_type *const a
= ppp_auth_by_index(req->auth[PPP_SELF]);
if (ppp_fsm_option_add(opts,
LCP_OPT_AUTH, a->len, a->data) == -1)
return (-1);
}
/* Do multi-link stuff */
if (req->multilink[PPP_SELF]) {
val16 = htons(req->mrru[PPP_SELF]);
if (ppp_fsm_option_add(opts, LCP_OPT_MRRU, 2, &val16) == -1)
return (-1);
if (req->shortseq[PPP_SELF]
&& ppp_fsm_option_add(opts, LCP_OPT_SHSEQ, 0, NULL) == -1)
return (-1);
}
/* Do endpoint descriminator */
if (req->eid[PPP_SELF].class != PPP_EID_CLASS_NULL) {
u_char eid_buf[1 + PPP_EID_MAXLEN];
eid_buf[0] = req->eid[PPP_SELF].class;
memcpy(eid_buf + 1,
req->eid[PPP_SELF].value, req->eid[PPP_SELF].length);
if (ppp_fsm_option_add(opts, LCP_OPT_EDISC,
1 + req->eid[PPP_SELF].length, eid_buf) == -1)
return (-1);
}
/* Done */
return (0);
}
static int
ppp_lcp_recv_conf_req(struct ppp_fsm_instance *fsm, struct ppp_fsm_options *crq,
struct ppp_fsm_options *nak, struct ppp_fsm_options *rej)
{
struct lcp *const lcp = (struct lcp *)fsm->arg;
struct ppp_lcp_config *const conf = &lcp->conf;
struct ppp_lcp_req *const req = &lcp->req;
int i;
/* Initialize peer's request state */
req->mru[PPP_PEER] = LCP_DEFAULT_MRU;
req->accm[PPP_PEER] = ~0;
req->acfcomp[PPP_PEER] = 0;
req->pfcomp[PPP_PEER] = 0;
req->magic[PPP_PEER] = 0;
req->auth[PPP_PEER] = PPP_AUTH_NONE;
req->mrru[PPP_PEER] = LCP_DEFAULT_MRRU;
req->multilink[PPP_PEER] = 0;
req->shortseq[PPP_PEER] = 0;
req->eid[PPP_PEER].class = PPP_EID_CLASS_NULL;
req->eid[PPP_PEER].length = 0;
/* Process options */
for (i = 0; i < crq->num; i++) {
const struct ppp_fsm_option *const opt = &crq->opts[i];
switch (opt->type) {
case LCP_OPT_MRU:
{
u_int16_t mru;
memcpy(&mru, opt->data, 2);
mru = ntohs(mru);
if (mru < conf->min_mru[PPP_PEER]) {
mru = htons(conf->min_mru[PPP_PEER]);
if (ppp_fsm_option_add(nak, opt->type,
sizeof(mru), &mru) == -1)
return (-1);
break;
}
if (mru > conf->max_mru[PPP_PEER]) {
mru = htons(conf->max_mru[PPP_PEER]);
if (ppp_fsm_option_add(nak, opt->type,
sizeof(mru), &mru) == -1)
return (-1);
break;
}
req->mru[PPP_PEER] = mru;
break;
}
case LCP_OPT_ACCMAP:
{
memcpy(&req->accm[PPP_PEER], opt->data, 4);
req->accm[PPP_PEER] = ntohl(req->accm[PPP_PEER]);
break;
}
case LCP_OPT_PFCMP:
{
if (!conf->pfcomp[PPP_PEER])
goto reject;
req->pfcomp[PPP_PEER] = 1;
break;
}
case LCP_OPT_ACFCMP:
{
if (!conf->acfcomp[PPP_PEER])
goto reject;
req->acfcomp[PPP_PEER] = 1;
break;
}
case LCP_OPT_MAGIC:
{
u_int32_t magic;
memcpy(&magic, opt->data, 4);
magic = ntohl(magic);
if (magic == req->magic[PPP_SELF]) {
errno = ELOOP; /* indicate loopback */
return (-1);
}
req->magic[PPP_PEER] = magic;
break;
}
case LCP_OPT_AUTH:
{
const struct ppp_auth_type *a = ppp_auth_by_option(opt);
int i;
/* Sanity check */
if (a == NULL)
break;
/* Check if we accept peer's auth proposal */
if (conf->auth[PPP_PEER][a->index] != 0) {
req->auth[PPP_PEER] = a->index;
break;
}
/* See what other type we can do and nak with it */
for (i = 1; i < PPP_AUTH_MAX; i++) {
if (conf->auth[PPP_PEER][i] != 0) {
a = ppp_auth_by_index(i);
if (ppp_fsm_option_add(nak, opt->type,
a->len, a->data) == -1)
return (-1);
break;
}
}
/* No auth type is acceptable */
goto reject;
}
case LCP_OPT_MRRU:
{
u_int16_t mrru;
if (!conf->multilink[PPP_PEER])
goto reject;
memcpy(&mrru, opt->data, 2);
mrru = ntohs(mrru);
if (mrru < conf->min_mrru[PPP_PEER]) {
mrru = htons(conf->min_mrru[PPP_PEER]);
if (ppp_fsm_option_add(nak,
opt->type, 2, &mrru) == -1)
return (-1);
break;
}
if (mrru > conf->max_mrru[PPP_PEER]) {
mrru = htons(conf->max_mrru[PPP_PEER]);
if (ppp_fsm_option_add(nak,
opt->type, 2, &mrru) == -1)
return (-1);
break;
}
req->multilink[PPP_PEER] = 1;
req->multilink[PPP_SELF] = 1;
req->mrru[PPP_PEER] = mrru;
break;
}
case LCP_OPT_SHSEQ:
{
if (!conf->multilink[PPP_PEER]
|| !conf->shortseq[PPP_PEER])
goto reject;
req->shortseq[PPP_PEER] = 1;
break;
}
case LCP_OPT_EDISC:
{
struct ppp_eid eid;
if (opt->len < 1)
goto reject;
eid.class = opt->data[0];
eid.length = opt->len - 1;
if (eid.class >= PPP_EID_CLASS_MAX
|| eid.length < eid_types[eid.class].min
|| eid.length > eid_types[eid.class].max)
goto reject;
memcpy(eid.value, opt->data + 1, eid.length);
req->eid[PPP_PEER] = eid;
break;
}
default:
goto reject;
}
/* OK */
continue;
reject:
/* Reject this requested option */
if (ppp_fsm_option_add(rej,
opt->type, opt->len, opt->data) == -1)
return (-1);
}
/* If no auth requested, but we require auth, nak for it */
if (req->auth[PPP_PEER] == PPP_AUTH_NONE
&& !conf->auth[PPP_PEER][PPP_AUTH_NONE]) {
for (i = 1; i < PPP_AUTH_MAX; i++) {
if (conf->auth[PPP_PEER][i] != 0) {
const struct ppp_auth_type *const a
= ppp_auth_by_index(i);
if (ppp_fsm_option_add(nak,
LCP_OPT_AUTH, a->len, a->data) == -1)
return (-1);
}
}
}
/* Do same for multilink? */
/* Done */
return (0);
}
static int
ppp_lcp_recv_conf_rej(struct ppp_fsm_instance *fsm, struct ppp_fsm_options *rej)
{
struct lcp *const lcp = (struct lcp *)fsm->arg;
struct ppp_lcp_config *const conf = &lcp->conf;
struct ppp_lcp_req *const req = &lcp->req;
int i;
for (i = 0; i < rej->num; i++) {
const struct ppp_fsm_option *const opt = &rej->opts[i];
switch (opt->type) {
case LCP_OPT_MRU:
req->mru[PPP_SELF] = LCP_DEFAULT_MRU;
break;
case LCP_OPT_ACCMAP:
req->accm[PPP_SELF] = ~0;
break;
case LCP_OPT_PFCMP:
req->pfcomp[PPP_SELF] = 0;
break;
case LCP_OPT_ACFCMP:
req->acfcomp[PPP_SELF] = 0;
break;
case LCP_OPT_MAGIC:
req->magic[PPP_SELF] = 0;
break;
case LCP_OPT_AUTH:
{
const struct ppp_auth_type *a = ppp_auth_by_option(opt);
int i;
/* Sanity check */
if (a == NULL)
break;
/* Mark this auth type as rejected by peer */
req->auth_rej[a->index] = 1;
/* Find next acceptable type not rejected by peer */
for (i = 0; i < PPP_AUTH_MAX; i++) {
if (conf->auth[PPP_SELF][i]
&& !req->auth_rej[i]) {
req->auth[PPP_SELF] = i;
break;
}
}
/* None found? Can't continue */
if (i == PPP_AUTH_MAX) {
errno = EINVAL;
return (-1);
}
break;
}
case LCP_OPT_MRRU:
req->multilink[PPP_SELF] = 0;
/* fall through */
case LCP_OPT_SHSEQ:
req->shortseq[PPP_SELF] = 0;
break;
case LCP_OPT_EDISC:
req->eid[PPP_SELF].class = PPP_EID_CLASS_NULL;
req->eid[PPP_SELF].length = 0;
break;
default:
break;
}
}
/* Done */
return (0);
}
static int
ppp_lcp_recv_conf_nak(struct ppp_fsm_instance *fsm, struct ppp_fsm_options *nak)
{
struct lcp *const lcp = (struct lcp *)fsm->arg;
struct ppp_lcp_config *const conf = &lcp->conf;
struct ppp_lcp_req *const req = &lcp->req;
int i;
for (i = 0; i < nak->num; i++) {
const struct ppp_fsm_option *const opt = &nak->opts[i];
switch (opt->type) {
case LCP_OPT_MRU:
{
u_int16_t mru;
memcpy(&mru, opt->data, 2);
mru = ntohs(mru);
if (mru < conf->min_mru[PPP_SELF])
mru = conf->min_mru[PPP_SELF];
if (mru > conf->max_mru[PPP_SELF])
mru = conf->max_mru[PPP_SELF];
req->mru[PPP_SELF] = mru;
break;
}
case LCP_OPT_ACCMAP:
{
memcpy(&req->accm[PPP_SELF], opt->data, 4);
req->accm[PPP_SELF] = ntohl(req->accm[PPP_SELF]);
break;
}
case LCP_OPT_PFCMP:
{
if (conf->pfcomp[PPP_SELF])
req->pfcomp[PPP_SELF] = 1;
break;
}
case LCP_OPT_ACFCMP:
{
if (conf->acfcomp[PPP_SELF])
req->acfcomp[PPP_SELF] = 1;
break;
}
case LCP_OPT_AUTH:
{
const struct ppp_auth_type *a = ppp_auth_by_option(opt);
if (a != NULL && conf->auth[PPP_SELF][a->index])
req->auth[PPP_SELF] = a->index;
break;
}
case LCP_OPT_MRRU:
{
u_int16_t mrru;
memcpy(&mrru, opt->data, 2);
mrru = ntohs(mrru);
if (mrru < conf->min_mrru[PPP_SELF])
mrru = conf->min_mrru[PPP_SELF];
if (mrru > conf->max_mrru[PPP_SELF])
mrru = conf->max_mrru[PPP_SELF];
req->mrru[PPP_SELF] = mrru;
break;
}
case LCP_OPT_SHSEQ:
{
if (conf->shortseq[PPP_SELF])
req->shortseq[PPP_SELF] = 1;
break;
}
default:
break;
}
}
/* Done */
return (0);
}
static u_int32_t
ppp_lcp_get_magic(struct ppp_fsm_instance *fsm, int dir)
{
struct lcp *const lcp = (struct lcp *)fsm->arg;
struct ppp_lcp_req *const req = &lcp->req;
return (req->magic[dir]);
}
/***********************************************************************
INTERNAL FUNCTIONS
***********************************************************************/
static void
lcp_pr_eid(const struct ppp_fsm_optdesc *desc,
const struct ppp_fsm_option *opt, char *buf, size_t bmax)
{
const struct eid_type *type;
struct ppp_eid eid;
int i;
if (opt->len < 1) {
strlcpy(buf, "<truncated>", bmax);
return;
}
eid.class = opt->data[0];
if (eid.class >= PPP_EID_CLASS_MAX) {
snprintf(buf, bmax, "<invalid class %u>", eid.class);
return;
}
type = &eid_types[eid.class];
eid.length = opt->len - 1;
if (eid.length < type->min || eid.length > type->max) {
snprintf(buf, bmax,
"[%s] <invalid length %d>", type->desc, eid.length);
return;
}
memcpy(eid.value, opt->data + 1, eid.length);
snprintf(buf, bmax, "%s:", type->desc);
switch (eid.class) {
case PPP_EID_CLASS_NULL:
break;
case PPP_EID_CLASS_IP:
{
struct in_addr ip;
memcpy(&ip, eid.value, sizeof(ip));
strlcpy(buf + strlen(buf), inet_ntoa(ip), bmax - strlen(buf));
break;
}
case PPP_EID_CLASS_LOCAL:
for (i = 0; i < eid.length
&& (isprint(eid.value[i]) || isspace(eid.value[i])); i++);
if (i == eid.length) {
ppp_util_ascify(buf + strlen(buf),
bmax - strlen(buf), eid.value, eid.length);
break;
}
/* FALL THROUGH */
default:
for (i = 0; i < eid.length; i++) {
snprintf(buf + strlen(buf), bmax - strlen(buf),
"%s%02x", (i > 0) ? ":" : "", eid.value[i]);
}
break;
}
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>