File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / command.c
Revision 1.1.1.4 (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


/*
 * command.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 "msg.h"
#include "ppp.h"
#include "console.h"
#ifndef NOWEB
#include "web.h"
#endif
#include "radsrv.h"
#include "command.h"
#include "ccp.h"
#include "iface.h"
#include "radius.h"
#include "bund.h"
#include "link.h"
#include "lcp.h"
#ifdef	USE_NG_NAT
#include "nat.h"
#endif
#include "ipcp.h"
#include "ip.h"
#include "ippool.h"
#include "devices.h"
#include "netgraph.h"
#include "ngfunc.h"
#ifdef CCP_MPPC
#include "ccp_mppc.h"
#endif
#include "util.h"
#ifdef USE_FETCH
#include <fetch.h>
#endif
#ifdef USE_NG_NETFLOW
#include <netgraph/netflow/ng_netflow.h>
#endif

/*
 * DEFINITIONS
 */

  struct layer {
    const char	*name;
    int		(*opener)(Context ctx);
    int		(*closer)(Context ctx);
    int		(*admit)(Context ctx, CmdTab cmd);
    const char	*desc;
  };
  typedef struct layer	*Layer;

  #define DEFAULT_OPEN_LAYER	"link"

  /* Set menu options */
  enum {
    SET_ENABLE,
    SET_DISABLE,
#ifdef USE_IPFW
    SET_RULE,
    SET_QUEUE,
    SET_PIPE,
    SET_TABLE,
#endif
#ifdef PHYSTYPE_PPTP
    SET_PPTPTO,
    SET_PPTPLIMIT,
#endif
#ifdef PHYSTYPE_L2TP
    SET_L2TPTO,
    SET_L2TPLIMIT,
#endif
    SET_MAX_CHILDREN,
    SET_QTHRESHOLD,
#ifdef USE_NG_BPF
    SET_FILTER
#endif
  };

  enum {
    SHOW_IFACE,
    SHOW_IP,
    SHOW_USER,
    SHOW_SESSION,
    SHOW_MSESSION,
    SHOW_BUNDLE,
    SHOW_LINK,
    SHOW_PEER
  };

/*
 * INTERNAL FUNCTIONS
 */

  /* Commands */
  static int	ShowVersion(Context ctx, int ac, const char *const av[], const void *arg);
  static int	ShowLayers(Context ctx, int ac, const char *const av[], const void *arg);
  static int	ShowTypes(Context ctx, int ac, const char *const av[], const void *arg);
  static int	ShowSummary(Context ctx, int ac, const char *const av[], const void *arg);
  static int	ShowSessions(Context ctx, int ac, const char *const av[], const void *arg);
  static int	ShowCustomer(Context ctx, int ac, const char *const av[], const void *arg);
  static int	ShowEvents(Context ctx, int ac, const char *const av[], const void *arg);
  static int	ShowGlobal(Context ctx, int ac, const char *const av[], const void *arg);
  static int	OpenCommand(Context ctx, int ac, const char *const av[], const void *arg);
  static int	CloseCommand(Context ctx, int ac, const char *const av[], const void *arg);
  static int	LoadCommand(Context ctx, int ac, const char *const av[], const void *arg);
  static int	ExitCommand(Context ctx, int ac, const char *const av[], const void *arg);
  static int	QuitCommand(Context ctx, int ac, const char *const av[], const void *arg);
  static int	GlobalSetCommand(Context ctx, int ac, const char *const av[], const void *arg);
  static int	SetDebugCommand(Context ctx, int ac, const char *const av[], const void *arg);

  /* Other stuff */
  static Layer	GetLayer(const char *name);

/*
 * INTERNAL VARIABLES
 */

  static const struct cmdtab GlobalSetCmds[] = {
    { "enable {opt ...}", 		"Enable option" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_ENABLE },
    { "disable {opt ...}", 		"Disable option" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_DISABLE },
#ifdef USE_IPFW
    { "startrule {num}",		"Initial ipfw rule number" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_RULE },
    { "startqueue {num}", 		"Initial ipfw queue number" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_QUEUE },
    { "startpipe {num}",		"Initial ipfw pipe number" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_PIPE },
    { "starttable {num}", 		"Initial ipfw table number" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_TABLE },
#endif /* USE_IPFW */
#ifdef PHYSTYPE_L2TP
    { "l2tptimeout {sec}", 		"L2TP tunnel unused timeout" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_L2TPTO },
    { "l2tplimit {num}", 		"Calls per L2TP tunnel limit" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_L2TPLIMIT },
#endif
#ifdef PHYSTYPE_PPTP
    { "pptptimeout {sec}", 		"PPTP tunnel unused timeout" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_PPTPTO },
    { "pptplimit {num}", 		"Calls per PPTP tunnel limit" ,
       	GlobalSetCommand, NULL, 2, (void *) SET_PPTPLIMIT },
#endif
    { "max-children {num}",		"Max number of children",
	GlobalSetCommand, NULL, 2, (void *) SET_MAX_CHILDREN },
    { "qthreshold {min} {max}",		"Message queue limit thresholds",
        GlobalSetCommand, NULL, 2, (void *) SET_QTHRESHOLD },
#ifdef USE_NG_BPF
    { "filter {num} add|clear [\"{flt}\"]",	"Global traffic filters management",
	GlobalSetCommand, NULL, 2, (void *) SET_FILTER },
#endif
    { NULL, NULL, NULL, NULL, 0, NULL },
  };

  static const struct confinfo	gGlobalConfList[] = {
#ifdef USE_WRAP
    { 0,	GLOBAL_CONF_TCPWRAPPER,	"tcp-wrapper"	},
#endif
    { 0,	GLOBAL_CONF_ONESHOT,	"one-shot"	},
    { 0,	GLOBAL_CONF_AGENT_CID,	"agent-cid"	},
    { 0,	GLOBAL_CONF_SESS_TIME,	"session-time"	},
    { 0,	0,			NULL		},
  };

  static const struct cmdtab CreateCommands[] = {
    { "link [template|static] {name} {template|type}",	"Create link/template",
	LinkCreate, NULL, 2, NULL },
    { "bundle [template|static] {name} {template}",	"Create bundle/template",
	BundCreate, NULL, 2, NULL },
    { NULL, NULL, NULL, NULL, 0, NULL },
  };

  static const struct cmdtab DestroyCommands[] = {
    { "link [{name}]",			"Destroy link/template",
	LinkDestroy, NULL, 2, NULL },
    { "bundle [{name}]",		"Destroy bundle/template",
	BundDestroy, NULL, 2, NULL },
    { NULL, NULL, NULL, NULL, 0, NULL },
  };

  static const struct cmdtab ShowSessCmds[] = {
    { "iface {name}",			"Filter by iface name",
	ShowSessions, NULL, 0, (void *) SHOW_IFACE },
    { "ip {ip}",			"Filter by IP address",
	ShowSessions, NULL, 0, (void *) SHOW_IP },
    { "user {name}",			"Filter by user name",
	ShowSessions, NULL, 0, (void *) SHOW_USER },
    { "session {ID}",			"Filter by session ID",
	ShowSessions, NULL, 0, (void *) SHOW_SESSION },
    { "msession {ID}",			"Filter by msession ID",
	ShowSessions, NULL, 0, (void *) SHOW_MSESSION },
    { "bundle {name}",			"Filter by bundle name",
	ShowSessions, NULL, 0, (void *) SHOW_BUNDLE },
    { "link {name}",			"Filter by link name",
	ShowSessions, NULL, 0, (void *) SHOW_LINK },
    { "peer {name}",			"Filter by peer name",
	ShowSessions, NULL, 0, (void *) SHOW_PEER },
    { NULL, NULL, NULL, NULL, 0, NULL },
  };

  static const struct cmdtab ShowCommands[] = {
    { "bundle [{name}]",		"Bundle status",
	BundStat, AdmitBund, 0, NULL },
    { "customer",			"Customer summary",
	ShowCustomer, NULL, 0, NULL },
    { "repeater [{name}]",		"Repeater status",
	RepStat, AdmitRep, 0, NULL },
    { "ccp",				"CCP status",
	CcpStat, AdmitBund, 0, NULL },
#ifdef CCP_MPPC
    { "mppc",				"MPPC status",
	MppcStat, AdmitBund, 0, NULL },
#endif
    { "ecp",				"ECP status",
	EcpStat, AdmitBund, 0, NULL },
    { "eap",				"EAP status",
	EapStat, AdmitLink, 0, NULL },
    { "events",				"Current events",
	ShowEvents, NULL, 0, NULL },
    { "ipcp",				"IPCP status",
	IpcpStat, AdmitBund, 0, NULL },
    { "ipv6cp",				"IPV6CP status",
	Ipv6cpStat, AdmitBund, 0, NULL },
    { "ippool",				"IP pool status",
	IPPoolStat, NULL, 0, NULL },
    { "iface",				"Interface status",
	IfaceStat, AdmitBund, 0, NULL },
    { "routes",				"IP routing table",
	IpShowRoutes, NULL, 0, NULL },
    { "layers",				"Layers to open/close",
	ShowLayers, NULL, 0, NULL },
    { "device",				"Physical device status",
	PhysStat, AdmitLink, 0, NULL },
#ifdef PHYSTYPE_PPTP
    { "pptp",				"PPTP tunnels status",
	PptpsStat, NULL, 0, NULL },
#endif
#ifdef PHYSTYPE_L2TP
    { "l2tp",				"L2TP tunnels status",
	L2tpsStat, NULL, 0, NULL },
#endif
    { "link",				"Link status",
	LinkStat, AdmitLink, 0, NULL },
    { "auth",				"Auth status",
	AuthStat, AdmitLink, 0, NULL },
    { "radius",				"RADIUS status",
	RadStat, AdmitLink, 0, NULL },
#ifdef RAD_COA_REQUEST
    { "radsrv",				"RADIUS server status",
	RadsrvStat, NULL, 0, NULL },
#endif
    { "lcp",				"LCP status",
	LcpStat, AdmitLink, 0, NULL },
#ifdef	USE_NG_NAT
    { "nat",				"NAT status",
	NatStat, AdmitBund, 0, NULL },
#endif
    { "mem",				"Memory map",
	MemStat, NULL, 0, NULL },
    { "console",			"Console status",
	ConsoleStat, NULL, 0, NULL },
#ifndef NOWEB
    { "web",				"Web status",
	WebStat, NULL, 0, NULL },
#endif
    { "user",				"Console users" ,
      	UserStat, NULL, 0, NULL },
    { "global",				"Global settings",
	ShowGlobal, NULL, 0, NULL },
    { "types",				"Supported device types",
	ShowTypes, NULL, 0, NULL },
    { "version",			"Version string",
	ShowVersion, NULL, 0, NULL },
    { "sessions [ {param} {value} ]",	"Active sessions",
	CMD_SUBMENU, NULL, 0, ShowSessCmds},
    { "summary",			"Daemon status summary",
	ShowSummary, NULL, 0, NULL },
#ifdef USE_NG_NETFLOW
    { "netflow",			"Netflow settings",
	ShowNetflow, NULL, 0, NULL },
#endif
    { NULL, NULL, NULL, NULL, 0, NULL },
  };

  static const struct cmdtab UnSetCommands[] = {
   { "radius ...",			"RADIUS specific stuff",
	CMD_SUBMENU, AdmitLink, 2, RadiusUnSetCmds },
#ifdef NG_NAT_DESC_LENGTH
   { "nat ...",			"NAT specific stuff",
	CMD_SUBMENU, AdmitBund, 2, NatUnSetCmds },
#endif
   { NULL, NULL, NULL, NULL, 0, NULL },
  };
  
  static const struct cmdtab SetCommands[] = {
    { "bundle ...",			"Bundle specific stuff",
	CMD_SUBMENU, AdmitBund, 2, BundSetCmds },
    { "link ...",			"Link specific stuff",
	CMD_SUBMENU, AdmitLink, 2, LinkSetCmds },
    { "iface ...",			"Interface specific stuff",
	CMD_SUBMENU, AdmitBund, 2, IfaceSetCmds },
    { "ipcp ...",			"IPCP specific stuff",
	CMD_SUBMENU, AdmitBund, 2, IpcpSetCmds },
    { "ipv6cp ...",			"IPV6CP specific stuff",
	CMD_SUBMENU, AdmitBund, 2, Ipv6cpSetCmds },
    { "ippool ...",			"IP pool specific stuff",
	CMD_SUBMENU, NULL, 2, IPPoolSetCmds },
    { "ccp ...",			"CCP specific stuff",
	CMD_SUBMENU, AdmitBund, 2, CcpSetCmds },
#ifdef CCP_MPPC
    { "mppc ...",			"MPPC specific stuff",
	CMD_SUBMENU, AdmitBund, 2, MppcSetCmds },
#endif
    { "ecp ...",			"ECP specific stuff",
	CMD_SUBMENU, AdmitBund, 2, EcpSetCmds },
    { "eap ...",			"EAP specific stuff",
	CMD_SUBMENU, AdmitLink, 2, EapSetCmds },
    { "auth ...",			"Auth specific stuff",
	CMD_SUBMENU, AdmitLink, 2, AuthSetCmds },
    { "radius ...",			"RADIUS specific stuff",
	CMD_SUBMENU, AdmitLink, 2, RadiusSetCmds },
#ifdef RAD_COA_REQUEST
    { "radsrv ...",			"RADIUS server specific stuff",
	CMD_SUBMENU, NULL, 2, RadsrvSetCmds },
#endif
    { "console ...",			"Console specific stuff",
	CMD_SUBMENU, NULL, 0, ConsoleSetCmds },
#ifndef NOWEB
    { "web ...",			"Web specific stuff",
	CMD_SUBMENU, NULL, 2, WebSetCmds },
#endif
    { "user {name} {password} [{priv}]",	"Add console user" ,
      	UserCommand, NULL, 2, NULL },
    { "global ...",			"Global settings",
	CMD_SUBMENU, NULL, 2, GlobalSetCmds },
#ifdef USE_NG_NETFLOW
    { "netflow ...", 			"NetFlow settings",
	CMD_SUBMENU, NULL, 2, NetflowSetCmds },
#endif
#ifdef	USE_NG_NAT
    { "nat ...", 			"Nat settings",
	CMD_SUBMENU, NULL, 2, NatSetCmds },
#endif
    { "debug level",			"Set netgraph debug level",
	SetDebugCommand, NULL, 2, NULL },
#define _WANT_DEVICE_CMDS
#include "devices.h"
    { NULL, NULL, NULL, NULL, 0, NULL },
  };

  const struct cmdtab gCommands[] = {
    { "authname {name} [CI]",		"Choose link by auth name",
	AuthnameCommand, NULL, 0, NULL },
    { "bundle [{name}]",		"Choose/list bundles",
	BundCommand, NULL, 0, NULL },
    { "close [{layer}]",		"Close a layer",
	CloseCommand, NULL, 1, NULL },
    { "create ...",			"Create new item",
    	CMD_SUBMENU, NULL, 2, CreateCommands },
    { "destroy ...",			"Destroy item",
    	CMD_SUBMENU, NULL, 2, DestroyCommands },
    { "exit",				"Exit console",
	ExitCommand, NULL, 0, NULL },
    { "iface {iface}",			"Choose bundle by iface",
	IfaceCommand, NULL, 0, NULL },
    { "help ...",			"Help on any command",
	HelpCommand, NULL, 0, NULL },
    { "link {name}",			"Choose link",
	LinkCommand, NULL, 0, NULL },
    { "load [{file}] {label}",		"Read from config file",
	LoadCommand, NULL, 0, NULL },
    { "log [+/-{opt} ...]",		"Set/view log options",
	LogCommand, NULL, 2, NULL },
    { "msession {msesid}",		"Ch. bundle by msession-id",
	MSessionCommand, NULL, 0, NULL },
    { "open [{layer}]",			"Open a layer",
	OpenCommand, NULL, 1, NULL },
    { "shutdown",			"Shutdown program",
	QuitCommand, NULL, 2, NULL },
    { "repeater [{name}]",		"Choose/list repeaters",
	RepCommand, NULL, 0, NULL },
    { "session {sesid}",		"Choose link by session-id",
	SessionCommand, NULL, 0, NULL },
    { "set ...",			"Set parameters",
	CMD_SUBMENU, NULL, 0, SetCommands },
	{ "unset ...",			"Unset parameters",
	CMD_SUBMENU, NULL, 0, UnSetCommands },
    { "show ...",			"Show status",
	CMD_SUBMENU, NULL, 0, ShowCommands },
    { NULL, NULL, NULL, NULL, 0, NULL },
  };



/*
 * Layers
 */

  static struct layer	gLayers[] = {
    { "iface",
      IfaceOpenCmd,
      IfaceCloseCmd,
      AdmitBund,
      "System interface"
    },
    { "ipcp",
      IpcpOpenCmd,
      IpcpCloseCmd,
      AdmitBund,
      "IPCP: IP control protocol"
    },
    { "ipv6cp",
      Ipv6cpOpenCmd,
      Ipv6cpCloseCmd,
      AdmitBund,
      "IPV6CP: IPv6 control protocol"
    },
    { "ccp",
      CcpOpenCmd,
      CcpCloseCmd,
      AdmitBund,
      "CCP: compression ctrl prot."
    },
    { "ecp",
      EcpOpenCmd,
      EcpCloseCmd,
      AdmitBund,
      "ECP: encryption ctrl prot."
    },
    { "bund",
      BundOpenCmd,
      BundCloseCmd,
      AdmitBund,
      "Multilink bundle"
    },
    { "link",
      LinkOpenCmd,
      LinkCloseCmd,
      AdmitLink,
      "Link layer"
    },
    { "device",
      PhysOpenCmd,
      PhysCloseCmd,
      AdmitLink,
      "Physical link layer"
    },
  };

  #define NUM_LAYERS	(sizeof(gLayers) / sizeof(*gLayers))

/*
 * DoCommand()
 *
 * Executes command. Returns TRUE if user wants to quit.
 */

int
DoCommand(Context ctx, int ac, const char *const av[], const char *file, int line)
{
    int		rtn, i;
    char	filebuf[100], cmd[256];

    ctx->errmsg[0] = 0;
    rtn = DoCommandTab(ctx, gCommands, ac, av);

    if (rtn) {
	if (file) {
	    snprintf(filebuf,sizeof(filebuf),"%s:%d: ", file, line);
	} else {
	    filebuf[0] = 0;
	}
	cmd[0] = 0;
	for (i = 0; i < ac; i++) {
	    if (i)
		strlcat(cmd, " ", sizeof(cmd));
	    strlcat(cmd, av[i], sizeof(cmd));
	}
    }

    switch (rtn) {
	case 0:
	    break;
    
	default:
	case CMD_ERR_USAGE:
	case CMD_ERR_UNFIN:
	    HelpCommand(ctx, ac, av, filebuf);
	    Log2(LG_ERR, ("%sError in '%s'", filebuf, cmd));
	    break;
	case CMD_ERR_UNDEF:
    	    Printf("%sUnknown command: '%s'. Try \"help\".\r\n", filebuf, cmd);
	    Log2(LG_ERR, ("%sUnknown command: '%s'. Try \"help\".", filebuf, cmd));
	    break;
	case CMD_ERR_AMBIG:
    	    Printf("%sAmbiguous command: '%s'. Try \"help\".\r\n", filebuf, cmd);
	    Log2(LG_ERR, ("%sAmbiguous command: '%s'. Try \"help\".", filebuf, cmd));
	    break;
	case CMD_ERR_RECUR:
    	    Printf("%sRecursion detected for: '%s'!\r\n", filebuf, cmd);
	    Log2(LG_ERR, ("%sRecursion detected for: '%s'!", filebuf, cmd));
	    break;
	case CMD_ERR_NOCTX:
    	    Printf("%sIncorrect context for: '%s'\r\n", filebuf, cmd);
	    Log2(LG_ERR, ("%sIncorrect context for: '%s'", filebuf, cmd));
	    break;
	case CMD_ERR_OTHER:
    	    Printf("%sError in '%s': %s\r\n", filebuf, cmd, ctx->errmsg);
	    Log2(LG_ERR, ("%sError in '%s': %s", filebuf, cmd, ctx->errmsg));
	    break;
    }
  
    return(rtn);
}

/*
 * DoCommandTab()
 *
 * Execute command from given command menu
 */

int
DoCommandTab(Context ctx, CmdTab cmdlist, int ac, const char *const av[])
{
    CmdTab	cmd;
    int		rtn = 0;

    /* Huh? */
    if (ac <= 0)
	return(CMD_ERR_UNFIN);

    /* Find command */
    if ((rtn = FindCommand(ctx, cmdlist, av[0], &cmd)))
	return(rtn);

    /* Check command admissibility */
    if (cmd->admit && !(cmd->admit)(ctx, cmd))
	return(CMD_ERR_NOCTX);

    /* Find command and either execute or recurse into a submenu */
    if (cmd->func == CMD_SUBMENU) {
        if ((intptr_t)cmd->arg == (intptr_t)ShowSessCmds) {
            if (ac > 1)
	        rtn = DoCommandTab(ctx, (CmdTab) cmd->arg, ac - 1, av + 1);
            else
                rtn = ShowSessions(ctx, 0, NULL, NULL);
        } else
            rtn = DoCommandTab(ctx, (CmdTab) cmd->arg, ac - 1, av + 1);
    } else
	rtn = (cmd->func)(ctx, ac - 1, av + 1, cmd->arg);

    return(rtn);
}

/*
 * FindCommand()
 */

int
FindCommand(Context ctx, CmdTab cmds, const char *str, CmdTab *cmdp)
{
    int		nmatch;
    int		len = strlen(str);

    for (nmatch = 0; cmds->name; cmds++) {
	if (cmds->name && !strncmp(str, cmds->name, len) &&
	  cmds->priv <= ctx->priv) {
	    *cmdp = cmds;
    	    nmatch++;
	}
    }
    switch (nmatch) {
	case 0:
    	    return(CMD_ERR_UNDEF);
	case 1:
    	    return(0);
	default:
    	    return(CMD_ERR_AMBIG);
    }
}

/********** COMMANDS **********/


/*
 * GlobalSetCommand()
 */

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

  if (ac == 0)
    return(-1);

  switch ((intptr_t)arg) {
    case SET_ENABLE:
      EnableCommand(ac, av, &gGlobalConf.options, gGlobalConfList);
      break;

    case SET_DISABLE:
      DisableCommand(ac, av, &gGlobalConf.options, gGlobalConfList);
      break;

#ifdef USE_IPFW
    case SET_RULE:
	if (rule_pool) 
	    Error("Rule pool is not empty. Impossible to set initial number");
	else {
	    val = atoi(*av);
	    if (val <= 0 || val>=65535)
		Error("Incorrect rule number");
	    else
		rule_pool_start = val;
	}
      break;

    case SET_QUEUE:
	if (queue_pool) 
	    Error("Queue pool is not empty. Impossible to set initial number");
	else {
	    val = atoi(*av);
	    if (val <= 0 || val>=65535)
		Error("Incorrect queue number");
	    else
		queue_pool_start = val;
	}
      break;

    case SET_PIPE:
	if (pipe_pool) 
	    Error("Pipe pool is not empty. Impossible to set initial number");
	else {
	    val = atoi(*av);
	    if (val <= 0 || val>=65535)
		Error("Incorrect pipe number");
	    else
		pipe_pool_start = val;
	}
      break;

    case SET_TABLE:
	if (table_pool) 
	    Error("Table pool is not empty. Impossible to set initial number");
	else {
	    val = atoi(*av);
	    if (val <= 0 || val>127) /* table 0 is usually possible but we deny it */
		Error("Incorrect table number");
	    else
		table_pool_start = val;
	}
      break;
#endif /* USE_IPFW */

#ifdef PHYSTYPE_L2TP
    case SET_L2TPTO:
	val = atoi(*av);
	if (val < 0 || val > 1000000)
	    Error("Incorrect L2TP timeout");
	else
	    gL2TPto = val;
      break;

    case SET_L2TPLIMIT:
	val = atoi(*av);
	if (val < 0 || val > 65536)
	    Error("Incorrect L2TP call limit");
	else
	    gL2TPtunlimit = (unsigned)val;
      break;
#endif

#ifdef PHYSTYPE_PPTP
    case SET_PPTPTO:
	val = atoi(*av);
	if (val < 0 || val > 1000000)
	    Error("Incorrect PPTP timeout");
	else
	    gPPTPto = val;
      break;

    case SET_PPTPLIMIT:
	val = atoi(*av);
	if (val <= 0 || val > 65536)
	    Error("Incorrect PPTP call limit");
	else
	    gPPTPtunlimit = (unsigned)val;
      break;
#endif

    case SET_MAX_CHILDREN:
	val = atoi(*av);
	if (val < 0 || val > 65536)
	    Error("Incorrect children links limit");
	else
	    gMaxChildren = val;
      break;

#ifdef USE_NG_BPF
    case SET_FILTER:
	if (ac == 4 && strcasecmp(av[1], "add") == 0) {
	    struct acl	**acls, *acls1;
	    int		i;
	    
	    i = atol(av[0]);
	    if (i <= 0 || i > ACL_FILTERS)
		Error("Wrong filter number\r\n");
	    acls = &(acl_filters[i - 1]);
	    
	    i = atol(av[2]);
	    if (i <= 0)
	        Error("Wrong acl number: %i\r\n", i);

	    acls1 = Malloc(MB_AUTH, sizeof(struct acl) + strlen(av[3]));
	    acls1->number = i;
	    acls1->real_number = 0;
	    strcpy(acls1->rule, av[3]);
	    while ((*acls != NULL) && ((*acls)->number < acls1->number))
	      acls = &((*acls)->next);

	    if (*acls == NULL) {
	        acls1->next = NULL;
	    } else if ((*acls)->number == acls1->number) {
		Freee(acls1);
	        Error("Duplicate acl\r\n");
	    } else {
	        acls1->next = *acls;
	    }
	    *acls = acls1;

	} else if (ac == 2 && strcasecmp(av[1], "clear") == 0) {
	    struct acl          *acls, *acls1;
	    int i;
	    
	    i = atoi(av[0]);
	    if (i <= 0 || i > ACL_FILTERS)
		Error("Wrong filter number\r\n");
	    	    
	    acls = acl_filters[i - 1];
	    while (acls != NULL) {
	        acls1 = acls->next;
	        Freee(acls);
	        acls = acls1;
	    };
	    acl_filters[i - 1] = NULL;
	} else
	    return(-1);
	break;
#endif /* USE_NG_BPF */
	
    case SET_QTHRESHOLD:
        if (ac == 2) {
            int val_max;

            val = atoi(av[0]);
            if (val < 0 || val >= MSG_QUEUE_LEN-1)
                Error("Incorrect minimum threshold for message queue, "
                      "must be between 0 and %d", MSG_QUEUE_LEN-1);
            val_max = atoi(av[1]);
            if (val_max <= val || val_max >= MSG_QUEUE_LEN)
                Error("Incorrect maximum threshold for message queue, "
                      "must be greater than minimum and less than %d",
                      MSG_QUEUE_LEN);
            gQThresMin = val;
            gQThresMax = val_max;
            gQThresDiff = val_max - val;
        }
        else
            return (-1);
        break;
    default:
      return(-1);
  }

  return 0;
}

/*
 * HelpCommand()
 */

int
HelpCommand(Context ctx, int ac, const char *const av[], const void *arg)
{
  int		depth, i;
  CmdTab	menu, cmd;
  char		*mark, *mark_save;
  const char	*errfmt;
  char		buf[256];
  int		err;

  for (mark = buf, depth = *buf = 0, menu = gCommands;
      depth < ac;
      depth++, menu = (CmdTab) cmd->arg) {
    if ((err = FindCommand(ctx, menu, av[depth], &cmd))) {
      int k;

      for (*buf = k = 0; k <= depth; k++)
	snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s%c",
	  av[k], k == depth ? '\0' : ' ');
      if (err == CMD_ERR_AMBIG)
	  errfmt = "%sAmbiguous command: '%s'.\r\n";
      else 
          errfmt = "%sUnknown command: '%s'.\r\n";
      if (arg) {
        Printf(errfmt, (const char*)arg, buf);
      } else {
        Printf(errfmt, "", buf);
      }
      return(0);
    }
    sprintf(mark, depth ? " %s" : "%s", cmd->name);
    mark_save = mark;
    if ((mark = strchr(mark + 1, ' ')) == NULL)
      mark = mark_save + strlen(mark_save);
    if (cmd->func != CMD_SUBMENU)
    {
      Printf("Usage: %s\r\n", buf);
      return(0);
    }
  }

    /* Show list of available commands in this submenu */
    *mark = 0;
    if (!*buf)
	Printf("Available commands:\r\n");
    else
	Printf("Commands available under \"%s\":\r\n", buf);
    i = 0;
    for (cmd = menu; cmd->name; cmd++) {
	if (cmd->priv > ctx->priv)
	    continue;
	strlcpy(buf, cmd->name, sizeof(buf));
	if ((mark = strchr(buf, ' ')))
    	    *mark = 0;
	Printf(" %-9s: %-20s%s", buf, cmd->desc,
    	    (i & 1)? "\r\n" : "\t");
	i++;
    }
    if (i & 1)
	Printf("\r\n");
    return(0);
}

/*
 * SetDebugCommand()
 */

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

  switch (ac) {
    case 1:
      NgSetDebug(atoi(av[0]));
      break;
    default:
      return(-1);
  }
  return(0);
}

/*
 * ShowVersion()
 */

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

  Printf("MPD version: %s\r\n", gVersion);
  Printf("  Available features:\r\n");
#ifdef	USE_IPFW
  Printf("	ipfw rules	: yes\r\n");
#else
  Printf("	ipfw rules	: no\r\n");
#endif
#ifdef	USE_FETCH
  Printf("	config fetch	: yes\r\n");
#else
  Printf("	config fetch	: no\r\n");
#endif
#ifdef USE_BACKTRACE
  Printf("	backtrace()	: yes\r\n");
#else
  Printf("	backtrace()	: no\r\n");
#endif
#ifdef	USE_NG_BPF
  Printf("	ng_bpf		: yes\r\n");
#else
  Printf("	ng_bpf		: no\r\n");
#endif
#ifdef	USE_NG_CAR
  Printf("	ng_car		: yes\r\n");
#else
  Printf("	ng_car		: no\r\n");
#endif
#ifdef	USE_NG_DEFLATE
  Printf("	ng_deflate	: yes\r\n");
#else
  Printf("	ng_deflate	: no\r\n");
#endif
#ifdef	USE_NG_IPACCT
  Printf("	ng_ipacct	: yes\r\n");
#else
  Printf("	ng_ipacct	: no\r\n");
#endif
#ifdef  CCP_MPPC
  Printf("	ng_mppc (MPPC)	: %s\r\n", MPPCPresent?"yes":"no");
  Printf("	ng_mppc (MPPE)	: %s\r\n", MPPEPresent?"yes":"no");
#else
  Printf("	ng_mppc (MPPC)	: no\r\n");
  Printf("	ng_mppc (MPPE)	: no\r\n");
#endif
#ifdef	USE_NG_NAT
  Printf("	ng_nat		: yes\r\n");
#ifdef NG_NAT_DESC_LENGTH
  Printf("	nat redirect	: yes\r\n");
#else
  Printf("	nat redirect	: no\r\n");
#endif
#else
  Printf("	ng_nat		: no\r\n");
#endif
#ifdef	USE_NG_NETFLOW
  Printf("	ng_netflow	: yes\r\n");
#if NGM_NETFLOW_COOKIE >= 1309868867
  Printf("	netflow v9	: yes\r\n");
#else
  Printf("	netflow v9	: no\r\n");
#endif
#else
  Printf("	ng_netflow	: no\r\n");
#endif
#ifdef	USE_NG_PRED1
  Printf("	ng_pred1	: yes\r\n");
#else
  Printf("	ng_pred1	: emulated\r\n");
#endif
#ifdef	USE_NG_TCPMSS
  Printf("	ng_tcpmss	: yes\r\n");
#else
  Printf("	ng_tcpmss	: emulated\r\n");
#endif
  return(0);
}

/*
 * ShowEvents()
 */

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

  EventDump(ctx);
  return(0);
}

/*
 * ShowGlobal()
 */

static int
ShowGlobal(Context ctx, int ac, const char *const av[], const void *arg)
{
#ifdef USE_NG_BPF
    int	k;
#endif

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

    Printf("Global settings:\r\n");
#ifdef USE_IPFW
    Printf("	startrule	: %d\r\n", rule_pool_start);
    Printf("	startpipe	: %d\r\n", pipe_pool_start);
    Printf("	startqueue	: %d\r\n", queue_pool_start);
    Printf("	starttable	: %d\r\n", table_pool_start);
#endif
#ifdef PHYSTYPE_L2TP
    Printf("	l2tptimeout	: %d\r\n", gL2TPto);
    Printf("	l2tplimit	: %u\r\n", gL2TPtunlimit);
#endif
#ifdef PHYSTYPE_PPTP
    Printf("	pptptimeout	: %d\r\n", gPPTPto);
    Printf("	pptplimit	: %u\r\n", gPPTPtunlimit);
#endif
    Printf("	max-children	: %d\r\n", gMaxChildren);
    Printf("	qthreshold	: %d %d\r\n", gQThresMin, gQThresMax);
    Printf("Global options:\r\n");
    OptStat(ctx, &gGlobalConf.options, gGlobalConfList);
#ifdef USE_NG_BPF
    Printf("Global traffic filters:\r\n");
    for (k = 0; k < ACL_FILTERS; k++) {
        struct acl *a = acl_filters[k];
        while (a) {
    	    Printf("\t%d#%d\t: '%s'\r\n", (k + 1), a->number, a->rule);
    	    a = a->next;
	}
    }
#endif
    Printf("Global state:\r\n");
    Printf("	children	: %d\r\n", gChildren);
  return 0;
}


/*
 * ExitCommand()
 */

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

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

    if (ctx->cs)
	ctx->cs->exit = TRUE;
    return(0);
}

/*
 * QuitCommand()
 */

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

    if (ctx->cs)
	ctx->cs->exit = TRUE;
    SendSignal(SIGTERM);
    return(0);
}

/*
 * LoadCommand()
 */

static int
LoadCommand(Context ctx, int ac, const char *const av[], const void *arg)
{
    char filename[128];
#ifdef USE_FETCH
    char buf[1024];
    FILE *f = NULL;
    FILE *rf = NULL;
    int fetch = 0;
#endif

    (void)arg;
    if (ac < 1 || ac > 2)
	return(-1);
    else {
	if (ctx->depth > 20)
    	    return(CMD_ERR_RECUR);
	ctx->depth++;

	if (ac == 1)
	    strlcpy(filename, gConfigFile, sizeof(filename));
	else {
#ifdef USE_FETCH
	    if (strncmp(av[0], "http://", 7) == 0 ||
		strncmp(av[0], "https://", 8) == 0 ||
		strncmp(av[0], "ftp://", 6) == 0) {
		    fetch = 1;
		strcpy(filename, "/tmp/mpd.conf.XXXXXX");
		mkstemp(filename);
		f = fopen(filename, "w");
		if (f == NULL) {
		    Printf("Can't create temporal file\r\n");
		    goto out;
		}
		rf = fetchGetURL(av[0], "");
		if (rf == NULL) {
		    Printf("Can't fetch '%s'\r\n", av[0]);
		    fclose(f);
		    goto out;
		}
		Printf("Fetching '%s' ...", av[0]);
		while (!feof(rf)) {
		    int b = fread(buf, 1, sizeof(buf), rf);
		    fwrite(buf, b, 1, f);
		}
		Printf(" done\r\n");
		fclose(f);
	    } else
#endif
		strlcpy(filename, av[0], sizeof(filename));
	}
	if (ReadFile(filename, av[ac - 1], DoCommand, ctx) < 0)
	    Printf("can't read configuration for \"%s\" from \"%s\"\r\n",
		av[ac -1], filename);
#ifdef USE_FETCH
out:	if (fetch)
	    unlink(filename);
#endif
	ctx->depth--;
    }
    return(0);
}

/*
 * OpenCommand()
 */

static int
OpenCommand(Context ctx, int ac, const char *const av[], const void *arg)
{
    Layer	layer;
    const char	*name;

    (void)arg;
    switch (ac) {
    case 0:
        name = DEFAULT_OPEN_LAYER;
        break;
    case 1:
        name = av[0];
        break;
    default:
        return(-1);
    }
    if ((layer = GetLayer(name)) != NULL) {
	/* Check command admissibility */
	if (!layer->admit || (layer->admit)(ctx, NULL))
	    return (*layer->opener)(ctx);
	else
	    return (CMD_ERR_NOCTX);
    }
    return(-1);
}

/*
 * CloseCommand()
 */

static int
CloseCommand(Context ctx, int ac, const char *const av[], const void *arg)
{
    Layer	layer;
    const char	*name;

    (void)arg;
    switch (ac) {
    case 0:
      name = DEFAULT_OPEN_LAYER;
      break;
    case 1:
      name = av[0];
      break;
    default:
      return(-1);
    }
    if ((layer = GetLayer(name)) != NULL) {
	/* Check command admissibility */
	if (!layer->admit || (layer->admit)(ctx, NULL))
	    return (*layer->closer)(ctx);
	else
	    return (CMD_ERR_NOCTX);
    }
    return(-1);
}

/*
 * GetLayer()
 */

static Layer
GetLayer(const char *name)
{
  size_t	k;
  int		found;

  if (name == NULL)
    name = "iface";
  for (found = -1, k = 0; k < NUM_LAYERS; k++) {
    if (!strncasecmp(name, gLayers[k].name, strlen(name))) {
      if (found > 0) {
	Log(LG_ERR, ("%s: ambiguous", name));
	return(NULL);
      } else
	found = k;
    }
  }
  if (found < 0) {
    Log(LG_ERR, ("unknown layer \"%s\": try \"show layers\"", name));
    return(NULL);
  }
  return(&gLayers[found]);
}

/*
 * ShowLayers()
 */

static int
ShowLayers(Context ctx, int ac, const char *const av[], const void *arg)
{
  size_t	k;

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

  Printf("\tName\t\tDescription\r\n");
  Printf("\t----\t\t-----------\r\n");
  for (k = 0; k < NUM_LAYERS; k++)
    Printf("\t%s\t\t%s\r\n", gLayers[k].name, gLayers[k].desc);
  return(0);
}

/*
 * ShowTypes()
 */

static int
ShowTypes(Context ctx, int ac, const char *const av[], const void *arg)
{
  const struct phystype	*pt;
  int		k;

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

  Printf("\tName\t\tDescription\r\n");
  Printf("\t----\t\t-----------\r\n");
  for (k = 0; (pt = gPhysTypes[k]); k++)
    Printf("\t%s\t\t%s\r\n", pt->name, pt->descr);
  return(0);
}

/*
 * ShowSummary()
 */

static int
ShowSummary(Context ctx, int ac, const char *const av[], const void *arg)
{
  int		b, l, f;
  Bund		B;
  Link  	L;
  Rep		R;
  char	buf[64];

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

  Printf("Current daemon status summary\r\n");
  Printf("Iface\tBund\t\tLink\tLCP\tDevice\t\tUser\t\tFrom\r\n");
  for (b = 0; b<gNumLinks; b++) {
    if ((L=gLinks[b]) != NULL && L->bund == NULL && L->rep == NULL) {
	Printf("\t\t\t");
	Printf("%s\t%s\t", 
	    L->name,
	    FsmStateName(L->lcp.fsm.state));
	PhysGetPeerAddr(L, buf, sizeof(buf));
	Printf("%s\t%s\t%8s\t%s", 
	    (L->type?L->type->name:""),
	    gPhysStateNames[L->state],
	    L->lcp.auth.params.authname,
	    buf
	);
	Printf("\r\n");
    }
  }
  for (b = 0; b<gNumBundles; b++) {
    if ((B=gBundles[b]) != NULL) {
	Printf("%s\t%s\t%s\t", B->iface.ifname, B->name, (B->iface.up?"Up":"Down"));
	f = 1;
	if (B->n_links == 0)
	    Printf("\r\n");
	else for (l = 0; l < NG_PPP_MAX_LINKS; l++) {
	    if ((L=B->links[l]) != NULL) {
		if (f == 1)
		    f = 0;
		else
		    Printf("\t\t\t");
		PhysGetPeerAddr(L, buf, sizeof(buf));
		Printf("%s\t%s\t%s\t%s\t%8s\t%s", 
		    L->name,
		    FsmStateName(L->lcp.fsm.state),
		    (L->type?L->type->name:""),
		    gPhysStateNames[L->state],
		    L->lcp.auth.params.authname,
		    buf
		    );
		Printf("\r\n");
	    }
	}
    }
  }
  for (b = 0; b < gNumReps; b++) {
    if ((R = gReps[b]) != NULL) {
	Printf("Repeater\t%s\t", R->name);
	f = 1;
	for (l = 0; l < 2; l++) {
	    if ((L = R->links[l])!= NULL) {
		if (f)
		    f = 0;
		else
		    Printf("\t\t\t");
		PhysGetPeerAddr(L, buf, sizeof(buf));
		Printf("%s\t%s\t%s\t%s\t%8s\t%s", 
		    L->name,
		    "",
		    (L->type?L->type->name:""),
		    gPhysStateNames[L->state],
		    "",
		    buf
		    );
		Printf("\r\n");
	    }
	}
	if (f)
	    Printf("\r\n");
    }
  }
  return(0);
}

/*
 * ShowSessions()
 */

static int
ShowSessions(Context ctx, int ac, const char *const av[], const void *arg)
{
    int		l;
    Bund	B;
    Link  	L;
    char	peer[64], addr[64], buf[64];

    if (ac != 0 && ac != 1)
	return (-1);

    for (l = 0; l < gNumLinks; l++) {
	if ((L=gLinks[l]) != NULL && L->session_id[0] && L->bund) {
	    B = L->bund;
	    u_addrtoa(&B->iface.peer_addr, addr, sizeof(addr));
	    PhysGetPeerAddr(L, peer, sizeof(peer));
	    if (ac == 0)
	        goto out;
	    switch ((intptr_t)arg) {
		case SHOW_IFACE:
		    if (strcmp(av[0], B->iface.ifname))
			continue;
		    break;
		case SHOW_IP:
		    if (strcmp(av[0], addr))
			continue;
		    break;
		case SHOW_USER:
		    if (strcmp(av[0], L->lcp.auth.params.authname))
			continue;
		    break;
		case SHOW_MSESSION:
		    if (strcmp(av[0], B->msession_id))
			continue;
		    break;
		case SHOW_SESSION:
		    if (strcmp(av[0], L->session_id))
			continue;
		    break;
		case SHOW_BUNDLE:
		    if (strcmp(av[0], B->name))
			continue;
		    break;
		case SHOW_LINK:
		    if (av[0][0] == '[') {
			int k;
			if (sscanf(av[0], "[%x]", &k) != 1)
			    return (-1);
			else {
			    if (L->id != k)
				continue;
			}
		    } else {
			if (strcmp(av[1], L->name))
			    continue;
		    }
		    break;
		case SHOW_PEER:
		    if (strcmp(av[0], peer))
			continue;
		    break;
		default:
		    return (-1);
	    }
out:
	    Printf("%s\t%s\t%s\t%s\t", B->iface.ifname,
		addr, B->name, B->msession_id);
	    Printf("%s\t%d\t%s\t%s\t%s", 
		L->name,
		L->id,
		L->session_id,
		L->lcp.auth.params.authname,
		peer
	    );
	    if (Enabled(&gGlobalConf.options, GLOBAL_CONF_AGENT_CID)) {
		PhysGetSelfName(L, buf, sizeof(buf));
		Printf("\t%s", buf);
	    }
	    if (Enabled(&gGlobalConf.options, GLOBAL_CONF_SESS_TIME)) {
		if (L->state == PHYS_STATE_UP)
		    Printf("\t%ld", (long int)(time(NULL) - L->last_up));
	    }
	    Printf("\r\n");
	}
    }
    return(0);
}

/*
 * ShowCustomer()
 */

static int
ShowCustomer(Context ctx, int ac, const char *const av[], const void *arg)
{
    Link	l = ctx->lnk;
    Bund	b = ctx->bund;
    IfaceState	iface;
    IfaceRoute	r;
    int		j;
    char	buf[64];
#if defined(USE_NG_BPF) || defined(USE_IPFW)
    struct acl	*a;
#endif

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

    if (b && b->iface.ifname[0]) {
	iface = &b->iface;
	Printf("Interface:\r\n");
	Printf("\tName            : %s\r\n", iface->ifname);
	Printf("\tStatus          : %s\r\n", iface->up ? (iface->dod?"DoD":"UP") : "DOWN");
	if (iface->up) {
	    Printf("\tSession time    : %ld seconds\r\n", (long int)(time(NULL) - iface->last_up));
#ifdef USE_NG_BPF
	    if (b->params.idle_timeout || iface->idle_timeout)
		Printf("\tIdle timeout    : %d seconds\r\n", b->params.idle_timeout?b->params.idle_timeout:iface->idle_timeout);
#endif
	    if (b->params.session_timeout || iface->session_timeout)
		Printf("\tSession timeout : %d seconds\r\n", b->params.session_timeout?b->params.session_timeout:iface->session_timeout);
	    Printf("\tMTU             : %d bytes\r\n", iface->mtu);
	}
	if (iface->ip_up && !u_rangeempty(&iface->self_addr)) {
	    Printf("\tIP Addresses    : %s -> ", u_rangetoa(&iface->self_addr,buf,sizeof(buf)));
	    Printf("%s\r\n", u_addrtoa(&iface->peer_addr,buf,sizeof(buf)));
	}
	if (iface->ipv6_up && !u_addrempty(&iface->self_ipv6_addr)) {
	    Printf("\tIPv6 Addresses  : %s%%%s -> ", 
		u_addrtoa(&iface->self_ipv6_addr,buf,sizeof(buf)), iface->ifname);
	    Printf("%s%%%s\r\n", u_addrtoa(&iface->peer_ipv6_addr,buf,sizeof(buf)), iface->ifname);
	}
	if (iface->up) {
	    if (!SLIST_EMPTY(&iface->routes) || !SLIST_EMPTY(&b->params.routes)) {
    		Printf("\tRoutes via peer :\r\n");
		SLIST_FOREACH(r, &iface->routes, next)
		    Printf("\t\t: %s\r\n", u_rangetoa(&r->dest,buf,sizeof(buf)));
		SLIST_FOREACH(r, &b->params.routes, next)
		    Printf("\t\t: %s\r\n", u_rangetoa(&r->dest,buf,sizeof(buf)));
	    }
#ifdef USE_IPFW
	    if (b->params.acl_pipe) {
		Printf("\tIPFW pipes      :\r\n");
		a = b->params.acl_pipe;
		while (a) {
		    Printf("\t\t%d (%d)\t: '%s'\r\n", a->number, a->real_number, a->rule);
		    a = a->next;
		}
	    }
	    if (b->params.acl_queue) {
		Printf("\tIPFW queues     :\r\n");
		a = b->params.acl_queue;
		while (a) {
		    Printf("\t\t%d (%d)\t: '%s'\r\n", a->number, a->real_number, a->rule);
	    	    a = a->next;
		}
	    }
	    if (b->params.acl_table) {
		Printf("\tIPFW tables     :\r\n");
		a = b->params.acl_table;
		while (a) {
		    if (a->number != 0)
			Printf("\t\t%d (%d)\t: '%s'\r\n", a->number, a->real_number, a->rule);
		    else
			Printf("\t\t(%d)\t: '%s'\r\n", a->real_number, a->rule);
		    a = a->next;
		}
	    }
	    if (b->params.acl_rule) {
		Printf("\tIPFW rules      :\r\n");
		a = b->params.acl_rule;
		while (a) {
	    	    Printf("\t\t%d (%d)\t: '%s'\r\n", a->number, a->real_number, a->rule);
	    	    a = a->next;
		}
	    }
#endif /* USE_IPFW */
#ifdef USE_NG_BPF
	    if (b->params.acl_limits[0] || b->params.acl_limits[1]) {
		int k;
		Printf("\tTraffic filters :\r\n");
		for (k = 0; k < ACL_FILTERS; k++) {
		    a = b->params.acl_filters[k];
		    if (a == NULL)
			a = acl_filters[k];
		    while (a) {
			Printf("\t\t%d#%d\t: '%s'\r\n", (k + 1), a->number, a->rule);
			a = a->next;
		    }
		}
		Printf("\tTraffic limits  :\r\n");
		for (k = 0; k < 2; k++) {
		    a = b->params.acl_limits[k];
		    while (a) {
			Printf("\t\t%s#%d%s%s\t: '%s'\r\n", (k?"out":"in"), a->number,
			    ((a->name[0])?"#":""), a->name, a->rule);
			a = a->next;
		    }
		}
	    }
#endif /* USE_NG_BPF */
	}
    }
    if (b) {
	Printf("Bundle %s%s:\r\n", b->name, b->tmpl?" (template)":(b->stay?" (static)":""));
	Printf("\tStatus          : %s\r\n", b->open ? "OPEN" : "CLOSED");
	Printf("\tMulti Session Id: %s\r\n", b->msession_id);
	Printf("\tPeer authname   : \"%s\"\r\n", b->params.authname);
	if (b->n_up)
    	    Printf("\tSession time    : %ld seconds\r\n", (long int)(time(NULL) - l->last_up));

	if (b->peer_mrru) {
	    Printf("\tMultilink PPP:\r\n");
    	    Printf("\t\tPeer auth name : \"%s\"\r\n", b->params.authname);
    	    Printf("\t\tPeer discrimin.: %s\r\n", MpDiscrimText(&b->peer_discrim, buf, sizeof(buf)));
	}

	if (!b->tmpl) {
	    /* Show stats */
	    BundUpdateStats(b);
	    Printf("\tTraffic stats:\r\n");

	    Printf("\t\tInput octets   : %llu\r\n", (unsigned long long)b->stats.recvOctets);
	    Printf("\t\tInput frames   : %llu\r\n", (unsigned long long)b->stats.recvFrames);
	    Printf("\t\tOutput octets  : %llu\r\n", (unsigned long long)b->stats.xmitOctets);
	    Printf("\t\tOutput frames  : %llu\r\n", (unsigned long long)b->stats.xmitFrames);
	    Printf("\t\tBad protocols  : %llu\r\n", (unsigned long long)b->stats.badProtos);
	    Printf("\t\tRunts          : %llu\r\n", (unsigned long long)b->stats.runts);
	}
    }
    for (j = 0; j < NG_PPP_MAX_LINKS; j++) {
	if (b)
	    l = b->links[j];
	else if (j != 0)
	    l = NULL;
	if (l) {
	    Printf("Link %s:\r\n", l->name);
	    Printf("\tDevice type     : %s\r\n", l->type?l->type->name:"");
	    Printf("\tStatus          : %s/%s\r\n",
		FsmStateName(l->lcp.fsm.state),
		gPhysStateNames[l->state]);
	    Printf("\tSession Id      : %s\r\n", l->session_id);
	    Printf("\tPeer ident      : %s\r\n", l->lcp.peer_ident);
	    if (l->state == PHYS_STATE_UP)
    		Printf("\tSession time    : %ld seconds\r\n", (long int)(time(NULL) - l->last_up));

	    PhysGetSelfAddr(l, buf, sizeof(buf));
	    Printf("\tSelf addr (name): %s", buf);
	    PhysGetSelfName(l, buf, sizeof(buf));
	    Printf(" (%s)\r\n", buf);
	    PhysGetPeerAddr(l, buf, sizeof(buf));
	    Printf("\tPeer addr (name): %s", buf);
	    PhysGetPeerName(l, buf, sizeof(buf));
	    Printf(" (%s)\r\n", buf);
	    PhysGetPeerMacAddr(l, buf, sizeof(buf));
	    Printf("\tPeer MAC address: %s\r\n", buf);
	    PhysGetPeerIface(l, buf, sizeof(buf));
	    Printf("\tPeer iface      : %s\r\n", buf);
	    PhysGetCallingNum(l, buf, sizeof(buf));
	    Printf("\tCalling         : %s\r\n", buf);
	    PhysGetCalledNum(l, buf, sizeof(buf));
	    Printf("\tCalled          : %s\r\n", buf);

	    if (l->bund) {
		LinkUpdateStats(l);
		Printf("\tTraffic stats:\r\n");
		Printf("\t\tInput octets   : %llu\r\n", (unsigned long long)l->stats.recvOctets);
		Printf("\t\tInput frames   : %llu\r\n", (unsigned long long)l->stats.recvFrames);
		Printf("\t\tOutput octets  : %llu\r\n", (unsigned long long)l->stats.xmitOctets);
	        Printf("\t\tOutput frames  : %llu\r\n", (unsigned long long)l->stats.xmitFrames);
	        Printf("\t\tBad protocols  : %llu\r\n", (unsigned long long)l->stats.badProtos);
	        Printf("\t\tRunts          : %llu\r\n", (unsigned long long)l->stats.runts);
	    }
	}
    }

    return(0);
}

/*
 * AdmitBund()
 */

int
AdmitBund(Context ctx, CmdTab cmd)
{
    (void)cmd;
    if (!ctx->bund)
	return(FALSE);
    return(TRUE);
}

/*
 * AdmitLink()
 */

int
AdmitLink(Context ctx, CmdTab cmd)
{
    (void)cmd;
    if (!ctx->lnk)
	return(FALSE);
    return(TRUE);
}

/*
 * AdmitRep()
 */

int
AdmitRep(Context ctx, CmdTab cmd)
{
    (void)cmd;
    if (!ctx->rep)
	return(FALSE);
    return(TRUE);
}

/*
 * AdmitDev()
 */

int
AdmitDev(Context ctx, CmdTab cmd)
{
    if (!cmd)
	return(FALSE);
    if (!ctx->lnk)
	return(FALSE);
    if (strncmp(cmd->name, ctx->lnk->type->name, strlen(ctx->lnk->type->name)))
	return(FALSE);
    return(TRUE);
}


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