/*
* link.c
*
* Written by Archie Cobbs <archie@freebsd.org>
* Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
* See ``COPYRIGHT.whistle''
*/
#include "ppp.h"
#include "link.h"
#include "msg.h"
#include "lcp.h"
#include "phys.h"
#include "command.h"
#include "input.h"
#include "ngfunc.h"
#include "util.h"
/*
* DEFINITIONS
*/
/* Set menu options */
enum {
SET_DEVTYPE,
SET_BANDWIDTH,
SET_LATENCY,
SET_ACCMAP,
SET_MRU,
SET_MTU,
SET_FSM_RETRY,
SET_MAX_RETRY,
SET_KEEPALIVE,
SET_IDENT,
SET_ACCEPT,
SET_DENY,
SET_ENABLE,
SET_DISABLE,
SET_YES,
SET_NO,
};
#define RBUF_SIZE 100
/*
* INTERNAL FUNCTIONS
*/
static int LinkSetCommand(Context ctx, int ac, char *av[], void *arg);
static void LinkMsg(int type, void *cookie);
/*
* GLOBAL VARIABLES
*/
const struct cmdtab LinkSetCmds[] = {
{ "bandwidth bps", "Link bandwidth",
LinkSetCommand, NULL, (void *) SET_BANDWIDTH },
{ "type type", "Device type",
LinkSetCommand, NULL, (void *) SET_DEVTYPE },
{ "latency microsecs", "Link latency",
LinkSetCommand, NULL, (void *) SET_LATENCY },
{ "accmap hex-value", "Accmap value",
LinkSetCommand, NULL, (void *) SET_ACCMAP },
{ "mru value", "Link MRU value",
LinkSetCommand, NULL, (void *) SET_MRU },
{ "mtu value", "Link MTU value",
LinkSetCommand, NULL, (void *) SET_MTU },
{ "fsm-timeout seconds", "FSM retry timeout",
LinkSetCommand, NULL, (void *) SET_FSM_RETRY },
{ "max-redial num", "Max connect attempts",
LinkSetCommand, NULL, (void *) SET_MAX_RETRY },
{ "keep-alive secs max", "LCP echo keep-alives",
LinkSetCommand, NULL, (void *) SET_KEEPALIVE },
{ "ident ident-string", "LCP ident string",
LinkSetCommand, NULL, (void *) SET_IDENT },
{ "accept [opt ...]", "Accept option",
LinkSetCommand, NULL, (void *) SET_ACCEPT },
{ "deny [opt ...]", "Deny option",
LinkSetCommand, NULL, (void *) SET_DENY },
{ "enable [opt ...]", "Enable option",
LinkSetCommand, NULL, (void *) SET_ENABLE },
{ "disable [opt ...]", "Disable option",
LinkSetCommand, NULL, (void *) SET_DISABLE },
{ "yes [opt ...]", "Enable and accept option",
LinkSetCommand, NULL, (void *) SET_YES },
{ "no [opt ...]", "Disable and deny option",
LinkSetCommand, NULL, (void *) SET_NO },
{ NULL },
};
/*
* INTERNAL VARIABLES
*/
static struct confinfo gConfList[] = {
{ 1, LINK_CONF_PAP, "pap" },
{ 1, LINK_CONF_CHAPMD5, "chap-md5" },
{ 1, LINK_CONF_CHAPMSv1, "chap-msv1" },
{ 1, LINK_CONF_CHAPMSv2, "chap-msv2" },
{ 1, LINK_CONF_EAP, "eap" },
{ 1, LINK_CONF_ACFCOMP, "acfcomp" },
{ 1, LINK_CONF_PROTOCOMP, "protocomp" },
{ 0, LINK_CONF_MSDOMAIN, "keep-ms-domain"},
{ 0, LINK_CONF_MAGICNUM, "magicnum" },
{ 0, LINK_CONF_PASSIVE, "passive" },
{ 0, LINK_CONF_CHECK_MAGIC, "check-magic" },
{ 0, LINK_CONF_NO_ORIG_AUTH, "no-orig-auth" },
{ 0, LINK_CONF_CALLBACK, "callback" },
{ 0, 0, NULL },
};
/*
* LinkOpenCmd()
*/
void
LinkOpenCmd(Context ctx)
{
RecordLinkUpDownReason(NULL, ctx->lnk, 1, STR_MANUALLY, NULL);
LinkOpen(ctx->lnk);
}
/*
* LinkCloseCmd()
*/
void
LinkCloseCmd(Context ctx)
{
RecordLinkUpDownReason(NULL, ctx->lnk, 0, STR_MANUALLY, NULL);
LinkClose(ctx->lnk);
}
/*
* LinkOpen()
*/
void
LinkOpen(Link l)
{
MsgSend(l->msgs, MSG_OPEN, l);
}
/*
* LinkClose()
*/
void
LinkClose(Link l)
{
MsgSend(l->msgs, MSG_CLOSE, l);
}
/*
* LinkUp()
*/
void
LinkUp(Link l)
{
Log(LG_LINK, ("[%s] link: UP event", l->name));
l->originate = PhysGetOriginate(l->phys);
Log(LG_LINK, ("[%s] link: origination is %s",
l->name, LINK_ORIGINATION(l->originate)));
LcpUp(l);
}
/*
* LinkDown()
*/
void
LinkDown(Link l)
{
Log(LG_LINK, ("[%s] link: DOWN event", l->name));
if (OPEN_STATE(l->lcp.fsm.state)) {
if ((l->conf.max_redial != 0) && (l->num_redial >= l->conf.max_redial)) {
if (l->conf.max_redial >= 0) {
Log(LG_LINK, ("[%s] link: giving up after %d reconnection attempts",
l->name, l->num_redial));
}
LcpClose(l);
LcpDown(l);
} else {
l->num_redial++;
Log(LG_LINK, ("[%s] link: reconnection attempt %d",
l->name, l->num_redial));
RecordLinkUpDownReason(NULL, l, 1, STR_REDIAL, NULL);
LcpDown(l);
if (!gShutdownInProgress) /* Giveup on shutdown */
PhysOpen(l->phys); /* Try again */
}
} else {
LcpDown(l);
}
/* reset Link-stats */
LinkResetStats(l); /* XXX: I don't think this is a right place */
}
/*
* LinkMsg()
*
* Deal with incoming message to this link
*/
static void
LinkMsg(int type, void *arg)
{
Link l = (Link)arg;
Log(LG_LINK, ("[%s] link: %s event", l->name, MsgName(type)));
switch (type) {
case MSG_OPEN:
l->last_open = time(NULL);
l->num_redial = 0;
LcpOpen(l);
break;
case MSG_CLOSE:
LcpClose(l);
break;
default:
assert(FALSE);
}
}
/*
* LinkNew()
*
* Allocate a new link for the specified device, then
* read in any device-specific commands from ppp.links.
*/
Link
LinkNew(char *name, Bund b, int bI)
{
Link lnk;
/* Create and initialize new link */
lnk = Malloc(MB_LINK, sizeof(*lnk));
snprintf(lnk->name, sizeof(lnk->name), "%s", name);
lnk->bund = b;
lnk->bundleIndex = bI;
lnk->msgs = MsgRegister(LinkMsg);
/* Initialize link configuration with defaults */
lnk->conf.mru = LCP_DEFAULT_MRU;
lnk->conf.mtu = LCP_DEFAULT_MRU;
lnk->conf.accmap = 0x000a0000;
lnk->conf.max_redial = -1;
lnk->conf.retry_timeout = LINK_DEFAULT_RETRY;
lnk->bandwidth = LINK_DEFAULT_BANDWIDTH;
lnk->latency = LINK_DEFAULT_LATENCY;
lnk->upReason = NULL;
lnk->upReasonValid = 0;
lnk->downReason = NULL;
lnk->downReasonValid = 0;
Disable(&lnk->conf.options, LINK_CONF_CHAPMD5);
Accept(&lnk->conf.options, LINK_CONF_CHAPMD5);
Disable(&lnk->conf.options, LINK_CONF_CHAPMSv1);
Deny(&lnk->conf.options, LINK_CONF_CHAPMSv1);
Disable(&lnk->conf.options, LINK_CONF_CHAPMSv2);
Accept(&lnk->conf.options, LINK_CONF_CHAPMSv2);
Disable(&lnk->conf.options, LINK_CONF_PAP);
Accept(&lnk->conf.options, LINK_CONF_PAP);
Disable(&lnk->conf.options, LINK_CONF_EAP);
Accept(&lnk->conf.options, LINK_CONF_EAP);
Disable(&lnk->conf.options, LINK_CONF_MSDOMAIN);
Enable(&lnk->conf.options, LINK_CONF_ACFCOMP);
Accept(&lnk->conf.options, LINK_CONF_ACFCOMP);
Enable(&lnk->conf.options, LINK_CONF_PROTOCOMP);
Accept(&lnk->conf.options, LINK_CONF_PROTOCOMP);
Enable(&lnk->conf.options, LINK_CONF_MAGICNUM);
Disable(&lnk->conf.options, LINK_CONF_PASSIVE);
Enable(&lnk->conf.options, LINK_CONF_CHECK_MAGIC);
LcpInit(lnk);
EapInit(lnk);
/* Initialize link layer stuff */
lnk->phys = PhysInit(lnk->name, lnk, NULL);
/* Hang out and be a link */
return(lnk);
}
/*
* LinkShutdown()
*
*/
void
LinkShutdown(Link l)
{
MsgUnRegister(&l->msgs);
if (l->phys)
PhysShutdown(l->phys);
Freee(MB_LINK, l);
}
/*
* LinkFind()
*
* Find a link structure
*/
Link
LinkFind(char *name)
{
int k;
k = gNumPhyses;
if ((sscanf(name, "[%x]", &k) != 1) || (k < 0) || (k >= gNumPhyses)) {
/* Find link */
for (k = 0;
k < gNumPhyses && (gPhyses[k] == NULL || gPhyses[k]->link == NULL ||
strcmp(gPhyses[k]->link->name, name));
k++);
};
if (k == gNumPhyses) {
return (NULL);
}
return (gPhyses[k]->link);
}
/*
* LinkCommand()
*/
int
LinkCommand(Context ctx, int ac, char *av[], void *arg)
{
Link l;
if (ac != 1)
return(-1);
if ((l = LinkFind(av[0])) == NULL) {
Printf("Link \"%s\" is not defined\r\n", av[0]);
ctx->lnk = NULL;
ctx->bund = NULL;
ctx->phys = NULL;
ctx->rep = NULL;
return(0);
}
/* Change default link and bundle */
ctx->lnk = l;
ctx->bund = l->bund;
ctx->phys = l->phys;
ctx->rep = NULL;
return(0);
}
/*
* SessionCommand()
*/
int
SessionCommand(Context ctx, int ac, char *av[], void *arg)
{
Link l;
int k;
if (ac != 1)
return(-1);
/* Find link */
for (k = 0;
k < gNumPhyses && (gPhyses[k] == NULL || gPhyses[k]->link == NULL ||
strcmp(gPhyses[k]->link->session_id, av[0]));
k++);
if (k == gNumPhyses) {
Printf("Session \"%s\" is not found\r\n", av[0]);
/* Change default link and bundle */
ctx->lnk = NULL;
ctx->bund = NULL;
ctx->phys = NULL;
ctx->rep = NULL;
} else {
l = gPhyses[k]->link;
/* Change default link and bundle */
ctx->lnk = l;
ctx->bund = l->bund;
ctx->phys = l->phys;
ctx->rep = NULL;
}
return(0);
}
/*
* RecordLinkUpDownReason()
*
* This is called whenever a reason for the link going up or
* down has just become known. Record this reason so that when
* the link actually goes up or down, we can record it.
*
* If this gets called more than once in the "down" case,
* the first call prevails.
*/
static void
RecordLinkUpDownReason2(Link l, int up, const char *key, const char *fmt, va_list args)
{
char **const cpp = up ? &l->upReason : &l->downReason;
char *buf;
/* First reason overrides later ones */
if (up) {
if (l->upReasonValid) {
return;
} else {
l->upReasonValid = 1;
}
} else {
if (l->downReasonValid) {
return;
} else {
l->downReasonValid = 1;
}
}
/* Allocate buffer if necessary */
if (!*cpp)
*cpp = Malloc(MB_UTIL, RBUF_SIZE);
buf = *cpp;
/* Record reason */
if (fmt) {
snprintf(buf, RBUF_SIZE, "%s:", key);
vsnprintf(buf + strlen(buf), RBUF_SIZE - strlen(buf), fmt, args);
} else
snprintf(buf, RBUF_SIZE, "%s", key);
}
void
RecordLinkUpDownReason(Bund b, Link l, int up, const char *key, const char *fmt, ...)
{
va_list args;
int k;
if (l != NULL) {
va_start(args, fmt);
RecordLinkUpDownReason2(l, up, key, fmt, args);
va_end(args);
} else if (b != NULL) {
for (k = 0; k < b->n_links; k++) {
if (b->links[k]) {
va_start(args, fmt);
RecordLinkUpDownReason2(b->links[k], up, key, fmt, args);
va_end(args);
}
}
}
}
/*
* LinkStat()
*/
int
LinkStat(Context ctx, int ac, char *av[], void *arg)
{
Link l = ctx->lnk;
Printf("Link %s:\r\n", l->name);
Printf("Configuration\r\n");
Printf("\tMRU : %d bytes\r\n", l->conf.mru);
Printf("\tCtrl char map : 0x%08x bytes\r\n", l->conf.accmap);
Printf("\tRetry timeout : %d seconds\r\n", l->conf.retry_timeout);
Printf("\tMax redial : ");
if (l->conf.max_redial < 0)
Printf("no redial\r\n");
else if (l->conf.max_redial == 0)
Printf("unlimited\r\n");
else
Printf("%d connect attempts\r\n", l->conf.max_redial);
Printf("\tBandwidth : %d bits/sec\r\n", l->bandwidth);
Printf("\tLatency : %d usec\r\n", l->latency);
Printf("\tKeep-alive : ");
if (l->lcp.fsm.conf.echo_int == 0)
Printf("disabled\r\n");
else
Printf("every %d secs, timeout %d\r\n",
l->lcp.fsm.conf.echo_int, l->lcp.fsm.conf.echo_max);
Printf("\tIdent string : \"%s\"\r\n", l->conf.ident ? l->conf.ident : "");
Printf("\tSession-Id : %s\r\n", l->session_id);
Printf("Link level options\r\n");
OptStat(ctx, &l->conf.options, gConfList);
LinkUpdateStats(l);
Printf("Up/Down stats:\r\n");
if (l->downReason && (!l->downReasonValid))
Printf("\tDown Reason : %s\r\n", l->downReason);
if (l->upReason)
Printf("\tUp Reason : %s\r\n", l->upReason);
if (l->downReason && l->downReasonValid)
Printf("\tDown Reason : %s\r\n", l->downReason);
Printf("Traffic stats:\r\n");
Printf("\tOctets input : %llu\r\n", (unsigned long long)l->stats.recvOctets);
Printf("\tFrames input : %llu\r\n", (unsigned long long)l->stats.recvFrames);
Printf("\tOctets output : %llu\r\n", (unsigned long long)l->stats.xmitOctets);
Printf("\tFrames output : %llu\r\n", (unsigned long long)l->stats.xmitFrames);
Printf("\tBad protocols : %llu\r\n", (unsigned long long)l->stats.badProtos);
Printf("\tRunts : %llu\r\n", (unsigned long long)l->stats.runts);
Printf("\tDup fragments : %llu\r\n", (unsigned long long)l->stats.dupFragments);
Printf("\tDrop fragments : %llu\r\n", (unsigned long long)l->stats.dropFragments);
return(0);
}
/*
* LinkUpdateStats()
*/
void
LinkUpdateStats(Link l)
{
#ifndef NG_PPP_STATS64
struct ng_ppp_link_stat stats;
if (NgFuncGetStats(l->bund, l->bundleIndex, &stats) != -1) {
l->stats.xmitFrames += abs(stats.xmitFrames - l->oldStats.xmitFrames);
l->stats.xmitOctets += abs(stats.xmitOctets - l->oldStats.xmitOctets);
l->stats.recvFrames += abs(stats.recvFrames - l->oldStats.recvFrames);
l->stats.recvOctets += abs(stats.recvOctets - l->oldStats.recvOctets);
l->stats.badProtos += abs(stats.badProtos - l->oldStats.badProtos);
l->stats.runts += abs(stats.runts - l->oldStats.runts);
l->stats.dupFragments += abs(stats.dupFragments - l->oldStats.dupFragments);
l->stats.dropFragments += abs(stats.dropFragments - l->oldStats.dropFragments);
}
l->oldStats = stats;
#else
NgFuncGetStats64(l->bund, l->bundleIndex, &l->stats);
#endif
}
/*
* LinkResetStats()
*/
void
LinkResetStats(Link l)
{
NgFuncClrStats(l->bund, l->bundleIndex);
memset(&l->stats, 0, sizeof(l->stats));
#ifndef NG_PPP_STATS64
memset(&l->oldStats, 0, sizeof(l->oldStats));
#endif
}
/*
* LinkSetCommand()
*/
static int
LinkSetCommand(Context ctx, int ac, char *av[], void *arg)
{
Link l = ctx->lnk;
int val, nac = 0;
const char *name;
char *nav[ac];
const char *av2[] = { "chap-md5", "chap-msv1", "chap-msv2" };
if (ac == 0)
return(-1);
/* make "chap" as an alias for all chap-variants, this should keep BC */
switch ((intptr_t)arg) {
case SET_ACCEPT:
case SET_DENY:
case SET_ENABLE:
case SET_DISABLE:
case SET_YES:
case SET_NO:
{
int i = 0;
for ( ; i < ac; i++)
{
if (strcasecmp(av[i], "chap") == 0) {
LinkSetCommand(ctx, 3, (char **)av2, arg);
} else {
nav[nac++] = av[i];
}
}
av = nav;
ac = nac;
break;
}
}
switch ((intptr_t)arg) {
case SET_BANDWIDTH:
val = atoi(*av);
if (val <= 0)
Log(LG_ERR, ("[%s] Bandwidth must be positive", l->name));
else if (val > NG_PPP_MAX_BANDWIDTH * 10 * 8) {
l->bandwidth = NG_PPP_MAX_BANDWIDTH * 10 * 8;
Log(LG_ERR, ("[%s] Bandwidth truncated to %d bit/s", l->name,
l->bandwidth));
} else
l->bandwidth = val;
break;
case SET_LATENCY:
val = atoi(*av);
if (val < 0)
Log(LG_ERR, ("[%s] Latency must be not negative", l->name));
else if (val > NG_PPP_MAX_LATENCY * 1000) {
Log(LG_ERR, ("[%s] Latency truncated to %d usec", l->name,
NG_PPP_MAX_LATENCY * 1000));
l->latency = NG_PPP_MAX_LATENCY * 1000;
} else
l->latency = val;
break;
case SET_DEVTYPE:
PhysSetDeviceType(ctx->phys, *av);
break;
case SET_MRU:
case SET_MTU:
val = atoi(*av);
name = ((intptr_t)arg == SET_MTU) ? "MTU" : "MRU";
if (!l->phys->type)
Log(LG_ERR, ("[%s] this link has no type set", l->name));
else if (val < LCP_MIN_MRU)
Log(LG_ERR, ("[%s] the min %s is %d", l->name, name, LCP_MIN_MRU));
else if (l->phys->type && (val > l->phys->type->mru))
Log(LG_ERR, ("[%s] the max %s on type \"%s\" links is %d",
l->name, name, l->phys->type->name, l->phys->type->mru));
else if ((intptr_t)arg == SET_MTU)
l->conf.mtu = val;
else
l->conf.mru = val;
break;
case SET_FSM_RETRY:
l->conf.retry_timeout = atoi(*av);
if (l->conf.retry_timeout < 1 || l->conf.retry_timeout > 10)
l->conf.retry_timeout = LINK_DEFAULT_RETRY;
break;
case SET_MAX_RETRY:
l->conf.max_redial = atoi(*av);
break;
case SET_ACCMAP:
sscanf(*av, "%x", &val);
l->conf.accmap = val;
break;
case SET_KEEPALIVE:
if (ac != 2)
return(-1);
l->lcp.fsm.conf.echo_int = atoi(av[0]);
l->lcp.fsm.conf.echo_max = atoi(av[1]);
break;
case SET_IDENT:
if (ac != 1)
return(-1);
if (l->conf.ident != NULL) {
Freee(MB_FSM, l->conf.ident);
l->conf.ident = NULL;
}
if (*av[0] != '\0')
strcpy(l->conf.ident = Malloc(MB_FSM, strlen(av[0]) + 1), av[0]);
break;
case SET_ACCEPT:
AcceptCommand(ac, av, &l->conf.options, gConfList);
break;
case SET_DENY:
DenyCommand(ac, av, &l->conf.options, gConfList);
break;
case SET_ENABLE:
EnableCommand(ac, av, &l->conf.options, gConfList);
break;
case SET_DISABLE:
DisableCommand(ac, av, &l->conf.options, gConfList);
break;
case SET_YES:
YesCommand(ac, av, &l->conf.options, gConfList);
break;
case SET_NO:
NoCommand(ac, av, &l->conf.options, gConfList);
break;
default:
assert(0);
}
return(0);
}
syntax highlighted by Code2HTML, v. 0.9.1