File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / ipcp.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:39:23 2021 UTC (3 years, 2 months ago) by misho
Branches: mpd, MAIN
CVS tags: v5_9p16, v5_9, HEAD
mpd 5.9


/*
 * ipcp.c
 *
 * Written by Toshiharu OHNO <tony-o@iij.ad.jp>
 * Copyright (c) 1993, Internet Initiative Japan, Inc. All rights reserved.
 * See ``COPYRIGHT.iij''
 * 
 * Rewritten by Archie Cobbs <archie@freebsd.org>
 * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
 * See ``COPYRIGHT.whistle''
 */

#include "ppp.h"
#include "ipcp.h"
#include "fsm.h"
#include "ip.h"
#include "iface.h"
#include "msg.h"
#include "ngfunc.h"
#include "ippool.h"
#include "util.h"

#include <netgraph.h>
#include <sys/mbuf.h>
#ifdef USE_NG_VJC
#include <net/slcompress.h>
#include <netgraph/ng_vjc.h>
#endif

/*
 * DEFINITIONS
 */

  #define IPCP_KNOWN_CODES	(   (1 << CODE_CONFIGREQ)	\
				  | (1 << CODE_CONFIGACK)	\
				  | (1 << CODE_CONFIGNAK)	\
				  | (1 << CODE_CONFIGREJ)	\
				  | (1 << CODE_TERMREQ)		\
				  | (1 << CODE_TERMACK)		\
				  | (1 << CODE_CODEREJ)		)

  #define TY_IPADDRS		1
  #define TY_COMPPROTO		2
  #define TY_IPADDR		3
  #define TY_MOBILEIP		4
  #define TY_PRIMARYDNS		129
  #define TY_PRIMARYNBNS	130
  #define TY_SECONDARYDNS	131
  #define TY_SECONDARYNBNS	132

  /* Keep sync with above */
  #define o2b(x)		(((x)<128)?(x):(x)-128+3)

  #define IPCP_REJECTED(p,x)	((p)->peer_reject & (1<<o2b(x)))
  #define IPCP_PEER_REJ(p,x)	do{(p)->peer_reject |= (1<<o2b(x));}while(0)

#ifdef USE_NG_VJC
  #define IPCP_VJCOMP_MIN_MAXCHAN	(NG_VJC_MIN_CHANNELS - 1)
  #define IPCP_VJCOMP_MAX_MAXCHAN	(NG_VJC_MAX_CHANNELS - 1)
  #define IPCP_VJCOMP_DEFAULT_MAXCHAN	IPCP_VJCOMP_MAX_MAXCHAN
#endif

  /* Set menu options */
  enum {
    SET_RANGES,
    SET_ENABLE,
    SET_DNS,
    SET_NBNS,
    SET_DISABLE,
    SET_ACCEPT,
    SET_DENY,
    SET_YES,
    SET_NO
  };

/*
 * INTERNAL FUNCTIONS
 */

  static void	IpcpConfigure(Fsm fp);
  static void	IpcpUnConfigure(Fsm fp);

  static u_char	*IpcpBuildConfigReq(Fsm fp, u_char *cp);
  static void	IpcpDecodeConfig(Fsm fp, FsmOption list, int num, int mode);
  static void	IpcpLayerStart(Fsm fp);
  static void	IpcpLayerFinish(Fsm fp);
  static void	IpcpLayerUp(Fsm fp);
  static void	IpcpLayerDown(Fsm fp);
  static void	IpcpFailure(Fsm fp, enum fsmfail reason);

#ifdef USE_NG_VJC
  static int	IpcpNgInitVJ(Bund b);
  static void	IpcpNgShutdownVJ(Bund b);
#endif

  static int	IpcpSetCommand(Context ctx, int ac, const char *const av[], const void *arg);

/*
 * GLOBAL VARIABLES
 */

  const struct cmdtab IpcpSetCmds[] = {
    { "ranges {self}[/{width}]|ippool {pool} {peer}[/{width}]|ippool {pool}",	"Allowed IP address ranges",
	IpcpSetCommand, NULL, 2, (void *) SET_RANGES },
    { "enable [opt ...]",		"Enable option",
	IpcpSetCommand, NULL, 2, (void *) SET_ENABLE},
    { "dns primary [secondary]",	"Set peer DNS servers",
	IpcpSetCommand, NULL, 2, (void *) SET_DNS},
    { "nbns primary [secondary]",	"Set peer NBNS servers",
	IpcpSetCommand, NULL, 2, (void *) SET_NBNS},
    { "disable [opt ...]",		"Disable option",
	IpcpSetCommand, NULL, 2, (void *) SET_DISABLE},
    { "accept [opt ...]",		"Accept option",
	IpcpSetCommand, NULL, 2, (void *) SET_ACCEPT},
    { "deny [opt ...]",			"Deny option",
	IpcpSetCommand, NULL, 2, (void *) SET_DENY},
    { "yes [opt ...]",			"Enable and accept option",
	IpcpSetCommand, NULL, 2, (void *) SET_YES},
    { "no [opt ...]",			"Disable and deny option",
	IpcpSetCommand, NULL, 2, (void *) SET_NO},
    { NULL, NULL, NULL, NULL, 0, NULL },
  };

/*
 * INTERNAL VARIABLES
 */

  static const struct fsmoptinfo	gIpcpConfOpts[] = {
    { "IPADDRS",	TY_IPADDRS,		8, 8, FALSE },
#ifdef USE_NG_VJC
    { "COMPPROTO",	TY_COMPPROTO,		4, 4, TRUE },
#endif
    { "IPADDR",		TY_IPADDR,		4, 4, TRUE },
    { "MOBILEIP",	TY_MOBILEIP,		6, 6, FALSE },
    { "PRIDNS",		TY_PRIMARYDNS,		4, 4, TRUE },
    { "PRINBNS",	TY_PRIMARYNBNS,		4, 4, TRUE },
    { "SECDNS",		TY_SECONDARYDNS,	4, 4, TRUE },
    { "SECNBNS",	TY_SECONDARYNBNS,	4, 4, TRUE },
    { NULL, 0, 0, 0, 0 }
  };

  static const struct confinfo gConfList[] = {
#ifdef USE_NG_VJC
    { 1,	IPCP_CONF_VJCOMP,	"vjcomp"	},
#endif
    { 0,	IPCP_CONF_REQPRIDNS,	"req-pri-dns"	},
    { 0,	IPCP_CONF_REQSECDNS,	"req-sec-dns"	},
    { 0,	IPCP_CONF_REQPRINBNS,	"req-pri-nbns"	},
    { 0,	IPCP_CONF_REQSECNBNS,	"req-sec-nbns"	},
    { 0,	IPCP_CONF_PRETENDIP,	"pretend-ip"	},
    { 0,	0,			NULL		},
  };

  static const struct fsmtype gIpcpFsmType = {
    "IPCP",
    PROTO_IPCP,
    IPCP_KNOWN_CODES,
    FALSE,
    LG_IPCP, LG_IPCP2,
    NULL,
    IpcpLayerUp,
    IpcpLayerDown,
    IpcpLayerStart,
    IpcpLayerFinish,
    IpcpBuildConfigReq,
    IpcpDecodeConfig,
    IpcpConfigure,
    IpcpUnConfigure,
    NULL,
    NULL,
    NULL,
    NULL,
    IpcpFailure,
    NULL,
    NULL,
    NULL, NULL, NULL, NULL
  };

/*
 * IpcpStat()
 */

int
IpcpStat(Context ctx, int ac, const char *const av[], const void *arg)
{
#ifdef USE_NG_VJC
  char			path[NG_PATHSIZ];
#endif
  IpcpState		const ipcp = &ctx->bund->ipcp;
  Fsm			fp = &ipcp->fsm;
#ifdef USE_NG_VJC
  union {
      u_char		buf[sizeof(struct ng_mesg) + sizeof(struct slcompress)];
      struct ng_mesg	reply;
  }			u;
  struct slcompress	*const sls = (struct slcompress *)(void *)u.reply.data;
#endif
  char			buf[48];

  (void)ac;
  (void)av;
  (void)arg;

  Printf("[%s] %s [%s]\r\n", Pref(fp), Fsm(fp), FsmStateName(fp->state));
  Printf("Allowed IP address ranges:\r\n");
    if (ipcp->conf.self_ippool[0]) {
	Printf("\tPeer: ippool %s\r\n",
	  ipcp->conf.self_ippool);
    } else {
	Printf("\tSelf: %s\r\n",
	    u_rangetoa(&ipcp->conf.self_allow,buf,sizeof(buf)));
    }
    if (ipcp->conf.ippool[0]) {
	Printf("\tPeer: ippool %s\r\n",
	  ipcp->conf.ippool);
    } else {
	Printf("\tPeer: %s\r\n",
	  u_rangetoa(&ipcp->conf.peer_allow,buf,sizeof(buf)));
    }
  Printf("IPCP Options:\r\n");
  OptStat(ctx, &ipcp->conf.options, gConfList);
  Printf("Current addressing:\r\n");
  Printf("\tSelf: %s\r\n", inet_ntoa(ipcp->want_addr));
  Printf("\tPeer: %s\r\n", inet_ntoa(ipcp->peer_addr));
#ifdef USE_NG_VJC
  Printf("Compression:\r\n");
  Printf("\tSelf: ");
  if (ipcp->want_comp.proto != 0)
    Printf("%s, %d compression channels, CID %scompressible\r\n",
      ProtoName(ntohs(ipcp->want_comp.proto)),
      ipcp->want_comp.maxchan + 1, ipcp->want_comp.compcid ? "" : "not ");
  else
    Printf("None\r\n");
  Printf("\tPeer: ");
  if (ipcp->peer_comp.proto != 0)
    Printf("%s, %d compression channels, CID %scompressible\n",
      ProtoName(ntohs(ipcp->peer_comp.proto)),
      ipcp->peer_comp.maxchan + 1, ipcp->peer_comp.compcid ? "" : "not ");
  else
    Printf("None\r\n");
#endif /* USE_NG_VJC */
  Printf("Server info we give to peer:\r\n");
  Printf("DNS servers : %15s", inet_ntoa(ipcp->conf.peer_dns[0]));
  Printf("  %15s\r\n", inet_ntoa(ipcp->conf.peer_dns[1]));
  Printf("NBNS servers: %15s", inet_ntoa(ipcp->conf.peer_nbns[0]));
  Printf("  %15s\r\n", inet_ntoa(ipcp->conf.peer_nbns[1]));
  Printf("Server info peer gave to us:\r\n");
  Printf("DNS servers : %15s", inet_ntoa(ipcp->want_dns[0]));
  Printf("  %15s\r\n", inet_ntoa(ipcp->want_dns[1]));
  Printf("NBNS servers: %15s", inet_ntoa(ipcp->want_nbns[0]));
  Printf("  %15s\r\n", inet_ntoa(ipcp->want_nbns[1]));

#ifdef USE_NG_VJC
  /* Get VJC state */
  snprintf(path, sizeof(path), "mpd%d-%s:%s", gPid, ctx->bund->name, NG_PPP_HOOK_VJC_IP);
  if (NgFuncSendQuery(path, NGM_VJC_COOKIE, NGM_VJC_GET_STATE,
      NULL, 0, &u.reply, sizeof(u), NULL) < 0)
    return(0);

  Printf("VJ Compression:\r\n");
  Printf("\tOut comp : %d\r\n", sls->sls_compressed);
  Printf("\tOut total: %d\r\n", sls->sls_packets);
  Printf("\tMissed   : %d\r\n", sls->sls_misses);
  Printf("\tSearched : %d\r\n", sls->sls_searches);
  Printf("\tIn comp  : %d\r\n", sls->sls_compressedin);
  Printf("\tIn uncomp: %d\r\n", sls->sls_uncompressedin);
  Printf("\tIn error : %d\r\n", sls->sls_errorin);
  Printf("\tIn tossed: %d\r\n", sls->sls_tossed);
#endif /* USE_NG_VJC */
  return(0);
}

/*
 * IpcpInit()
 */

void
IpcpInit(Bund b)
{
  IpcpState		const ipcp = &b->ipcp;

  /* Init state machine */
  memset(ipcp, 0, sizeof(*ipcp));
  FsmInit(&ipcp->fsm, &gIpcpFsmType, b);

  /* Come up with a default IP address for my side of the link */
  u_rangeclear(&ipcp->conf.self_allow);
  GetAnyIpAddress(&ipcp->conf.self_allow.addr, NULL);

#ifdef USE_NG_VJC
  /* Default we want VJ comp */
  Enable(&ipcp->conf.options, IPCP_CONF_VJCOMP);
  Accept(&ipcp->conf.options, IPCP_CONF_VJCOMP);
#endif
}

/*
 * IpcpInst()
 */

void
IpcpInst(Bund b, Bund bt)
{
  IpcpState		const ipcp = &b->ipcp;

  /* Init state machine */
  memcpy(ipcp, &bt->ipcp, sizeof(*ipcp));
  FsmInst(&ipcp->fsm, &bt->ipcp.fsm, b);
}

/*
 * IpcpConfigure()
 */

static void
IpcpConfigure(Fsm fp)
{
    Bund 	b = (Bund)fp->arg;
    IpcpState	const ipcp = &b->ipcp;
    char	buf[48];

    /* FSM stuff */
    ipcp->peer_reject = 0;

    /* Get allowed IP addresses from config and/or from current bundle */
    if (ipcp->conf.self_ippool[0]) {
	if (IPPoolGet(ipcp->conf.self_ippool, &ipcp->self_allow.addr)) {
	    Log(LG_IPCP, ("[%s] IPCP: Can't get IP from pool \"%s\" for self",
		b->name, ipcp->conf.self_ippool));
	} else {
	    Log(LG_IPCP, ("[%s] IPCP: Got IP %s from pool \"%s\" for self",
		b->name,
		u_addrtoa(&ipcp->self_allow.addr, buf, sizeof(buf)),
		ipcp->conf.self_ippool));
	    ipcp->self_allow.width = 32;
	    ipcp->self_ippool_used = 1;
	}
    } else
	ipcp->self_allow = ipcp->conf.self_allow;

    if ((b->params.range_valid) && (!u_rangeempty(&b->params.range)))
	ipcp->peer_allow = b->params.range;
    else if (b->params.ippool[0]) {
	/* Get IP from pool if needed */
	if (IPPoolGet(b->params.ippool, &ipcp->peer_allow.addr)) {
	    Log(LG_IPCP, ("[%s] IPCP: Can't get IP from pool \"%s\" for peer",
		b->name, b->params.ippool));
	} else {
	    Log(LG_IPCP, ("[%s] IPCP: Got IP %s from pool \"%s\" for peer",
		b->name,
		u_addrtoa(&ipcp->peer_allow.addr, buf, sizeof(buf)),
		b->params.ippool));
	    ipcp->peer_allow.width = 32;
	    b->params.ippool_used = 1;
	}
    } else if (ipcp->conf.ippool[0]) {
	if (IPPoolGet(ipcp->conf.ippool, &ipcp->peer_allow.addr)) {
	    Log(LG_IPCP, ("[%s] IPCP: Can't get IP from pool \"%s\"",
		b->name, ipcp->conf.ippool));
	} else {
	    Log(LG_IPCP, ("[%s] IPCP: Got IP %s from pool \"%s\" for peer",
		b->name,
		u_addrtoa(&ipcp->peer_allow.addr, buf, sizeof(buf)),
		ipcp->conf.ippool));
	    ipcp->peer_allow.width = 32;
	    ipcp->ippool_used = 1;
	}
    } else
	ipcp->peer_allow = ipcp->conf.peer_allow;
    
    /* Initially request addresses as specified by config */
    u_addrtoin_addr(&ipcp->self_allow.addr, &ipcp->want_addr);
    u_addrtoin_addr(&ipcp->peer_allow.addr, &ipcp->peer_addr);

#ifdef USE_NG_VJC
    /* Van Jacobson compression */
    ipcp->peer_comp.proto = 0;
    ipcp->peer_comp.maxchan = IPCP_VJCOMP_DEFAULT_MAXCHAN;
    ipcp->peer_comp.compcid = 0;

    ipcp->want_comp.proto =
	(b->params.vjc_enable || Enabled(&ipcp->conf.options, IPCP_CONF_VJCOMP)) ?
	    htons(PROTO_VJCOMP) : 0;
    ipcp->want_comp.maxchan = IPCP_VJCOMP_MAX_MAXCHAN;

    /* If any of our links are unable to give receive error indications, we must
     tell the peer not to compress the slot-id in VJCOMP packets (cf. RFC1144).
     To be on the safe side, we always say this. */
    ipcp->want_comp.compcid = 0;
#endif

    /* DNS and NBNS servers */
    memset(&ipcp->want_dns, 0, sizeof(ipcp->want_dns));
    memset(&ipcp->want_nbns, 0, sizeof(ipcp->want_nbns));
}

/*
 * IpcpUnConfigure()
 */

static void
IpcpUnConfigure(Fsm fp)
{
    Bund 	b = (Bund)fp->arg;
    IpcpState	const ipcp = &b->ipcp;
  
    if (ipcp->self_ippool_used) {
	struct u_addr ip;
	in_addrtou_addr(&ipcp->want_addr, &ip);
	IPPoolFree(ipcp->conf.self_ippool, &ip);
	ipcp->self_ippool_used = 0;
    }
    if (b->params.ippool_used) {
	struct u_addr ip;
	in_addrtou_addr(&ipcp->peer_addr, &ip);
	IPPoolFree(b->params.ippool, &ip);
	b->params.ippool_used = 0;
    } else if (ipcp->ippool_used) {
	struct u_addr ip;
	in_addrtou_addr(&ipcp->peer_addr, &ip);
	IPPoolFree(ipcp->conf.ippool, &ip);
	ipcp->ippool_used = 0;
    }
}

/*
 * IpcpBuildConfigReq()
 */

static u_char *
IpcpBuildConfigReq(Fsm fp, u_char *cp)
{
    Bund 	b = (Bund)fp->arg;
    IpcpState	const ipcp = &b->ipcp;

    /* Put in my desired IP address */
    if (!IPCP_REJECTED(ipcp, TY_IPADDR) || ipcp->want_addr.s_addr == 0)
	cp = FsmConfValue(cp, TY_IPADDR, 4, &ipcp->want_addr.s_addr);

#ifdef USE_NG_VJC
    /* Put in my requested compression protocol */
    if (ipcp->want_comp.proto != 0 && !IPCP_REJECTED(ipcp, TY_COMPPROTO))
	cp = FsmConfValue(cp, TY_COMPPROTO, 4, &ipcp->want_comp);
#endif

  /* Request peer's DNS and NBNS servers */
  {
    const int	sopts[2][2] = { { IPCP_CONF_REQPRIDNS, IPCP_CONF_REQSECDNS },
				{ IPCP_CONF_REQPRINBNS, IPCP_CONF_REQSECNBNS }};
    const int	nopts[2][2] = { { TY_PRIMARYDNS, TY_SECONDARYDNS }, 
				{ TY_PRIMARYNBNS, TY_SECONDARYNBNS } };
    struct in_addr	*vals[2] = { ipcp->want_dns, ipcp->want_nbns };
    int			sopt, pri;

    for (sopt = 0; sopt < 2; sopt++) {
      for (pri = 0; pri < 2; pri++) {
	const int	opt = nopts[sopt][pri];

	/* Add option if we desire it and it hasn't been rejected */
	if (Enabled(&ipcp->conf.options, sopts[sopt][pri])
	    && !IPCP_REJECTED(ipcp, opt)) {
	  cp = FsmConfValue(cp, opt, 4, &vals[sopt][pri]);
	}
      }
    }
  }

/* Done */

  return(cp);
}

/*
 * IpcpLayerStart()
 *
 * Tell the lower layer (the bundle) that we need it
 */

static void
IpcpLayerStart(Fsm fp)
{
  BundNcpsStart((Bund)(fp->arg), NCP_IPCP);
}

/*
 * IpcpLayerFinish()
 *
 * Tell the lower layer (the bundle) that we no longer need it
 */

static void
IpcpLayerFinish(Fsm fp)
{
  BundNcpsFinish((Bund)(fp->arg), NCP_IPCP);
}

/*
 * IpcpLayerUp()
 *
 * Called when IPCP has reached the OPEN state
 */

static void
IpcpLayerUp(Fsm fp)
{
    Bund 			b = (Bund)fp->arg;
    IpcpState			const ipcp = &b->ipcp;
    char			ipbuf[20];
#ifdef USE_NG_VJC
    char			path[NG_PATHSIZ];
    struct ngm_vjc_config	vjc;
#endif
    struct u_addr		tmp;

    /* Determine actual address we'll use for ourselves */
    in_addrtou_addr(&ipcp->want_addr, &tmp);
    if (!IpAddrInRange(&ipcp->self_allow, &tmp)) {
	Log(LG_IPCP, ("[%s]   Note: ignoring negotiated %s IP %s,",
    	    b->name, "self", inet_ntoa(ipcp->want_addr)));
	u_addrtoin_addr(&ipcp->self_allow.addr, &ipcp->want_addr);
	Log(LG_IPCP, ("[%s]        using %s instead.",
    	    b->name, inet_ntoa(ipcp->want_addr)));
    }

    /* Determine actual address we'll use for peer */
    in_addrtou_addr(&ipcp->peer_addr, &tmp);
    if (!IpAddrInRange(&ipcp->peer_allow, &tmp)
    	    && !u_addrempty(&ipcp->peer_allow.addr)) {
	Log(LG_IPCP, ("[%s]   Note: ignoring negotiated %s IP %s,",
    	    b->name, "peer", inet_ntoa(ipcp->peer_addr)));
	u_addrtoin_addr(&ipcp->peer_allow.addr, &ipcp->peer_addr);
	Log(LG_IPCP, ("[%s]        using %s instead.",
    	    b->name, inet_ntoa(ipcp->peer_addr)));
    }

    /* Report */
    strlcpy(ipbuf, inet_ntoa(ipcp->peer_addr), sizeof(ipbuf));
    Log(LG_IPCP, ("[%s]   %s -> %s", b->name, inet_ntoa(ipcp->want_addr), ipbuf));

#ifdef USE_NG_VJC
    memset(&vjc, 0, sizeof(vjc));
    if (ntohs(ipcp->peer_comp.proto) == PROTO_VJCOMP || 
	    ntohs(ipcp->want_comp.proto) == PROTO_VJCOMP) {
  
	IpcpNgInitVJ(b);

	/* Configure VJ compression node */
	vjc.enableComp = ntohs(ipcp->peer_comp.proto) == PROTO_VJCOMP;
	vjc.enableDecomp = ntohs(ipcp->want_comp.proto) == PROTO_VJCOMP;
	vjc.maxChannel = ipcp->peer_comp.maxchan;
	vjc.compressCID = ipcp->peer_comp.compcid;
        snprintf(path, sizeof(path), "[%x]:%s", b->nodeID, NG_PPP_HOOK_VJC_IP);
	if (NgSendMsg(gLinksCsock, path,
    		NGM_VJC_COOKIE, NGM_VJC_SET_CONFIG, &vjc, sizeof(vjc)) < 0) {
	    Perror("[%s] can't config %s node", b->name, NG_VJC_NODE_TYPE);
	}
    }
#endif /* USE_NG_VJC */

    /* Enable IP packets in the PPP node */
    b->pppConfig.bund.enableIP = 1;
#ifdef USE_NG_VJC
    b->pppConfig.bund.enableVJCompression = vjc.enableComp;
    b->pppConfig.bund.enableVJDecompression = vjc.enableDecomp;
#endif
    NgFuncSetConfig(b);

    BundNcpsJoin(b, NCP_IPCP);
}

/*
 * IpcpLayerDown()
 *
 * Called when IPCP leaves the OPEN state
 */

static void
IpcpLayerDown(Fsm fp)
{
    Bund 	b = (Bund)fp->arg;
#ifdef USE_NG_VJC
    IpcpState	const ipcp = &b->ipcp;
#endif

    BundNcpsLeave(b, NCP_IPCP);

    /* Turn off IP packets */
    b->pppConfig.bund.enableIP = 0;
#ifdef USE_NG_VJC
    b->pppConfig.bund.enableVJCompression = 0;
    b->pppConfig.bund.enableVJDecompression = 0;
#endif
    NgFuncSetConfig(b);

#ifdef USE_NG_VJC
    if (ntohs(ipcp->peer_comp.proto) == PROTO_VJCOMP || 
	    ntohs(ipcp->want_comp.proto) == PROTO_VJCOMP) {
	IpcpNgShutdownVJ(b);
    }
#endif /* USE_NG_VJC */
}

/*
 * IpcpUp()
 */

void
IpcpUp(Bund b)
{
    FsmUp(&b->ipcp.fsm);
}

/*
 * IpcpDown()
 */

void
IpcpDown(Bund b)
{
    FsmDown(&b->ipcp.fsm);
}

/*
 * IpcpOpen()
 */

void
IpcpOpen(Bund b)
{
    FsmOpen(&b->ipcp.fsm);
}

/*
 * IpcpClose()
 */

void
IpcpClose(Bund b)
{
    FsmClose(&b->ipcp.fsm);
}

/*
 * IpcpOpenCmd()
 */

int
IpcpOpenCmd(Context ctx)
{
    if (ctx->bund->tmpl)
	Error("impossible to open template");
    FsmOpen(&ctx->bund->ipcp.fsm);
    return (0);
}

/*
 * IpcpCloseCmd()
 */

int
IpcpCloseCmd(Context ctx)
{
    if (ctx->bund->tmpl)
	Error("impossible to close template");
    FsmClose(&ctx->bund->ipcp.fsm);
    return (0);
}

/*
 * IpcpFailure()
 */

static void
IpcpFailure(Fsm fp, enum fsmfail reason)
{
    Bund 	b = (Bund)fp->arg;
    RecordLinkUpDownReason(b, NULL, 0, STR_PROTO_ERR, STR_IPCP_FAILED, FsmFailureStr(reason));
}

/*
 * IpcpDecodeConfig()
 */

static void
IpcpDecodeConfig(Fsm fp, FsmOption list, int num, int mode)
{
    Bund 	b = (Bund)fp->arg;
  IpcpState		const ipcp = &b->ipcp;
  struct in_addr	*wantip, *peerip;
  int			k;

  /* Decode each config option */
  for (k = 0; k < num; k++) {
    FsmOption	const opt = &list[k];
    FsmOptInfo	const oi = FsmFindOptInfo(gIpcpConfOpts, opt->type);

    if (!oi) {
      Log(LG_IPCP, ("[%s]   UNKNOWN[%d] len=%d", b->name, opt->type, opt->len));
      if (mode == MODE_REQ)
	FsmRej(fp, opt);
      continue;
    }
    if (!oi->supported) {
      Log(LG_IPCP, ("[%s]   %s", b->name, oi->name));
      if (mode == MODE_REQ) {
	Log(LG_IPCP, ("[%s]     Not supported", b->name));
	FsmRej(fp, opt);
      }
      continue;
    }
    if (opt->len < oi->minLen + 2 || opt->len > oi->maxLen + 2) {
      Log(LG_IPCP, ("[%s]   %s", b->name, oi->name));
      if (mode == MODE_REQ) {
	Log(LG_IPCP, ("[%s]     bogus len=%d", b->name, opt->len));
	FsmRej(fp, opt);
      }
      continue;
    }
    switch (opt->type) {
      case TY_IPADDR:
	{
	  struct in_addr	ip;
	  struct u_addr		tmp;

	  memcpy(&ip, opt->data, 4);
	  in_addrtou_addr(&ip, &tmp);
	  Log(LG_IPCP, ("[%s]   %s %s", b->name, oi->name, inet_ntoa(ip)));
	  switch (mode) {
	    case MODE_REQ:
	      if (!IpAddrInRange(&ipcp->peer_allow, &tmp) || !ip.s_addr) {
		if (ipcp->peer_addr.s_addr == 0)
		  Log(LG_IPCP, ("[%s]     no IP address available for peer!", b->name));
		if (Enabled(&ipcp->conf.options, IPCP_CONF_PRETENDIP)) {
		  Log(LG_IPCP, ("[%s]     pretending that %s is OK, will ignore",
		      b->name, inet_ntoa(ip)));
		  ipcp->peer_addr = ip;
		  FsmAck(fp, opt);
		  break;
		}
		memcpy(opt->data, &ipcp->peer_addr, 4);
		Log(LG_IPCP, ("[%s]     NAKing with %s", b->name, inet_ntoa(ipcp->peer_addr)));
		FsmNak(fp, opt);
		break;
	      }
	      Log(LG_IPCP, ("[%s]     %s is OK", b->name, inet_ntoa(ip)));
	      ipcp->peer_addr = ip;
	      FsmAck(fp, opt);
	      break;
	    case MODE_NAK:
	      {
		if (IpAddrInRange(&ipcp->self_allow, &tmp)) {
		  Log(LG_IPCP, ("[%s]     %s is OK", b->name, inet_ntoa(ip)));
		  ipcp->want_addr = ip;
		} else if (Enabled(&ipcp->conf.options, IPCP_CONF_PRETENDIP)) {
		  Log(LG_IPCP, ("[%s]     pretending that %s is OK, will ignore",
		      b->name, inet_ntoa(ip)));
		  ipcp->want_addr = ip;
		} else
		  Log(LG_IPCP, ("[%s]     %s is unacceptable", b->name, inet_ntoa(ip)));
	      }
	      break;
	    case MODE_REJ:
	      IPCP_PEER_REJ(ipcp, opt->type);
	      if (ipcp->want_addr.s_addr == 0)
		Log(LG_IPCP, ("[%s]     Problem: I need an IP address!", b->name));
	      break;
	  }
	}
	break;

#ifdef USE_NG_VJC
      case TY_COMPPROTO:
	{
	  struct ipcpvjcomp	vj;

	  memcpy(&vj, opt->data, sizeof(vj));
	  Log(LG_IPCP, ("[%s]   %s %s, %d comp. channels, %s comp-cid",
	    b->name, oi->name, ProtoName(ntohs(vj.proto)),
	    vj.maxchan + 1, vj.compcid ? "allow" : "no"));
	  switch (mode) {
	    case MODE_REQ:
	      if (!Acceptable(&ipcp->conf.options, IPCP_CONF_VJCOMP) && 
	    	  !b->params.vjc_enable) {
		FsmRej(fp, opt);
		break;
	      }
	      if (ntohs(vj.proto) == PROTO_VJCOMP
		  && vj.maxchan <= IPCP_VJCOMP_MAX_MAXCHAN
		  && vj.maxchan >= IPCP_VJCOMP_MIN_MAXCHAN) {
		ipcp->peer_comp = vj;
		FsmAck(fp, opt);
		break;
	      }
	      vj.proto = htons(PROTO_VJCOMP);
	      vj.maxchan = IPCP_VJCOMP_MAX_MAXCHAN;
	      vj.compcid = 0;
	      memcpy(opt->data, &vj, sizeof(vj));
	      FsmNak(fp, opt);
	      break;
	    case MODE_NAK:
	      if (ntohs(vj.proto) != PROTO_VJCOMP) {
		Log(LG_IPCP, ("[%s]     Can't accept proto 0x%04x",
		  b->name, (u_short) ntohs(vj.proto)));
		break;
	      }
	      if (vj.maxchan != ipcp->want_comp.maxchan) {
		if (vj.maxchan <= IPCP_VJCOMP_MAX_MAXCHAN
		    && vj.maxchan >= IPCP_VJCOMP_MIN_MAXCHAN) {
		  Log(LG_IPCP, ("[%s]     Adjusting # compression channels", b->name));
		  ipcp->want_comp.maxchan = vj.maxchan;
		} else {
		  Log(LG_IPCP, ("[%s]     Can't handle %d maxchan", b->name, vj.maxchan));
		}
	      }
	      if (vj.compcid) {
		Log(LG_IPCP, ("[%s]     Can't accept comp-cid", b->name));
		break;
	      }
	      break;
	    case MODE_REJ:
	      IPCP_PEER_REJ(ipcp, opt->type);
	      ipcp->want_comp.proto = 0;
	      break;
	  }
	}
	break;
#endif /* USE_NG_VJC */

      case TY_PRIMARYDNS:
        if (b->params.peer_dns[0].s_addr != 0)
	    peerip = &b->params.peer_dns[0];
	else
	    peerip = &ipcp->conf.peer_dns[0];
	wantip = &ipcp->want_dns[0];
	goto doDnsNbns;
      case TY_PRIMARYNBNS:
        if (b->params.peer_nbns[0].s_addr != 0)
	    peerip = &b->params.peer_nbns[0];
	else
	    peerip = &ipcp->conf.peer_nbns[0];
	wantip = &ipcp->want_nbns[0];
	goto doDnsNbns;
      case TY_SECONDARYDNS:
        if (b->params.peer_dns[1].s_addr != 0)
	    peerip = &b->params.peer_dns[1];
	else
	    peerip = &ipcp->conf.peer_dns[1];
	wantip = &ipcp->want_dns[1];
	goto doDnsNbns;
      case TY_SECONDARYNBNS:
        if (b->params.peer_nbns[1].s_addr != 0)
	    peerip = &b->params.peer_nbns[1];
	else
	    peerip = &ipcp->conf.peer_nbns[1];
	wantip = &ipcp->want_nbns[1];
doDnsNbns:
	{
	  struct in_addr	hisip;

	  memcpy(&hisip, opt->data, 4);
	  Log(LG_IPCP, ("[%s]   %s %s", b->name, oi->name, inet_ntoa(hisip)));
	  switch (mode) {
	    case MODE_REQ:
	      if (hisip.s_addr == 0) {		/* he's asking for one */
		if (peerip->s_addr == 0) {	/* we don't got one */
		  FsmRej(fp, opt);
		  break;
		}
		Log(LG_IPCP, ("[%s]     NAKing with %s", b->name, inet_ntoa(*peerip)));
		memcpy(opt->data, peerip, sizeof(*peerip));
		FsmNak(fp, opt);		/* we got one for him */
		break;
	      }
	      FsmAck(fp, opt);			/* he knows what he wants */
	      break;
	    case MODE_NAK:	/* we asked for his server, he's telling us */
	      *wantip = hisip;
	      break;
	    case MODE_REJ:	/* we asked for his server, he's ignorant */
	      IPCP_PEER_REJ(ipcp, opt->type);
	      break;
	  }
	}
	break;

      default:
	assert(0);
    }
  }
}

/*
 * IpcpInput()
 *
 * Deal with an incoming IPCP packet
 */

void
IpcpInput(Bund b, Mbuf bp)
{
    FsmInput(&b->ipcp.fsm, bp);
}

#ifdef USE_NG_VJC
static int
IpcpNgInitVJ(Bund b)
{
  struct ngm_mkpeer	mp;
  struct ngm_connect	cn;
  char path[NG_PATHSIZ];
  struct ngm_name	nm;

  /* Add a VJ compression node */
  snprintf(path, sizeof(path), "[%x]:", b->nodeID);
  strcpy(mp.type, NG_VJC_NODE_TYPE);
  strcpy(mp.ourhook, NG_PPP_HOOK_VJC_IP);
  strcpy(mp.peerhook, NG_VJC_HOOK_IP);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
    Perror("[%s] can't create %s node at \"%s\"->\"%s\"",
      b->name, NG_VJC_NODE_TYPE, path, mp.ourhook);
    goto fail;
  }

  /* Give it a name */
  strlcat(path, NG_PPP_HOOK_VJC_IP, sizeof(path));
  snprintf(nm.name, sizeof(nm.name), "mpd%d-%s-vjc", gPid, b->name);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
    Perror("[%s] can't name %s node", b->name, NG_VJC_NODE_TYPE);
    goto fail;
  }

  /* Connect the other three hooks between the ppp and vjc nodes */
  snprintf(path, sizeof(path), "[%x]:", b->nodeID);
  strcpy(cn.path, NG_PPP_HOOK_VJC_IP);
  strcpy(cn.ourhook, NG_PPP_HOOK_VJC_COMP);
  strcpy(cn.peerhook, NG_VJC_HOOK_VJCOMP);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
    Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
      b->name, path, cn.ourhook, cn.path, cn.peerhook);
    goto fail;
  }
  strcpy(cn.ourhook, NG_PPP_HOOK_VJC_UNCOMP);
  strcpy(cn.peerhook, NG_VJC_HOOK_VJUNCOMP);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
    Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
      b->name, path, cn.ourhook, cn.path, cn.peerhook);
    goto fail;
  }
  strcpy(cn.ourhook, NG_PPP_HOOK_VJC_VJIP);
  strcpy(cn.peerhook, NG_VJC_HOOK_VJIP);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
    Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
      b->name, path, cn.ourhook, cn.path, cn.peerhook);
    goto fail;
  }

    return 0;
fail:
    return -1;
}

static void
IpcpNgShutdownVJ(Bund b)
{
    char	path[NG_PATHSIZ];

    snprintf(path, sizeof(path), "[%x]:%s", b->nodeID, NG_PPP_HOOK_VJC_IP);
    NgFuncShutdownNode(gLinksCsock, b->name, path);
}
#endif /* USE_NG_VJC */

/*
 * IpcpSetCommand()
 */

static int
IpcpSetCommand(Context ctx, int ac, const char *const av[], const void *arg)
{
  IpcpState		const ipcp = &ctx->bund->ipcp;
  struct in_addr	*ips;

  if (ac == 0)
    return(-1);
  switch ((intptr_t)arg) {
    case SET_RANGES:
      {
	struct u_range	self_new_allow;
	struct u_range	peer_new_allow;
	int pos = 0, self_new_pool = -1, peer_new_pool = -1;

	/* Parse args */
	if (ac < 2)
	    return (-1);
	if (strcmp(av[pos], "ippool") == 0) {
	    self_new_pool = pos+1;
	    pos+=2;
	} else {
	    if (!ParseRange(av[pos], &self_new_allow, ALLOW_IPV4))
		return(-1);
	    pos++;
	}
	if (pos >= ac)
	    return (-1);
	if (strcmp(av[pos], "ippool") == 0) {
	    if ((pos + 1) >= ac)
		return (-1);
	    peer_new_pool = pos+1;
	    pos+=2;
	} else {
	    if (!ParseRange(av[pos], &peer_new_allow, ALLOW_IPV4))
		return(-1);
	    pos++;
	}
	if (pos != ac)
	    return (-1);

	if (self_new_pool >= 0)
	    strlcpy(ipcp->conf.self_ippool, av[self_new_pool], sizeof(ipcp->conf.self_ippool));
	else
	    ipcp->conf.self_ippool[0] = 0;
	if (peer_new_pool >= 0)
	    strlcpy(ipcp->conf.ippool, av[peer_new_pool], sizeof(ipcp->conf.ippool));
	else
	    ipcp->conf.ippool[0] = 0;
	ipcp->conf.self_allow = self_new_allow;
	ipcp->conf.peer_allow = peer_new_allow;

      }
      break;

    case SET_DNS:
      ips = ipcp->conf.peer_dns;
      goto getPrimSec;
      break;
    case SET_NBNS:
      ips = ipcp->conf.peer_nbns;
getPrimSec:
      if (!inet_aton(av[0], &ips[0]))
	Error("invalid IP address: \'%s\'", av[0]);
      ips[1].s_addr = 0;
      if (ac > 1 && !inet_aton(av[1], &ips[1]))
	Error("invalid IP address: \'%s\'", av[1]);
      break;

    case SET_ACCEPT:
      AcceptCommand(ac, av, &ipcp->conf.options, gConfList);
      break;

    case SET_DENY:
      DenyCommand(ac, av, &ipcp->conf.options, gConfList);
      break;

    case SET_ENABLE:
      EnableCommand(ac, av, &ipcp->conf.options, gConfList);
      break;

    case SET_DISABLE:
      DisableCommand(ac, av, &ipcp->conf.options, gConfList);
      break;

    case SET_YES:
      YesCommand(ac, av, &ipcp->conf.options, gConfList);
      break;

    case SET_NO:
      NoCommand(ac, av, &ipcp->conf.options, gConfList);
      break;

    default:
      assert(0);
  }
  return(0);
}


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