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


/*
 * udp.c
 *
 * Written by  Alexander Motin <mav@FreeBSD.org>
 */

#include "ppp.h"
#include "phys.h"
#include "mbuf.h"
#include "udp.h"
#include "ngfunc.h"
#include "util.h"
#include "log.h"

#include <netgraph/ng_message.h>
#include <netgraph/ng_socket.h>
#include <netgraph/ng_ksocket.h>
#include <netgraph.h>

/*
 * XXX this device type not completely correct, 
 * as it can deliver out-of-order frames. This can make problems 
 * for different compression and encryption protocols.
 */

/*
 * DEFINITIONS
 */

  #define UDP_MTU		2048
  #define UDP_MRU		2048

#ifndef SMALL_SYSTEM
#define UDP_MAXPARENTIFS	256
#else
#define UDP_MAXPARENTIFS	64
#endif

  struct udpinfo {
    struct {
	struct optinfo	options;
	struct u_addr	self_addr;	/* Configured local IP address */
	struct u_range	peer_addr;	/* Configured peer IP address */
	in_port_t	self_port;	/* Configured local port */
	in_port_t	peer_port;	/* Configured peer port */
	char		*fqdn_peer_addr; /* FQDN Peer address */
    } conf;

    /* State */
    u_char		incoming;		/* incoming vs. outgoing */
    struct UdpIf 	*If;
    struct u_addr	peer_addr;
    in_port_t		peer_port;
    ng_ID_t		node_id;
  };
  typedef struct udpinfo	*UdpInfo;

/* Set menu options */

  enum {
    SET_PEERADDR,
    SET_SELFADDR,
    SET_ENABLE,
    SET_DISABLE
  };

  /* Binary options */
  enum {
    UDP_CONF_RESOLVE_ONCE	/* Only once resolve peer_addr */
  };

/*
 * INTERNAL FUNCTIONS
 */

  static int	UdpInit(Link l);
  static int	UdpInst(Link l, Link lt);
  static void	UdpOpen(Link l);
  static void	UdpClose(Link l);
  static void	UdpStat(Context ctx);
  static int	UdpOrigination(Link l);
  static int	UdpIsSync(Link l);
  static int	UdpSelfAddr(Link l, void *buf, size_t buf_len);
  static int	UdpPeerAddr(Link l, void *buf, size_t buf_len);
  static int	UdpPeerPort(Link l, void *buf, size_t buf_len);
  static int	UdpCallingNum(Link l, void *buf, size_t buf_len);
  static int	UdpCalledNum(Link l, void *buf, size_t buf_len);

  static void	UdpDoClose(Link l);
  static void	UdpShutdown(Link l);
  static int	UdpSetCommand(Context ctx, int ac, const char *const av[], const void *arg);
  static void	UdpNodeUpdate(Link l);
  static int	UdpListen(Link l);
  static int	UdpUnListen(Link l);

/*
 * GLOBAL VARIABLES
 */

  const struct phystype gUdpPhysType = {
    .name		= "udp",
    .descr		= "PPP over UDP",
    .mtu		= UDP_MTU,
    .mru		= UDP_MRU,
    .tmpl		= 1,
    .init		= UdpInit,
    .inst		= UdpInst,
    .open		= UdpOpen,
    .close		= UdpClose,
    .update		= UdpNodeUpdate,
    .shutdown		= UdpShutdown,
    .showstat		= UdpStat,
    .originate		= UdpOrigination,
    .issync		= UdpIsSync,
    .selfaddr		= UdpSelfAddr,
    .peeraddr		= UdpPeerAddr,
    .peerport		= UdpPeerPort,
    .callingnum		= UdpCallingNum,
    .callednum		= UdpCalledNum,
  };

  const struct cmdtab UdpSetCmds[] = {
    { "self {ip} [{port}]",		"Set local IP address",
	UdpSetCommand, NULL, 2, (void *) SET_SELFADDR },
    { "peer {ip} [{port}]",		"Set remote IP address",
	UdpSetCommand, NULL, 2, (void *) SET_PEERADDR },
    { "enable [opt ...]",		"Enable option",
	UdpSetCommand, NULL, 2, (void *) SET_ENABLE },
    { "disable [opt ...]",		"Disable option",
	UdpSetCommand, NULL, 2, (void *) SET_DISABLE },
    { NULL, NULL, NULL, NULL, 0, NULL },
  };

struct UdpIf {
    struct u_addr	self_addr;
    in_port_t	self_port;
    int		refs;
    int		csock;                  /* netgraph Control socket */
    EventRef	ctrlEvent;		/* listen for ctrl messages */
};
static struct UdpIf UdpIfs[UDP_MAXPARENTIFS];

 /*
 * INTERNAL VARIABLES
 */

  static const struct confinfo	gConfList[] = {
    { 0,	UDP_CONF_RESOLVE_ONCE,	"resolve-once"	},
    { 0,	0,			NULL		},
  };


/*
 * UdpInit()
 */

static int
UdpInit(Link l)
{
    UdpInfo	pi;

    pi = (UdpInfo) (l->info = Malloc(MB_PHYS, sizeof(*pi)));

    u_addrclear(&pi->conf.self_addr);
    u_rangeclear(&pi->conf.peer_addr);
    pi->conf.self_port=0;
    pi->conf.peer_port=0;

    pi->incoming = 0;
    pi->If = NULL;

    u_addrclear(&pi->peer_addr);
    pi->peer_port=0;
    pi->conf.fqdn_peer_addr = NULL;
    Enable(&pi->conf.options, UDP_CONF_RESOLVE_ONCE);

    return(0);
}

/*
 * UdpInst()
 */

static int
UdpInst(Link l, Link lt)
{
    UdpInfo	pi;
    UdpInfo const pit = (UdpInfo) lt->info;

    /* Initialize this link */
    pi = (UdpInfo) (l->info = Mdup(MB_PHYS, lt->info, sizeof(*pi)));
    if (pit->conf.fqdn_peer_addr != NULL)
        pi->conf.fqdn_peer_addr =
            Mstrdup(MB_PHYS, pit->conf.fqdn_peer_addr);
    
    if (pi->If)
	pi->If->refs++;

    return(0);
}

/*
 * UdpOpen()
 */

static void
UdpOpen(Link l)
{
	UdpInfo			const pi = (UdpInfo) l->info;
	char        		path[NG_PATHSIZ];
	char        		hook[NG_HOOKSIZ];
	struct ngm_mkpeer	mkp;
	struct ngm_name         nm;
	struct sockaddr_storage	addr;
        union {
            u_char buf[sizeof(struct ng_ksocket_sockopt) + sizeof(int)];
            struct ng_ksocket_sockopt ksso;
        } u;
        struct ng_ksocket_sockopt *const ksso = &u.ksso;
	int			csock;

	/* Create a new netgraph node to control TCP ksocket node. */
	if (NgMkSockNode(NULL, &csock, NULL) < 0) {
		Perror("[%s] TCP can't create control socket", l->name);
		goto fail;
	}
	(void)fcntl(csock, F_SETFD, 1);

        if (!PhysGetUpperHook(l, path, hook)) {
		Log(LG_PHYS, ("[%s] UDP: can't get upper hook", l->name));
    		goto fail;
        }

	/* Attach ksocket node to PPP node */
	memset(&mkp, 0, sizeof(mkp));
	strcpy(mkp.type, NG_KSOCKET_NODE_TYPE);
	strlcpy(mkp.ourhook, hook, sizeof(mkp.ourhook));
	if ((pi->conf.self_addr.family==AF_INET6) || 
	    (pi->conf.self_addr.family==AF_UNSPEC && pi->conf.peer_addr.addr.family==AF_INET6)) {
	        snprintf(mkp.peerhook, sizeof(mkp.peerhook), "%d/%d/%d", PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
	} else {
	    snprintf(mkp.peerhook, sizeof(mkp.peerhook), "inet/dgram/udp");
	}
	if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
	    NGM_MKPEER, &mkp, sizeof(mkp)) < 0) {
	        Perror("[%s] can't attach %s node",
	    	    l->name, NG_KSOCKET_NODE_TYPE);
		goto fail;
	}

	strlcat(path, ".", sizeof(path));
	strlcat(path, hook, sizeof(path));

	/* Give it a name */
	memset(&nm, 0, sizeof(nm));
	snprintf(nm.name, sizeof(nm.name), "mpd%d-%s-kso", gPid, l->name);
	if (NgSendMsg(csock, path,
	    NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
		Perror("[%s] can't name %s node", l->name, NG_KSOCKET_NODE_TYPE);
	}

	if ((pi->node_id = NgGetNodeID(csock, path)) == 0) {
	    Perror("[%s] Cannot get %s node id", l->name, NG_KSOCKET_NODE_TYPE);
	    goto fail;
	};

    if ((pi->incoming) || (pi->conf.self_port != 0)) {
	/* Setsockopt socket. */
	ksso->level=SOL_SOCKET;
	ksso->name=SO_REUSEPORT;
	((int *)(void *)(ksso->value))[0]=1;
	if (NgSendMsg(csock, path, NGM_KSOCKET_COOKIE,
    	    NGM_KSOCKET_SETOPT, &u, sizeof(u)) < 0) {
    	    Perror("[%s] can't setsockopt() %s node",
    		l->name, NG_KSOCKET_NODE_TYPE);
	    goto fail;
	}

	if ((!Enabled(&pi->conf.options, UDP_CONF_RESOLVE_ONCE)) &&
	    (pi->conf.fqdn_peer_addr != NULL)) {
	    struct u_range	rng;
	    if (ParseRange(pi->conf.fqdn_peer_addr, &rng, ALLOW_IPV4|ALLOW_IPV6))
		pi->conf.peer_addr = rng;
	}

	/* Bind socket */
	u_addrtosockaddr(&pi->conf.self_addr, pi->conf.self_port, &addr);
	if (NgSendMsg(csock, path, NGM_KSOCKET_COOKIE,
	  NGM_KSOCKET_BIND, &addr, addr.ss_len) < 0) {
	    Perror("[%s] can't bind() %s node", l->name, NG_KSOCKET_NODE_TYPE);
	    goto fail;
	}
    }

    if (!pi->incoming) {
	if ((!u_rangeempty(&pi->conf.peer_addr)) && (pi->conf.peer_port != 0)) {
	    u_addrcopy(&pi->conf.peer_addr.addr,&pi->peer_addr);
	    pi->peer_port = pi->conf.peer_port;
	} else {
	    Log(LG_ERR, ("[%s] Can't connect without peer specified", l->name));
	    goto fail;
	}
    }
    u_addrtosockaddr(&pi->peer_addr, pi->peer_port, &addr);

    /* Connect socket if peer address and port is specified */
    if (NgSendMsg(csock, path, NGM_KSOCKET_COOKIE,
      NGM_KSOCKET_CONNECT, &addr, addr.ss_len) < 0) {
	Perror("[%s] can't connect() %s node", l->name, NG_KSOCKET_NODE_TYPE);
	goto fail;
    }
  
    close(csock);

    /* OK */
    l->state = PHYS_STATE_UP;
    PhysUp(l);
    return;

fail:
    UdpDoClose(l);
    pi->incoming=0;
    l->state = PHYS_STATE_DOWN;
    u_addrclear(&pi->peer_addr);
    pi->peer_port=0;
    if (csock>0)
	close(csock);

    PhysDown(l, STR_ERROR, NULL);
}

/*
 * UdpClose()
 */

static void
UdpClose(Link l)
{
    UdpInfo const pi = (UdpInfo) l->info;
    if (l->state != PHYS_STATE_DOWN) {
	UdpDoClose(l);
	pi->incoming=0;
	l->state = PHYS_STATE_DOWN;
	u_addrclear(&pi->peer_addr);
	pi->peer_port=0;
	PhysDown(l, STR_MANUALLY, NULL);
    }
}

/*
 * UdpShutdown()
 */

static void
UdpShutdown(Link l)
{
	UdpInfo const pi = (UdpInfo) l->info;

	if (pi->conf.fqdn_peer_addr)
		Freee(pi->conf.fqdn_peer_addr);

	UdpDoClose(l);
	UdpUnListen(l);
	Freee(l->info);
}

/*
 * UdpDoClose()
 */

static void
UdpDoClose(Link l)
{
	UdpInfo	const pi = (UdpInfo) l->info;
	char	path[NG_PATHSIZ];
	int	csock;

	if (pi->node_id == 0)
		return;

	/* Get a temporary netgraph socket node */
	if (NgMkSockNode(NULL, &csock, NULL) == -1) {
		Perror("UDP: NgMkSockNode");
		return;
	}
	
	/* Disconnect session hook. */
	snprintf(path, sizeof(path), "[%lx]:", (u_long)pi->node_id);
	NgFuncShutdownNode(csock, l->name, path);
	
	close(csock);
	
	pi->node_id = 0;
}

/*
 * UdpOrigination()
 */

static int
UdpOrigination(Link l)
{
    UdpInfo	const pi = (UdpInfo) l->info;

    return (pi->incoming ? LINK_ORIGINATE_REMOTE : LINK_ORIGINATE_LOCAL);
}

/*
 * UdpIsSync()
 */

static int
UdpIsSync(Link l)
{
    (void)l;
    return (1);
}

static int
UdpSelfAddr(Link l, void *buf, size_t buf_len)
{
    UdpInfo	const pi = (UdpInfo) l->info;

    if (!u_addrempty(&pi->conf.self_addr)) {
	if (u_addrtoa(&pi->conf.self_addr, buf, buf_len))
	    return (0);
  	else {
	    ((char*)buf)[0]=0;
	    return (-1);
	}
    }
    ((char*)buf)[0]=0;
    return (0);
}

static int
UdpPeerAddr(Link l, void *buf, size_t buf_len)
{
    UdpInfo	const pi = (UdpInfo) l->info;

    if (u_addrtoa(&pi->peer_addr, buf, buf_len))
	return(0);
    else
	return(-1);
}

static int
UdpPeerPort(Link l, void *buf, size_t buf_len)
{
    UdpInfo	const pi = (UdpInfo) l->info;

    if (snprintf(buf, buf_len, "%d", pi->peer_port))
	return(0);
    else
	return(-1);
}

static int
UdpCallingNum(Link l, void *buf, size_t buf_len)
{
	UdpInfo const pi = (UdpInfo) l->info;

	if (pi->incoming) {
	    if (u_addrtoa(&pi->peer_addr, buf, buf_len))
	    	return (0);
  	    else
		return (-1);
	} else {
	    if (u_addrtoa(&pi->conf.self_addr, buf, buf_len))
	    	return (0);
  	    else
		return (-1);
	}
}

static int
UdpCalledNum(Link l, void *buf, size_t buf_len)
{
	UdpInfo const pi = (UdpInfo) l->info;

	if (!pi->incoming) {
	    if (u_addrtoa(&pi->peer_addr, buf, buf_len))
	    	return (0);
  	    else
		return (-1);
	} else {
	    if (u_addrtoa(&pi->conf.self_addr, buf, buf_len))
	    	return (0);
  	    else
		return (-1);
	}
}

/*
 * UdpStat()
 */

void
UdpStat(Context ctx)
{
	UdpInfo const pi = (UdpInfo) ctx->lnk->info;
	char	buf[48];

	Printf("UDP configuration:\r\n");
	Printf("\tPeer FQDN    : %s\r\n", pi->conf.fqdn_peer_addr);
	Printf("\tSelf address : %s, port %u\r\n",
	    u_addrtoa(&pi->conf.self_addr, buf, sizeof(buf)), pi->conf.self_port);
	Printf("\tPeer address : %s, port %u\r\n",
	    u_rangetoa(&pi->conf.peer_addr, buf, sizeof(buf)), pi->conf.peer_port);
	Printf("UDP state:\r\n");
	if (ctx->lnk->state != PHYS_STATE_DOWN) {
	    Printf("\tIncoming     : %s\r\n", (pi->incoming?"YES":"NO"));
	    Printf("\tCurrent peer : %s, port %u\r\n",
		u_addrtoa(&pi->peer_addr, buf, sizeof(buf)), pi->peer_port);
	}
}

/*
 * UdpAcceptEvent() triggers when we accept incoming connection.
 */

static void
UdpAcceptEvent(int type, void *cookie)
{
	struct sockaddr_storage saddr;
	socklen_t	saddrlen;
	struct u_addr	addr;
	in_port_t	port;
	char		buf[48];
	char		buf1[48];
	int 		k;
	struct UdpIf 	*If=(struct UdpIf *)(cookie);
	Link		l = NULL;
	UdpInfo		pi = NULL;

	char		pktbuf[UDP_MRU+100];
	ssize_t		pktlen;

	assert(type == EVENT_READ);

	saddrlen = sizeof(saddr);
	if ((pktlen = recvfrom(If->csock, pktbuf, sizeof(pktbuf), MSG_DONTWAIT, (struct sockaddr *)(&saddr), &saddrlen)) < 0) {
	    Log(LG_PHYS, ("recvfrom() error: %s", strerror(errno)));
	}

	sockaddrtou_addr(&saddr, &addr, &port);

	Log(LG_PHYS, ("Incoming UDP connection from %s %u to %s %u",
	    u_addrtoa(&addr, buf, sizeof(buf)), port,
	    u_addrtoa(&If->self_addr, buf1, sizeof(buf1)), If->self_port));

	if (gShutdownInProgress) {
		Log(LG_PHYS, ("Shutdown sequence in progress, ignoring request."));
		goto failed;
	}

	if (OVERLOAD()) {
		Log(LG_PHYS, ("Daemon overloaded, ignoring request."));
		goto failed;
	}

	/* Examine all UDP links. */
	for (k = 0; k < gNumLinks; k++) {
		Link l2;
	        UdpInfo pi2;

		if (!gLinks[k] || gLinks[k]->type != &gUdpPhysType)
			continue;

		l2 = gLinks[k];
		pi2 = (UdpInfo)l2->info;

		if ((!PhysIsBusy(l2)) &&
		    Enabled(&l2->conf.options, LINK_CONF_INCOMING) &&
		    (pi2->If == If) &&
		    IpAddrInRange(&pi2->conf.peer_addr, &addr) &&
		    (pi2->conf.peer_port == 0 || pi2->conf.peer_port == port)) {

			if (pi == NULL || pi2->conf.peer_addr.width > pi->conf.peer_addr.width) {
				l = l2;
				pi = pi2;
				if (u_rangehost(&pi->conf.peer_addr)) {
					break;	/* Nothing could be better */
				}
			}
		}
	}
	if (l != NULL && l->tmpl)
    		l = LinkInst(l, NULL, 0, 0);

	if (l != NULL) {
    		pi = (UdpInfo)l->info;
		Log(LG_PHYS, ("[%s] Accepting UDP connection from %s %u to %s %u",
		    l->name, u_addrtoa(&addr, buf, sizeof(buf)), port,
		    u_addrtoa(&If->self_addr, buf1, sizeof(buf1)), If->self_port));

		sockaddrtou_addr(&saddr, &pi->peer_addr, &pi->peer_port);

		pi->incoming=1;
		l->state = PHYS_STATE_READY;

		PhysIncoming(l);
	} else {
		Log(LG_PHYS, ("No free UDP link with requested parameters "
	    	    "was found"));
	}

failed:
	EventRegister(&If->ctrlEvent, EVENT_READ, If->csock,
	    0, UdpAcceptEvent, If);
}

static int 
UdpListen(Link l)
{
	UdpInfo const pi = (UdpInfo) l->info;
	struct sockaddr_storage addr;
	char buf[48];
	int opt, i, j = -1, free = -1;
	
	if (pi->If)
	    return(1);

	for (i = 0; i < UDP_MAXPARENTIFS; i++) {
	    if (UdpIfs[i].self_port == 0)
	        free = i;
	    else if ((u_addrcompare(&UdpIfs[i].self_addr, &pi->conf.self_addr) == 0) &&
	        (UdpIfs[i].self_port == pi->conf.self_port)) {
	            j = i;
	    	    break;
	    }
	}

	if (j >= 0) {
	    UdpIfs[j].refs++;
	    pi->If=&UdpIfs[j];
	    return(1);
	}

	if (free < 0) {
	    Log(LG_ERR, ("[%s] UDP: Too many different listening ports! ", 
		l->name));
	    return (0);
	}

	UdpIfs[free].refs = 1;
	pi->If=&UdpIfs[free];
	
	u_addrcopy(&pi->conf.self_addr,&pi->If->self_addr);
	pi->If->self_port=pi->conf.self_port;
	
	/* Make listening UDP socket. */
	if (pi->If->self_addr.family==AF_INET6) {
	    pi->If->csock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
	} else {
	    pi->If->csock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	}
	(void)fcntl(pi->If->csock, F_SETFD, 1);

	/* Setsockopt socket. */
	opt = 1;
	if (setsockopt(pi->If->csock, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt))) {
		Perror("UDP: can't setsockopt socket");
		goto fail2;
	};

	/* Bind socket. */
	u_addrtosockaddr(&pi->If->self_addr, pi->If->self_port, &addr);
	if (bind(pi->If->csock, (struct sockaddr *)(&addr), addr.ss_len)) {
		Perror("UDP: can't bind socket");
		goto fail2;
	}

	Log(LG_PHYS, ("UDP: waiting for connection on %s %u",
	    u_addrtoa(&pi->If->self_addr, buf, sizeof(buf)), pi->If->self_port));
	EventRegister(&pi->If->ctrlEvent, EVENT_READ, pi->If->csock,
	    0, UdpAcceptEvent, pi->If);

	return (1);
fail2:
	close(pi->If->csock);
	pi->If->csock = -1;
	pi->If->self_port = 0;
	pi->If = NULL;
	return (0);
}


static int 
UdpUnListen(Link l)
{
	UdpInfo const pi = (UdpInfo) l->info;
	char buf[48];
	
	if (!pi->If)
	    return(1);

	pi->If->refs--;
	if (pi->If->refs == 0) {
	    Log(LG_PHYS, ("UDP: stop waiting for connection on %s %u",
		u_addrtoa(&pi->If->self_addr, buf, sizeof(buf)), pi->If->self_port));
	    EventUnRegister(&pi->If->ctrlEvent);
	    close(pi->If->csock);
	    pi->If->csock = -1;
	    pi->If->self_port = 0;
	    pi->If = NULL;
	}

	return (1);
}

/*
 * UdpNodeUpdate()
 */

static void
UdpNodeUpdate(Link l)
{
    UdpInfo const pi = (UdpInfo) l->info;
    if (!pi->If) {
	if (Enabled(&l->conf.options, LINK_CONF_INCOMING))
	    UdpListen(l);
    } else {
	if (!Enabled(&l->conf.options, LINK_CONF_INCOMING))
	    UdpUnListen(l);
    }
}

/*
 * UdpSetCommand()
 */

static int
UdpSetCommand(Context ctx, int ac, const char *const av[], const void *arg)
{
    UdpInfo		const pi = (UdpInfo) ctx->lnk->info;
    char		**fqdn_peer_addr = &pi->conf.fqdn_peer_addr;
    struct u_range	rng;
    int			port;
	
    switch ((intptr_t)arg) {
	case SET_PEERADDR:
	case SET_SELFADDR:
	    if ((ac == 1 || ac == 2) && (intptr_t)arg == SET_PEERADDR) {
		if (*fqdn_peer_addr)
		    Freee(*fqdn_peer_addr);
		*fqdn_peer_addr = Mstrdup(MB_PHYS, av[0]);
	    }
    	    if (ac < 1 || ac > 2 || !ParseRange(av[0], &rng, ALLOW_IPV4|ALLOW_IPV6))
		return(-1);
    	    if (ac > 1) {
		if ((port = atoi(av[1])) < 0 || port > 0xffff)
		    return(-1);
    	    } else {
		port = 0;
    	    }
    	    if ((intptr_t)arg == SET_SELFADDR) {
		pi->conf.self_addr = rng.addr;
		pi->conf.self_port = port;
    	    } else {
		pi->conf.peer_addr = rng;
		pi->conf.peer_port = port;
    	    }
	    if (pi->If) {
		UdpUnListen(ctx->lnk);
		UdpListen(ctx->lnk);
	    }
    	    break;
	case SET_ENABLE:
	    EnableCommand(ac, av, &pi->conf.options, gConfList);
	    UdpNodeUpdate(ctx->lnk);
	    break;
	case SET_DISABLE:
	    DisableCommand(ac, av, &pi->conf.options, gConfList);
	    UdpNodeUpdate(ctx->lnk);
	    break;
	default:
    	    assert(0);
    }
    return(0);
}


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