File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / ppp / ppp_ipcp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 3 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * 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_log.h"
#include "ppp/ppp_fsm_option.h"
#include "ppp/ppp_fsm.h"
#include "ppp/ppp_auth.h"
#include "ppp/ppp_ipcp.h"

/* Whether to do VJC compressed CID's */
#define IPCP_ALLOW_SELF_COMPCID	0	/* whether we allow to recv */
#define IPCP_ALLOW_PEER_COMPCID	1	/* whether we allow to send */

/* Memory type */
#define IPCP_MTYPE		"ipcp"

/* IPCP configuration options */
enum ipcp_option {
	IPCP_OPT_IP		=3,	/* ip address */
	IPCP_OPT_COMP		=2,	/* compression */
	IPCP_OPT_DNS1		=129,	/* primary dns */
	IPCP_OPT_DNS2		=131,	/* secondary dns */
	IPCP_OPT_NBNS1		=130,	/* primary nbns */
	IPCP_OPT_NBNS2		=132,	/* secondary nbns */
};

static const u_char ipcp_dns_opts[2] = { IPCP_OPT_DNS1, IPCP_OPT_DNS2 };
static const u_char ipcp_nbns_opts[2] = { IPCP_OPT_NBNS1, IPCP_OPT_NBNS2 };

/* Supported and required FSM codes */
#define IPCP_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)
#define IPCP_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)

/* FSM options descriptors */
static opt_pr_t	ppp_ipcp_pr_ip;
static opt_pr_t	ppp_ipcp_pr_ipcomp;

static const	struct ppp_fsm_optdesc ipcp_opt_desc[] = {
	{ "IP-Addr",	IPCP_OPT_IP,	4, 4,	1,	ppp_ipcp_pr_ip },
	{ "IP-Comp",	IPCP_OPT_COMP,	4, 4,	1,	ppp_ipcp_pr_ipcomp },
	{ "DNS1",	IPCP_OPT_DNS1,	4, 4,	1,	ppp_ipcp_pr_ip },
	{ "DNS2",	IPCP_OPT_DNS2,	4, 4,	1,	ppp_ipcp_pr_ip },
	{ "NBNS1",	IPCP_OPT_NBNS1,	4, 4,	1,	ppp_ipcp_pr_ip },
	{ "NBNS2",	IPCP_OPT_NBNS2,	4, 4,	1,	ppp_ipcp_pr_ip },
	{ NULL,		0,		0, 0,	0,	NULL }
};

/* FSM type for IPCP */
static ppp_fsm_type_destroy_t		ppp_ipcp_destroy;
static ppp_fsm_type_build_conf_req_t	ppp_ipcp_build_conf_req;
static ppp_fsm_type_recv_conf_req_t	ppp_ipcp_recv_conf_req;
static ppp_fsm_type_recv_conf_rej_t	ppp_ipcp_recv_conf_rej;
static ppp_fsm_type_recv_conf_nak_t	ppp_ipcp_recv_conf_nak;

const struct	ppp_fsm_type ppp_fsm_ipcp = {
	"IPCP",
	PPP_PROTO_IPCP,
	IPCP_SUPPORTED_CODES,
	IPCP_REQUIRED_CODES,
	ipcp_opt_desc,
	NULL,
	ppp_ipcp_destroy,
	ppp_ipcp_build_conf_req,
	ppp_ipcp_recv_conf_req,
	ppp_ipcp_recv_conf_rej,
	ppp_ipcp_recv_conf_nak,
	NULL,
	NULL,
	NULL,
	NULL
};

/* IPCP instance state */
struct ipcp {
	struct ppp_ipcp_config	conf;		/* initial config */
	struct ppp_ipcp_req	req;		/* current request state */
	struct ppp_node		*node;		/* ng_ppp(4) node */
};

/* VJC compression header */
struct ipcp_vjc {
	u_int16_t	proto;
	u_char		maxchan;
	u_char		compcid;
};

/***********************************************************************
			PUBLIC FUNCTIONS
***********************************************************************/

struct ppp_fsm_instance *
ppp_ipcp_create(struct ppp_ipcp_config *conf, struct ppp_node *node)
{
	struct ppp_fsm_instance *inst;
	struct ppp_ipcp_req *req;
	struct ipcp *ipcp = NULL;

	/* Construct instance object */
	if ((inst = MALLOC(IPCP_MTYPE, sizeof(*inst))) == NULL)
		return (NULL);
	memset(inst, 0, sizeof(*inst));
	inst->type = &ppp_fsm_ipcp;

	/* Attach private data */
	if ((ipcp = MALLOC(IPCP_MTYPE, sizeof(*ipcp))) == NULL)
		goto fail;
	memset(ipcp, 0, sizeof(*ipcp));
	ipcp->conf = *conf;
	ipcp->node = node;
	inst->arg = ipcp;

	/* Initialize local request state */
	req = &ipcp->req;
	req->ip[PPP_SELF] = conf->ip[PPP_SELF];
	req->vjc[PPP_SELF].enabled = 1;
	req->vjc[PPP_SELF].maxchan = NG_VJC_MAX_CHANNELS - 1;
	req->vjc[PPP_SELF].compcid = IPCP_ALLOW_SELF_COMPCID;
	req->ask_dns = conf->do_dns[PPP_SELF];
	req->ask_nbns = conf->do_nbns[PPP_SELF];

	/* Done */
	return (inst);

fail:
	/* Clean up after failure */
	if (ipcp != NULL)
		FREE(IPCP_MTYPE, ipcp);
	FREE(IPCP_MTYPE, inst);
	return (NULL);
}

/*
 * Get IPCP request state.
 */
void
ppp_ipcp_get_req(struct ppp_fsm *fsm, struct ppp_ipcp_req *req)
{
	struct ppp_fsm_instance *const inst = ppp_fsm_get_instance(fsm);
	struct ipcp *const ipcp = inst->arg;

	assert(inst->type == &ppp_fsm_ipcp);
	memcpy(req, &ipcp->req, sizeof(*req));
}

/***********************************************************************
			FSM CALLBACKS
***********************************************************************/

static void
ppp_ipcp_destroy(struct ppp_fsm_instance *inst)
{
	struct ipcp *const ipcp = inst->arg;

	FREE(IPCP_MTYPE, ipcp);
	FREE(IPCP_MTYPE, inst);
}

static int
ppp_ipcp_build_conf_req(struct ppp_fsm_instance *fsm,
	struct ppp_fsm_options *opts)
{
	struct ipcp *const ipcp = (struct ipcp *)fsm->arg;
	struct ppp_ipcp_req *const req = &ipcp->req;
	int i;

	/* Add requested config options */
	if (ppp_fsm_option_add(opts, IPCP_OPT_IP, 4, &req->ip[PPP_SELF]) == -1)
		return (-1);
	if (req->vjc[PPP_SELF].enabled) {
		struct ipcp_vjc vjc;

		vjc.proto = htons(PPP_PROTO_VJCOMP);
		vjc.maxchan = req->vjc[PPP_SELF].maxchan;
		vjc.compcid = req->vjc[PPP_SELF].compcid;
		if (ppp_fsm_option_add(opts,
		    IPCP_OPT_COMP, sizeof(vjc), &vjc) == -1)
			return (-1);
	}
	if (req->ask_dns) {
		for (i = 0; i < 2; i++) {
			if (ppp_fsm_option_add(opts,
			    ipcp_dns_opts[i], 4, &req->dns[i]) == -1)
				return (-1);
		}
	}
	if (req->ask_nbns) {
		for (i = 0; i < 2; i++) {
			if (ppp_fsm_option_add(opts,
			    ipcp_nbns_opts[i], 4, &req->nbns[i]) == -1)
				return (-1);
		}
	}

	/* Done */
	return (0);
}

static int
ppp_ipcp_recv_conf_req(struct ppp_fsm_instance *fsm,
	struct ppp_fsm_options *crq, struct ppp_fsm_options *nak,
	struct ppp_fsm_options *rej)
{
	struct ipcp *const ipcp = (struct ipcp *)fsm->arg;
	struct ppp_ipcp_config *const conf = &ipcp->conf;
	struct ppp_ipcp_req *const req = &ipcp->req;
	int saw_ip = 0;
	int i;

	/* Initialize peer's request state */
	req->ip[PPP_PEER].s_addr = 0;
	memset(&req->vjc[PPP_PEER], 0, sizeof(req->vjc[PPP_PEER]));

	/* Process options */
	for (i = 0; i < crq->num; i++) {
		const struct ppp_fsm_option *const opt = &crq->opts[i];

		switch (opt->type) {
		case IPCP_OPT_IP:
		    {
			struct in_addr ip;

			saw_ip = 1;
			memcpy(&ip, opt->data, 4);
			if ((ip.s_addr & conf->mask[PPP_PEER].s_addr)
			    != (conf->ip[PPP_PEER].s_addr
			      & conf->mask[PPP_PEER].s_addr)) {
				if (ppp_fsm_option_add(nak, opt->type,
				    4, &conf->ip[PPP_PEER]) == -1)
					return (-1);
				break;
			}
			req->ip[PPP_PEER] = ip;
			break;
		    }
		case IPCP_OPT_COMP:
		    {
			struct ipcp_vjc vjc;
			int nakit = 0;

			memcpy(&vjc, opt->data, sizeof(vjc));
			if (ntohs(vjc.proto) != PPP_PROTO_VJCOMP)
				goto reject;
			if (vjc.maxchan < NG_VJC_MIN_CHANNELS - 1) {
				vjc.maxchan = NG_VJC_MIN_CHANNELS - 1;
				nakit = 1;
			}
			if (vjc.maxchan > NG_VJC_MAX_CHANNELS - 1) {
				vjc.maxchan = NG_VJC_MAX_CHANNELS - 1;
				nakit = 1;
			}
#if !IPCP_ALLOW_PEER_COMPCID
			if (vjc.compcid) {
				vjc.compcid = 0;
				nakit = 1;
			}
#endif
			if (nakit) {
				if (ppp_fsm_option_add(nak,
				    opt->type, sizeof(vjc), &vjc) == -1)
					return (-1);
				break;
			}
			req->vjc[PPP_PEER].enabled = 1;
			req->vjc[PPP_PEER].maxchan = vjc.maxchan;
			req->vjc[PPP_PEER].compcid = vjc.compcid;
			break;
		    }
		case IPCP_OPT_DNS1:
		case IPCP_OPT_DNS2:
		    {
			const int i = (opt->type == IPCP_OPT_DNS2);
			struct in_addr ip;

			if (!conf->do_dns[PPP_PEER]
			    || conf->dns[i].s_addr == 0)
				goto reject;
			memcpy(&ip, opt->data, 4);
			if (ip.s_addr != conf->dns[i].s_addr) {
				if (ppp_fsm_option_add(nak, opt->type,
				    4, &conf->dns[i]) == -1)
					return (-1);
				break;
			}
			break;
		    }
		case IPCP_OPT_NBNS1:
		case IPCP_OPT_NBNS2:
		    {
			const int i = (opt->type == IPCP_OPT_NBNS2);
			struct in_addr ip;

			if (!conf->do_nbns[PPP_PEER]
			    || conf->nbns[i].s_addr == 0)
				goto reject;
			memcpy(&ip, opt->data, 4);
			if (ip.s_addr != conf->nbns[i].s_addr) {
				if (ppp_fsm_option_add(nak, opt->type,
				    4, &conf->nbns[i]) == -1)
					return (-1);
				break;
			}
			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);
	}

	/* Make sure we saw an IP address option */
	if (!saw_ip) {
		errno = EINVAL;
		return (-1);
	}

	/* Done */
	return (0);
}

static int
ppp_ipcp_recv_conf_rej(struct ppp_fsm_instance *fsm,
	struct ppp_fsm_options *rej)
{
	struct ipcp *const ipcp = (struct ipcp *)fsm->arg;
	struct ppp_ipcp_req *const req = &ipcp->req;
	int i;

	for (i = 0; i < rej->num; i++) {
		const struct ppp_fsm_option *const opt = &rej->opts[i];

		switch (opt->type) {
		case IPCP_OPT_IP:
			errno = EINVAL;
			return (-1);
		case IPCP_OPT_COMP:
			req->vjc[PPP_SELF].enabled = 0;
			break;
		case IPCP_OPT_DNS1:
		case IPCP_OPT_DNS2:
			req->ask_dns = 0;
			break;
		case IPCP_OPT_NBNS1:
		case IPCP_OPT_NBNS2:
			req->ask_nbns = 0;
			break;
		default:
			break;
		}
	}

	/* Done */
	return (0);
}

static int
ppp_ipcp_recv_conf_nak(struct ppp_fsm_instance *fsm,
	struct ppp_fsm_options *nak)
{
	struct ipcp *const ipcp = (struct ipcp *)fsm->arg;
	struct ppp_ipcp_config *const conf = &ipcp->conf;
	struct ppp_ipcp_req *const req = &ipcp->req;
	int i;

	for (i = 0; i < nak->num; i++) {
		const struct ppp_fsm_option *const opt = &nak->opts[i];

		switch (opt->type) {
		case IPCP_OPT_IP:
		    {
			struct in_addr ip;

			memcpy(&ip, opt->data, 4);
			if ((ip.s_addr & conf->mask[PPP_SELF].s_addr)
			    != (conf->ip[PPP_SELF].s_addr
			      & conf->mask[PPP_SELF].s_addr))
				break;
			req->ip[PPP_SELF] = ip;
			break;
		    }
		case IPCP_OPT_COMP:
		    {
			struct ipcp_vjc vjc;

			memcpy(&vjc, opt->data, sizeof(vjc));
			if (ntohs(vjc.proto) != PPP_PROTO_VJCOMP)
				break;
			if (vjc.maxchan < NG_VJC_MIN_CHANNELS - 1)
				vjc.maxchan = NG_VJC_MIN_CHANNELS - 1;
			if (vjc.maxchan > NG_VJC_MAX_CHANNELS - 1)
				vjc.maxchan = NG_VJC_MAX_CHANNELS - 1;
			req->vjc[PPP_SELF].maxchan = vjc.maxchan;
#if IPCP_ALLOW_SELF_COMPCID
			if (vjc.compcid)
				req->vjc[PPP_SELF].compcid = 1;
#endif
			break;
		    }
		case IPCP_OPT_DNS1:
		case IPCP_OPT_DNS2:
		    {
			const int i = (opt->type == IPCP_OPT_DNS2);

			memcpy(&req->dns[i], opt->data, 4);
			break;
		    }
		case IPCP_OPT_NBNS1:
		case IPCP_OPT_NBNS2:
		    {
			const int i = (opt->type == IPCP_OPT_NBNS2);

			memcpy(&req->nbns[i], opt->data, 4);
			break;
		    }
		default:
			break;
		}
	}

	/* Done */
	return (0);
}

/***********************************************************************
			INTERNAL FUNCTIONS
***********************************************************************/

static void
ppp_ipcp_pr_ip(const struct ppp_fsm_optdesc *desc,
	const struct ppp_fsm_option *opt, char *buf, size_t bmax)
{
	struct in_addr ip;

	if (opt->len < 4) {
		snprintf(buf, bmax, "<truncated>");
		return;
	}
	memcpy(&ip, opt->data, 4);
	snprintf(buf, bmax, "%s", inet_ntoa(ip));
}

static void
ppp_ipcp_pr_ipcomp(const struct ppp_fsm_optdesc *desc,
	const struct ppp_fsm_option *opt, char *buf, size_t bmax)
{
	struct ipcp_vjc vjc;

	if (opt->len < 2) {
		snprintf(buf, bmax, "<truncated>");
		return;
	}
	memcpy(&vjc, opt->data, 2);
	if (ntohs(vjc.proto) != PPP_PROTO_VJCOMP) {
		snprintf(buf, bmax, "?0x%04x", ntohs(vjc.proto));
		return;
	}
	if (opt->len < sizeof(vjc)) {
		snprintf(buf, bmax, "VJCOMP %s", "<truncated>");
		return;
	}
	memcpy(&vjc, opt->data, sizeof(vjc));
	snprintf(buf, bmax, "VJCOMP %d channels, %s comp-cid",
	    vjc.maxchan + 1, vjc.compcid ? "allow" : "no");
}


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>