/*
 * iface.c
 *
 * Written by Archie Cobbs <archie@freebsd.org>
 * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
 * See ``COPYRIGHT.whistle''
 *
 * TCP MSSFIX code copyright (c) 2000 Ruslan Ermilov
 * TCP MSSFIX contributed by Sergey Korolew <dsATbittu.org.ru>
 *
 */

#include "ppp.h"
#include "iface.h"
#include "ipcp.h"
#include "auth.h"
#include "custom.h"
#include "ngfunc.h"
#include "netgraph.h"
#include "util.h"

#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#include <netinet6/nd6.h>
#include <netgraph/ng_message.h>
#ifdef __DragonFly__
#include <netgraph/iface/ng_iface.h>
#include <netgraph/bpf/ng_bpf.h>
#include <netgraph/tee/ng_tee.h>
#include <netgraph/ksocket/ng_ksocket.h>
#else
#include <netgraph/ng_iface.h>
#include <netgraph/ng_bpf.h>
#include <netgraph/ng_tee.h>
#include <netgraph/ng_ksocket.h>
#endif
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef USE_NG_NAT
#include <netgraph/ng_nat.h>
#endif
#ifdef USE_NG_TCPMSS
#include <netgraph/ng_tcpmss.h>
#endif
#ifdef USE_NG_IPACCT
#include <netgraph/ng_ipacct.h>
#undef r_ip_p	/* XXX:DIRTY CONFLICT! */
#endif
#ifdef USE_NG_NETFLOW
#include <netgraph/netflow/ng_netflow.h>
#endif
#ifdef USE_NG_CAR
#include <netgraph/ng_car.h>
#endif

#include <pcap.h>

/*
 * DEFINITIONS
 */

  #define TEMPHOOK		"temphook"

/* Set menu options */

  enum {
    SET_IDLE,
    SET_SESSION,
    SET_ADDRS,
    SET_ROUTE,
    SET_MTU,
    SET_UP_SCRIPT,
    SET_DOWN_SCRIPT,
    SET_ENABLE,
    SET_DISABLE,
  };

/*
 * INTERNAL FUNCTIONS
 */

  static int	IfaceNgIpInit(Bund b, int ready);
  static void	IfaceNgIpShutdown(Bund b);
  static int	IfaceNgIpv6Init(Bund b, int ready);
  static void	IfaceNgIpv6Shutdown(Bund b);

#ifdef USE_NG_NETFLOW
  static int	IfaceInitNetflow(Bund b, char *path, char *hook, char out);
  static int	IfaceSetupNetflow(Bund b, char out);
  static void	IfaceShutdownNetflow(Bund b, char out);
#endif

#ifdef USE_NG_IPACCT
  static int	IfaceInitIpacct(Bund b, char *path, char *hook);
  static void	IfaceShutdownIpacct(Bund b);
#endif

#ifdef USE_NG_NAT
  static int	IfaceInitNAT(Bund b, char *path, char *hook);
  static int	IfaceSetupNAT(Bund b);
  static void	IfaceShutdownNAT(Bund b);
#endif

  static int	IfaceInitTee(Bund b, char *path, char *hook);
  static void	IfaceShutdownTee(Bund b);

  static int    IfaceInitMSS(Bund b, char *path, char *hook);
  static void	IfaceSetupMSS(Bund b, uint16_t maxMSS);
  static void	IfaceShutdownMSS(Bund b);

  static int    IfaceInitLimits(Bund b, char *path, char *hook);
  static void	IfaceSetupLimits(Bund b);
  static void	IfaceShutdownLimits(Bund b);

  static int	IfaceSetCommand(Context ctx, int ac, char *av[], void *arg);
  static void	IfaceSessionTimeout(void *arg);
  static void	IfaceIdleTimeout(void *arg);

  static void	IfaceCacheSend(Bund b);
  static void	IfaceCachePkt(Bund b, int proto, Mbuf pkt);
  static int	IfaceIsDemand(int proto, Mbuf pkt);

  static int	IfaceAllocACL (struct acl_pool ***ap, int start, char * ifname, int number);
  static int	IfaceFindACL (struct acl_pool *ap, char * ifname, int number);
  static char *	IFaceParseACL (char * src, char * ifname);
  
/*
 * GLOBAL VARIABLES
 */

  const struct cmdtab IfaceSetCmds[] = {
    { "addrs self peer",		"Set interface addresses",
	IfaceSetCommand, NULL, (void *) SET_ADDRS },
    { "route dest[/width]",		"Add IP route",
	IfaceSetCommand, NULL, (void *) SET_ROUTE },
    { "mtu size",			"Set max allowed interface MTU",
	IfaceSetCommand, NULL, (void *) SET_MTU },
    { "up-script [progname]",		"Interface up script",
	IfaceSetCommand, NULL, (void *) SET_UP_SCRIPT },
    { "down-script [progname]",		"Interface down script",
	IfaceSetCommand, NULL, (void *) SET_DOWN_SCRIPT },
    { "idle seconds",			"Idle timeout",
	IfaceSetCommand, NULL, (void *) SET_IDLE },
    { "session seconds",		"Session timeout",
	IfaceSetCommand, NULL, (void *) SET_SESSION },
    { "enable [opt ...]",		"Enable option",
	IfaceSetCommand, NULL, (void *) SET_ENABLE },
    { "disable [opt ...]",		"Disable option",
	IfaceSetCommand, NULL, (void *) SET_DISABLE },
    { NULL },
  };

/*
 * INTERNAL VARIABLES
 */

  static const struct confinfo	gConfList[] = {
    { 0,	IFACE_CONF_ONDEMAND,		"on-demand"	},
    { 0,	IFACE_CONF_PROXY,		"proxy-arp"	},
    { 0,	IFACE_CONF_TCPMSSFIX,           "tcpmssfix"	},
    { 0,	IFACE_CONF_TEE,			"tee"		},
    { 0,	IFACE_CONF_NAT,			"nat"		},
    { 0,	IFACE_CONF_NETFLOW_IN,		"netflow-in"	},
    { 0,	IFACE_CONF_NETFLOW_OUT,		"netflow-out"	},
    { 0,	IFACE_CONF_IPACCT,		"ipacct"	},
    { 0,	0,				NULL		},
  };

  struct acl_pool * rule_pool = NULL; /* Pointer to the first element in the list of rules */
  struct acl_pool * pipe_pool = NULL; /* Pointer to the first element in the list of pipes */
  struct acl_pool * queue_pool = NULL; /* Pointer to the first element in the list of queues */
  struct acl_pool * table_pool = NULL; /* Pointer to the first element in the list of tables */
  int rule_pool_start = 10000; /* Initial number of ipfw rules pool */
  int pipe_pool_start = 10000; /* Initial number of ipfw dummynet pipe pool */
  int queue_pool_start = 10000; /* Initial number of ipfw dummynet queue pool */
  int table_pool_start = 32; /* Initial number of ipfw tables pool */

  /* A BPF filter that matches TCP SYN packets */
  static const struct bpf_insn gTCPSYNProg[] = {
/*00*/	BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 9),		/* A <- IP protocol */
/*01*/	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_TCP, 0, 6), /* !TCP => 8 */
/*02*/	BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 6),	/* A <- fragmentation offset */
/*03*/	BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x1fff, 4, 0),	/* fragment => 8 */
/*04*/	BPF_STMT(BPF_LDX+BPF_B+BPF_MSH, 0),		/* X <- header len */
/*05*/	BPF_STMT(BPF_LD+BPF_B+BPF_IND, 13),		/* A <- TCP flags */
/*06*/	BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, TH_SYN, 0, 1),	/* !TH_SYN => 8 */
/*07*/	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),		/* accept packet */
/*08*/	BPF_STMT(BPF_RET+BPF_K, 0),			/* reject packet */
  };

  #define TCPSYN_PROG_LEN	(sizeof(gTCPSYNProg) / sizeof(*gTCPSYNProg))

  /* A BPF filter that matches nothing */
  static const struct bpf_insn gNoMatchProg[] = {
	BPF_STMT(BPF_RET+BPF_K, 0)
  };

  #define NOMATCH_PROG_LEN	(sizeof(gNoMatchProg) / sizeof(*gNoMatchProg))

  /* A BPF filter that matches everything */
  static const struct bpf_insn gMatchProg[] = {
	BPF_STMT(BPF_RET+BPF_K, (u_int)-1)
  };

  #define MATCH_PROG_LEN	(sizeof(gMatchProg) / sizeof(*gMatchProg))

#define IN6MASK128	{{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
			    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
static const struct in6_addr in6mask128 = IN6MASK128;


/*
 * IfaceInit()
 */

void
IfaceInit(Bund b)
{
  IfaceState	const iface = &b->iface;

  /* Default configuration */
  iface->mtu = NG_IFACE_MTU_DEFAULT;
  iface->max_mtu = NG_IFACE_MTU_DEFAULT;
  Disable(&iface->options, IFACE_CONF_ONDEMAND);
  Disable(&iface->options, IFACE_CONF_PROXY);
  Disable(&iface->options, IFACE_CONF_TCPMSSFIX);
  NatInit(b);
  Log(LG_BUND|LG_IFACE, ("[%s] using interface %s",
    b->name, b->iface.ifname));
}

/*
 * IfaceOpen()
 *
 * Open the interface layer
 */

void
IfaceOpen(Bund b)
{
    IfaceState	const iface = &b->iface;

    Log(LG_IFACE, ("[%s] IFACE: Open event", b->name));

    /* Open is useless without on-demand. */
    if (!Enabled(&iface->options, IFACE_CONF_ONDEMAND)) {
	Log(LG_ERR, ("[%s] 'open iface' is useless without on-demand enabled", b->name));
	return;
    }

    /* If interface is already open do nothing */
    if (iface->open)
	return;
    iface->open = TRUE;

    /* If on-demand, bring up system interface immediately and start
     listening for outgoing packets. The next outgoing packet will
     cause us to open the lower layer(s) */
    BundNcpsJoin(b, NCP_NONE);
}

/*
 * IfaceClose()
 *
 * Close the interface layer
 */

void
IfaceClose(Bund b)
{
    IfaceState	const iface = &b->iface;

    Log(LG_IFACE, ("[%s] IFACE: Close event", b->name));

    /* If interface is already closed do nothing */
    if (!iface->open)
	return;
    iface->open = FALSE;

    /* If there was on-demand, tell that it is not needed anymore */
    BundNcpsLeave(b, NCP_NONE);
}

/*
 * IfaceOpenCmd()
 *
 * Open the interface layer
 */

void
IfaceOpenCmd(Context ctx)
{
    IfaceOpen(ctx->bund);
}

/*
 * IfaceCloseCmd()
 *
 * Close the interface layer
 */

void
IfaceCloseCmd(Context ctx)
{
    IfaceClose(ctx->bund);
}

/*
 * IfaceUp()
 *
 * Our underlying PPP bundle is ready for traffic.
 * Note, while this assumes we're talking about IP traffic
 * here, in general a parameter could specify which type
 * of traffic, IP vs. AppleTalk vs. whatever, along with
 * additional protocol specific information (in this case,
 * the IP addresses of each end of the point-to-point link).
 */

void
IfaceUp(Bund b, int ready)
{
  IfaceState	const iface = &b->iface;
  int		session_timeout = 0, idle_timeout = 0;
  struct acl	*acls, *acl;
  char			*buf;
  struct acl_pool 	**poollast;
  int 			poollaststart;
  int		prev_number;
  int		prev_real_number;

  Log(LG_IFACE, ("[%s] IFACE: Up event", b->name));

  if (ready) {

    /* Start Session timer */
    if (b->params.session_timeout > 0) {
	session_timeout = b->params.session_timeout;
    } else if (iface->session_timeout > 0) {
	session_timeout = iface->session_timeout;
    }

    if (session_timeout > 0) {
	Log(LG_IFACE2, ("[%s] IFACE: session-timeout: %d seconds", 
    	    b->name, session_timeout));
	TimerInit(&iface->sessionTimer, "IfaceSession",
    	    session_timeout * SECONDS, IfaceSessionTimeout, b);
	TimerStart(&iface->sessionTimer);
    }

    /* Start idle timer */
    if (b->params.idle_timeout > 0) {
	idle_timeout = b->params.idle_timeout;
    } else if (iface->idle_timeout > 0) {
	idle_timeout = iface->idle_timeout;
    }
    
    if (idle_timeout > 0) {
	Log(LG_IFACE2, ("[%s] IFACE: idle-timeout: %d seconds", 
    	    b->name, idle_timeout));
    
	TimerInit(&iface->idleTimer, "IfaceIdle",
    	    idle_timeout * SECONDS / IFACE_IDLE_SPLIT, IfaceIdleTimeout, b);
	TimerStart(&iface->idleTimer);
	iface->traffic[1] = TRUE;
	iface->traffic[0] = FALSE;

	/* Reset statistics */
	memset(&iface->idleStats, 0, sizeof(iface->idleStats));
    }

  /* Allocate ACLs */
  acls = b->params.acl_pipe;
  poollast = &pipe_pool;
  poollaststart = pipe_pool_start;
  while (acls != NULL) {
    acls->real_number = IfaceAllocACL(&poollast, poollaststart, iface->ifname, acls->number);
    poollaststart = acls->real_number;
    acls = acls->next;
  };
  acls = b->params.acl_queue;
  poollast = &queue_pool;
  poollaststart = queue_pool_start;
  while (acls != NULL) {
    acls->real_number = IfaceAllocACL(&poollast, poollaststart, iface->ifname, acls->number);
    poollaststart = acls->real_number;
    acls = acls->next;
  };
  prev_number = -1;
  prev_real_number = -1;
  acls = b->params.acl_table;
  poollast = &table_pool;
  poollaststart = table_pool_start;
  while (acls != NULL) {
    if (acls->real_number == 0) {
	if (acls->number == prev_number) { /* ACL list is presorted so we need not allocate if equal */
	    acls->real_number = prev_real_number;
	} else {
	    acls->real_number = IfaceAllocACL(&poollast, poollaststart, iface->ifname, acls->number);
	    poollaststart = acls->real_number;
	    prev_number = acls->number;
	    prev_real_number = acls->real_number;
	}
    }
    acls = acls->next;
  };
  acls = b->params.acl_rule;
  poollast = &rule_pool;
  poollaststart = rule_pool_start;
  while (acls != NULL) {
    acls->real_number = IfaceAllocACL(&poollast, poollaststart, iface->ifname, acls->number);
    poollaststart = acls->real_number;
    acls = acls->next;
  };

  /* Set ACLs */
  acls = b->params.acl_pipe;
  while (acls != NULL) {
    buf = IFaceParseACL(acls->rule, iface->ifname);
    ExecCmd(LG_IFACE2, b->name, "%s pipe %d config %s", PATH_IPFW, acls->real_number, acls->rule);
    Freee(MB_IFACE, buf);
    acls = acls->next;
  }
  acls = b->params.acl_queue;
  while (acls != NULL) {
    buf = IFaceParseACL(acls->rule,iface->ifname);
    ExecCmd(LG_IFACE2, b->name, "%s queue %d config %s", PATH_IPFW, acls->real_number, buf);
    Freee(MB_IFACE, buf);
    acls = acls->next;
  }
  acls = b->params.acl_table;
  while (acls != NULL) {
    acl = Malloc(MB_IFACE, sizeof(struct acl));
    memcpy(acl, acls, sizeof(struct acl));
    acl->next = iface->tables;
    iface->tables = acl;
    ExecCmd(LG_IFACE2, b->name, "%s table %d add %s", PATH_IPFW, acls->real_number, acls->rule);
    acls = acls->next;
  };
  acls = b->params.acl_rule;
  while (acls != NULL) {
    buf = IFaceParseACL(acls->rule, iface->ifname);
    ExecCmd(LG_IFACE2, b->name, "%s add %d %s via %s", PATH_IPFW, acls->real_number, buf, iface->ifname);
    Freee(MB_IFACE, buf);
    acls = acls->next;
  };

  };

  /* Bring up system interface */
  IfaceChangeFlags(b, (ready?IFF_LINK0:0), IFF_UP | (ready?0:IFF_LINK0));

  /* Send any cached packets */
  IfaceCacheSend(b);

}

/*
 * IfaceDown()
 *
 * Our packet transport mechanism is no longer ready for traffic.
 */

void
IfaceDown(Bund b)
{
  IfaceState	const iface = &b->iface;
  struct acl_pool	**rp, *rp1;
  char		cb[32768];
  struct acl    *acl, *aclnext;

  Log(LG_IFACE, ("[%s] IFACE: Down event", b->name));

  /* Bring down system interface */
  IfaceChangeFlags(b, IFF_UP | IFF_LINK0, 0);

  TimerStop(&iface->idleTimer);
  TimerStop(&iface->sessionTimer);

  /* Remove rule ACLs */
  rp = &rule_pool;
  cb[0]=0;
  while (*rp != NULL) {
    if (strncmp((*rp)->ifname, iface->ifname, IFNAMSIZ) == 0) {
      sprintf(cb+strlen(cb), " %d", (*rp)->real_number);
      rp1 = *rp;
      *rp = (*rp)->next;
      Freee(MB_IFACE, rp1);
    } else {
      rp = &((*rp)->next);
    };
  };
  if (cb[0]!=0)
    ExecCmdNosh(LG_IFACE2, b->name, "%s delete%s",
      PATH_IPFW, cb);

  /* Remove table ACLs */
  rp = &table_pool;
  while (*rp != NULL) {
    if (strncmp((*rp)->ifname, iface->ifname, IFNAMSIZ) == 0) {
      rp1 = *rp;
      *rp = (*rp)->next;
      Freee(MB_IFACE, rp1);
    } else {
      rp = &((*rp)->next);
    };
  };
  acl = iface->tables;
  while (acl != NULL) {
    ExecCmd(LG_IFACE2, b->name, "%s table %d delete %s",
	PATH_IPFW, acl->real_number, acl->rule);
    aclnext = acl->next;
    Freee(MB_IFACE, acl);
    acl = aclnext;
  };
  iface->tables = NULL;

  /* Remove queue ACLs */
  rp = &queue_pool;
  cb[0]=0;
  while (*rp != NULL) {
    if (strncmp((*rp)->ifname, iface->ifname, IFNAMSIZ) == 0) {
      sprintf(cb+strlen(cb), " %d", (*rp)->real_number);
      rp1 = *rp;
      *rp = (*rp)->next;
      Freee(MB_IFACE, rp1);
    } else {
      rp = &((*rp)->next);
    };
  };
  if (cb[0]!=0)
    ExecCmdNosh(LG_IFACE2, b->name, "%s queue delete%s",
      PATH_IPFW, cb);

  /* Remove pipe ACLs */
  rp = &pipe_pool;
  cb[0]=0;
  while (*rp != NULL) {
    if (strncmp((*rp)->ifname, iface->ifname, IFNAMSIZ) == 0) {
      sprintf(cb+strlen(cb), " %d", (*rp)->real_number);
      rp1 = *rp;
      *rp = (*rp)->next;
      Freee(MB_IFACE, rp1);
    } else {
      rp = &((*rp)->next);
    };
  };
  if (cb[0]!=0)
    ExecCmdNosh(LG_IFACE2, b->name, "%s pipe delete%s",
      PATH_IPFW, cb);

}

/*
 * IfaceListenInput()
 *
 * A packet was received on our demand snooping hook. Stimulate a connection.
 */

void
IfaceListenInput(Bund b, int proto, Mbuf pkt)
{
  IfaceState	const iface = &b->iface;
  int		const isDemand = IfaceIsDemand(proto, pkt);
  Fsm		fsm;

  /* Does this count as demand traffic? */
  if (isDemand)
    iface->traffic[0] = TRUE;

  /* Get FSM for protocol (for now, we know it's IP) */
  assert(proto == PROTO_IP);
  fsm = &b->ipcp.fsm;

  if (OPEN_STATE(fsm->state)) {
    if (b->bm.n_up > 0) {
#ifndef USE_NG_TCPMSS
      if (Enabled(&iface->options, IFACE_CONF_TCPMSSFIX)) {
	if (proto == PROTO_IP)
	  IfaceCorrectMSS(pkt, MAXMSS(iface->mtu));
      } else
	Log(LG_IFACE, ("[%s] unexpected outgoing packet, len=%d",
	  b->name, MBLEN(pkt)));
#endif
      NgFuncWriteFrame(b, MPD_HOOK_DEMAND_TAP, pkt);
    } else {
      IfaceCachePkt(b, proto, pkt);
    }
  /* Maybe do dial-on-demand here */
  } else if (iface->open && isDemand) {
    Log(LG_IFACE, ("[%s] outgoing packet is demand", b->name));
    RecordLinkUpDownReason(b, NULL, 1, STR_DEMAND, NULL);
    BundOpenLinks(b);
    IfaceCachePkt(b, proto, pkt);
  } else {
    PFREE(pkt);
  }
}

/*
 * IfaceAllocACL ()
 *
 * Allocates unique real number for new ACL and adds it to the list of used ones.
 */

static int
IfaceAllocACL(struct acl_pool ***ap, int start, char *ifname, int number)
{
    int	i;
    struct acl_pool **rp,*rp1;

    rp1 = Malloc(MB_IFACE, sizeof(struct acl_pool));
    strncpy(rp1->ifname, ifname, IFNAMSIZ);
    rp1->acl_number = number;

    rp = *ap;
    i = start;
    while (*rp != NULL && (*rp)->real_number <= i) {
        i = (*rp)->real_number+1;
        rp = &((*rp)->next);
    };
    if (*rp == NULL) {
        rp1->next = NULL;
    } else {
        rp1->next = *rp;
    };
    rp1->real_number = i;
    *rp = rp1;
    *ap = rp;
    return(i);
};

/*
 * IfaceFindACL ()
 *
 * Finds ACL in the list and gets its real number.
 */

static int
IfaceFindACL (struct acl_pool *ap, char * ifname, int number)
{
    int	i;
    struct acl_pool *rp;

    rp=ap;
    i=-1;
    while (rp != NULL) {
	if ((rp->acl_number == number) && (strncmp(rp->ifname,ifname,IFNAMSIZ) == 0)) {
    	    i = rp->real_number;
	    break;
	};
        rp = rp->next;
    };
    return(i);
};

/*
 * IFaceParseACL ()
 *
 * Parces ACL and replaces %r, %p and %q macroses 
 * by the real numbers of rules, queues and pipes.
 */

static char *
IFaceParseACL (char * src, char * ifname)
{
    char *buf,*buf1;
    char *begin,*param,*end;
    char t;
    int num,real_number;
    struct acl_pool *ap;
    
    buf = Malloc(MB_IFACE, ACL_LEN+1);
    buf1 = Malloc(MB_IFACE, ACL_LEN+1);

    strncpy(buf,src,ACL_LEN);
    do {
        end = buf;
	begin = strsep(&end, "%");
	param = strsep(&end, " ");
	if (param != NULL) {
	    if (sscanf(param,"%c%d", &t, &num) == 2) {
		switch (t) {
		    case 'r':
			ap = rule_pool;
			break;
		    case 'p':
			ap = pipe_pool;
			break;
		    case 'q':
			ap = queue_pool;
			break;
		    case 't':
			ap = table_pool;
			break;
		    default:
			ap = NULL;
		};
		real_number = IfaceFindACL(ap,ifname,num);
		if (end != NULL) {
		    snprintf(buf1, ACL_LEN, "%s%d %s", begin, real_number, end);
		} else {
		    snprintf(buf1, ACL_LEN, "%s%d", begin, real_number);
		};
		strncpy(buf, buf1, ACL_LEN);
	    };
	};
    } while (end != NULL);
    Freee(MB_IFACE, buf1);
    return(buf);
};

/*
 * IfaceIpIfaceUp()
 *
 * Bring up the IP interface. The "ready" flag means that
 * IPCP is also up and we can deliver packets immediately.
 */

void
IfaceIpIfaceUp(Bund b, int ready)
{
  IfaceState		const iface = &b->iface;
  struct sockaddr_dl	hwa;
  char			hisaddr[20];
  IfaceRoute		r;
  u_char		*ether;

  if (ready) {
    in_addrtou_range(&b->ipcp.want_addr, 32, &iface->self_addr);
    in_addrtou_addr(&b->ipcp.peer_addr, &iface->peer_addr);
  }

  if (IfaceNgIpInit(b, ready)) {
    Log(LG_ERR, ("[%s] IfaceNgIpInit() error, closing IPCP", b->name));
    FsmFailure(&b->ipcp.fsm, FAIL_NEGOT_FAILURE);
    return;
  };

  /* Set addresses */
  IfaceChangeAddr(b, 1, &iface->self_addr, &iface->peer_addr);

  /* Proxy ARP for peer if desired and peer's address is known */
  u_addrclear(&iface->proxy_addr);
  if (Enabled(&iface->options, IFACE_CONF_PROXY)) {
    if (u_addrempty(&iface->peer_addr)) {
      Log(LG_IFACE,
	("[%s] can't proxy arp for %s",
	b->name, u_addrtoa(&iface->peer_addr,hisaddr,sizeof(hisaddr))));
    } else if (GetEther(&iface->peer_addr, &hwa) < 0) {
      Log(LG_IFACE,
	("[%s] no interface to proxy arp on for %s",
	b->name, u_addrtoa(&iface->peer_addr,hisaddr,sizeof(hisaddr))));
    } else {
      ether = (u_char *) LLADDR(&hwa);
      if (ExecCmdNosh(LG_IFACE2, b->name, 
	  "%s -S %s %x:%x:%x:%x:%x:%x pub",
	  PATH_ARP, u_addrtoa(&iface->peer_addr,hisaddr,sizeof(hisaddr)),
	  ether[0], ether[1], ether[2],
	  ether[3], ether[4], ether[5]) == 0)
	iface->proxy_addr = iface->peer_addr;
    }
  }
  
    /* Add static routes */
    SLIST_FOREACH(r, &iface->routes, next) {
	if (u_rangefamily(&r->dest)==AF_INET) {
	    r->ok = (IfaceSetRoute(b, RTM_ADD, &r->dest, &iface->peer_addr) == 0);
	}
    }
    /* Add dynamic routes */
    SLIST_FOREACH(r, &b->params.routes, next) {
	if (u_rangefamily(&r->dest)==AF_INET) {
	    r->ok = (IfaceSetRoute(b, RTM_ADD, &r->dest, &iface->peer_addr) == 0);
	}
    }

#ifdef USE_NG_NAT
  /* Set NAT IP */
  if (iface->nat_up) {
    IfaceSetupNAT(b);
  }
#endif

  /* Call "up" script */
  if (*iface->up_script) {
    char	selfbuf[40],peerbuf[40];
    char	ns1buf[21], ns2buf[21];

    if(b->ipcp.want_dns[0].s_addr != 0)
      snprintf(ns1buf, sizeof(ns1buf), "dns1 %s", inet_ntoa(b->ipcp.want_dns[0]));
    else
      ns1buf[0] = '\0';
    if(b->ipcp.want_dns[1].s_addr != 0)
      snprintf(ns2buf, sizeof(ns2buf), "dns2 %s", inet_ntoa(b->ipcp.want_dns[1]));
    else
      ns2buf[0] = '\0';

    ExecCmd(LG_IFACE2, b->name, "%s %s inet %s %s '%s' %s %s",
      iface->up_script, iface->ifname, u_rangetoa(&iface->self_addr,selfbuf, sizeof(selfbuf)),
      u_addrtoa(&iface->peer_addr, peerbuf, sizeof(peerbuf)), 
      *b->params.authname ? b->params.authname : "-", 
      ns1buf, ns2buf);
  }

}

/*
 * IfaceIpIfaceDown()
 *
 * Bring down the IP interface. This implies we're no longer ready.
 */

void
IfaceIpIfaceDown(Bund b)
{
  IfaceState	const iface = &b->iface;
  IfaceRoute	r;
  char          buf[64];

  /* Call "down" script */
  if (*iface->down_script) {
    ExecCmd(LG_IFACE2, b->name, "%s %s inet '%s'",
      iface->down_script, iface->ifname, 
      *b->params.authname ? b->params.authname : "-");
  }

    /* Delete dynamic routes */
    SLIST_FOREACH(r, &b->params.routes, next) {
	if (u_rangefamily(&r->dest)==AF_INET) {
	    if (!r->ok)
		continue;
	    IfaceSetRoute(b, RTM_DELETE, &r->dest, &iface->peer_addr);
	    r->ok = 0;
	}
    }
    /* Delete static routes */
    SLIST_FOREACH(r, &iface->routes, next) {
	if (u_rangefamily(&r->dest)==AF_INET) {
	    if (!r->ok)
		continue;
	    IfaceSetRoute(b, RTM_DELETE, &r->dest, &iface->peer_addr);
	    r->ok = 0;
	}
    }

  /* Delete any proxy arp entry */
  if (!u_addrempty(&iface->proxy_addr))
    ExecCmdNosh(LG_IFACE2, b->name, "%s -d %s", PATH_ARP, u_addrtoa(&iface->proxy_addr, buf, sizeof(buf)));
  u_addrclear(&iface->proxy_addr);

  /* Remove address from interface */
  IfaceChangeAddr(b, 0, &iface->self_addr, &iface->peer_addr);
    
  IfaceNgIpShutdown(b);
}

/*
 * IfaceIpv6IfaceUp()
 *
 * Bring up the IPv6 interface. The "ready" flag means that
 * IPCP is also up and we can deliver packets immediately. We signal
 * that the interface is not "ready" with the IFF_LINK0 flag.
 */

void
IfaceIpv6IfaceUp(Bund b, int ready)
{
  IfaceState		const iface = &b->iface;
  IfaceRoute		r;
  struct u_range	rng;

  if (ready) {

    iface->self_ipv6_addr.family = AF_INET6;
    iface->self_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[0] = 0x80fe;  /* Network byte order */
    iface->self_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[1] = 0x0000;
    iface->self_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[2] = 0x0000;
    iface->self_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[3] = 0x0000;
    iface->self_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[4] = ((u_short*)b->ipv6cp.myintid)[0];
    iface->self_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[5] = ((u_short*)b->ipv6cp.myintid)[1];
    iface->self_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[6] = ((u_short*)b->ipv6cp.myintid)[2];
    iface->self_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[7] = ((u_short*)b->ipv6cp.myintid)[3];

    iface->peer_ipv6_addr.family = AF_INET6;
    iface->peer_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[0] = 0x80fe;  /* Network byte order */
    iface->peer_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[1] = 0x0000;
    iface->peer_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[2] = 0x0000;
    iface->peer_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[3] = 0x0000;
    iface->peer_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[4] = ((u_short*)b->ipv6cp.hisintid)[0];
    iface->peer_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[5] = ((u_short*)b->ipv6cp.hisintid)[1];
    iface->peer_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[6] = ((u_short*)b->ipv6cp.hisintid)[2];
    iface->peer_ipv6_addr.u.ip6.__u6_addr.__u6_addr16[7] = ((u_short*)b->ipv6cp.hisintid)[3];
  }

  if (IfaceNgIpv6Init(b, ready)) {
    Log(LG_ERR, ("[%s] IfaceNgIpv6Init() failed, closing IPv6CP", b->name));
    FsmFailure(&b->ipv6cp.fsm, FAIL_NEGOT_FAILURE);
    return;
  };
  
    /* Set addresses */
    rng.addr = iface->self_ipv6_addr;
    rng.width = 64;
    IfaceChangeAddr(b, 1, &rng, &iface->peer_ipv6_addr);
  
    /* Add static routes */
    SLIST_FOREACH(r, &iface->routes, next) {
	if (u_rangefamily(&r->dest)==AF_INET6) {
	    r->ok = (IfaceSetRoute(b, RTM_ADD, &r->dest, &iface->peer_ipv6_addr) == 0);
	}
    }
    /* Add dynamic routes */
    SLIST_FOREACH(r, &b->params.routes, next) {
	if (u_rangefamily(&r->dest)==AF_INET6) {
	    r->ok = (IfaceSetRoute(b, RTM_ADD, &r->dest, &iface->peer_ipv6_addr) == 0);
	}
    }

  /* Call "up" script */
  if (*iface->up_script) {
    char	selfbuf[64],peerbuf[64];

    ExecCmd(LG_IFACE2, b->name, "%s %s inet6 %s%%%s %s%%%s '%s'",
      iface->up_script, iface->ifname, 
      u_addrtoa(&iface->self_ipv6_addr, selfbuf, sizeof(selfbuf)), iface->ifname,
      u_addrtoa(&iface->peer_ipv6_addr, peerbuf, sizeof(peerbuf)), iface->ifname, 
      *b->params.authname ? b->params.authname : "-");
  }

}

/*
 * IfaceIpv6IfaceDown()
 *
 * Bring down the IPv6 interface. This implies we're no longer ready.
 */

void
IfaceIpv6IfaceDown(Bund b)
{
  IfaceState		const iface = &b->iface;
  IfaceRoute		r;
  struct u_range        rng;

  /* Call "down" script */
  if (*iface->down_script) {
    ExecCmd(LG_IFACE2, b->name, "%s %s inet6 '%s'",
      iface->down_script, iface->ifname, 
      *b->params.authname ? b->params.authname : "-");
  }

    /* Delete dynamic routes */
    SLIST_FOREACH(r, &b->params.routes, next) {
	if (u_rangefamily(&r->dest)==AF_INET6) {
	    if (!r->ok)
		continue;
	    IfaceSetRoute(b, RTM_DELETE, &r->dest, &iface->peer_ipv6_addr);
	    r->ok = 0;
	}
    }
    /* Delete static routes */
    SLIST_FOREACH(r, &iface->routes, next) {
	if (u_rangefamily(&r->dest)==AF_INET6) {
	    if (!r->ok)
		continue;
	    IfaceSetRoute(b, RTM_DELETE, &r->dest, &iface->peer_ipv6_addr);
	    r->ok = 0;
	}
    }

  if (!u_addrempty(&iface->self_ipv6_addr)) {
    /* Remove address from interface */
    rng.addr = iface->self_ipv6_addr;
    rng.width = 64;
    IfaceChangeAddr(b, 0, &rng, &iface->peer_ipv6_addr);
  }

  IfaceNgIpv6Shutdown(b);
}

/*
 * IfaceIdleTimeout()
 */

static void
IfaceIdleTimeout(void *arg)
{
    Bund b = (Bund)arg;

  IfaceState			const iface = &b->iface;
  int				k;

  /* Get updated bpf node traffic statistics */
  BundUpdateStats(b);

  /* Mark current traffic period if there was traffic */
  if (iface->idleStats.recvFrames + iface->idleStats.xmitFrames < 
	b->stats.recvFrames + b->stats.xmitFrames) {
    iface->traffic[0] = TRUE;
  } else {		/* no demand traffic for a whole idle timeout period? */
    for (k = 0; k < IFACE_IDLE_SPLIT && !iface->traffic[k]; k++);
    if (k == IFACE_IDLE_SPLIT) {
      Log(LG_BUND, ("[%s] idle timeout",
	b->name));
      RecordLinkUpDownReason(b, NULL, 0, STR_IDLE_TIMEOUT, NULL);
      BundClose(b);
      return;
    }
  }

  iface->idleStats = b->stats;

  /* Shift traffic history */
  memmove(iface->traffic + 1,
    iface->traffic, (IFACE_IDLE_SPLIT - 1) * sizeof(*iface->traffic));
  iface->traffic[0] = FALSE;

  /* Restart timer */
  TimerStart(&iface->idleTimer);
}

/*
 * IfaceSessionTimeout()
 */

static void
IfaceSessionTimeout(void *arg)
{
    Bund b = (Bund)arg;

  Log(LG_BUND, ("[%s] session timeout ", b->name));

  RecordLinkUpDownReason(b, NULL, 0, STR_SESSION_TIMEOUT, NULL);

  BundClose(b);

}

/*
 * IfaceCachePkt()
 *
 * A packet caused dial-on-demand; save it for later if possible.
 * Consumes the mbuf in any case.
 */

static void
IfaceCachePkt(Bund b, int proto, Mbuf pkt)
{
  IfaceState	const iface = &b->iface;
  Mbuf		new;
  int		len;

  /* Only cache network layer data */
  if (!PROT_NETWORK_DATA(proto)) {
    PFREE(pkt);
    return;
  }

  /* Release previously cached packet, if any, and save this one */
  if (iface->dodCache.pkt)
    PFREE(iface->dodCache.pkt);

  /* Make an own permanent pkt copy */
  new = mballoc(pkt->type, len = plength(pkt));
  assert(mbread(pkt, MBDATA(new), len, NULL) == NULL);

  iface->dodCache.pkt = new;
  iface->dodCache.proto = proto;
  iface->dodCache.ts = time(NULL);
}

/*
 * IfaceCacheSend()
 *
 * Send cached packet
 */

static void
IfaceCacheSend(Bund b)
{
  IfaceState	const iface = &b->iface;

  if (iface->dodCache.pkt) {
    if (iface->dodCache.ts + MAX_DOD_CACHE_DELAY < time(NULL))
      PFREE(iface->dodCache.pkt);
    else {
      if (NgFuncWritePppFrame(b, NG_PPP_BUNDLE_LINKNUM,
	  iface->dodCache.proto, iface->dodCache.pkt) < 0) {
	Log(LG_ERR, ("[%s] can't write cached pkt: %s",
	  b->name, strerror(errno)));
      }
    }
    iface->dodCache.pkt = NULL;
  }
}

/*
 * IfaceIsDemand()
 *
 * Determine if this outgoing packet qualifies for dial-on-demand
 * Packet must be contiguous
 */

static int
IfaceIsDemand(int proto, Mbuf pkt)
{
  switch (proto) {
    case PROTO_IP:
      {
	u_char	buf[256];
	struct ip       *const ip = (struct ip *)(&buf);

	mbcopy(pkt, buf, sizeof(buf));
	switch (ip->ip_p) {
	  case IPPROTO_IGMP:		/* No multicast stuff */
	    return(0);
	  case IPPROTO_ICMP:
	    {
	      struct icmp	*const icmp =
		(struct icmp *) ((u_int32_t *) ip + ip->ip_hl);

	      switch (icmp->icmp_type)	/* No ICMP replies */
	      {
		case ICMP_ECHOREPLY:
		case ICMP_UNREACH:
		case ICMP_REDIRECT:
		  return(0);
		default:
		  break;
	      }
	    }
	    break;
	  case IPPROTO_UDP:
	    {
	      struct udphdr	*const udp =
		(struct udphdr *) ((u_int32_t *) ip + ip->ip_hl);

#define NTP_PORT	123
	      if (ntohs(udp->uh_dport) == NTP_PORT)	/* No NTP packets */
		return(0);
	    }
	    break;
	  case IPPROTO_TCP:
	    {
	      struct tcphdr	*const tcp =
		(struct tcphdr *) ((u_int32_t *) ip + ip->ip_hl);

	      if (tcp->th_flags & TH_RST)	/* No TCP reset packets */
		return(0);
	    }
	    break;
	  default:
	    break;
	}
	break;
      }
    default:
      break;
  }
  return(1);
}

/*
 * IfaceSetCommand()
 */

static int
IfaceSetCommand(Context ctx, int ac, char *av[], void *arg)
{
  IfaceState	const iface = &ctx->bund->iface;

  if (ac == 0)
    return(-1);
  switch ((intptr_t)arg) {
    case SET_IDLE:
      iface->idle_timeout = atoi(*av);
      break;
    case SET_SESSION:
      iface->session_timeout = atoi(*av);
      break;
    case SET_ADDRS:
      {
	struct u_range	self_addr;
	struct u_addr	peer_addr;

	/* Parse */
	if (ac != 2)
	  return(-1);
	if (!ParseRange(av[0], &self_addr, ALLOW_IPV4)) {
	  Log(LG_ERR, ("[%s] IFACE: Bad IP address \"%s\"", ctx->bund->name, av[0]));
	  return(-1);
	}
	if (!ParseAddr(av[1], &peer_addr, ALLOW_IPV4)) {
	  Log(LG_ERR, ("[%s] IFACE: Bad IP address \"%s\"", ctx->bund->name, av[1]));
	  return(-1);
	}

	/* OK */
	iface->self_addr = self_addr;
	iface->peer_addr = peer_addr;
      }
      break;

    case SET_ROUTE:
      {
	struct u_range		range;
	IfaceRoute		r;

	/* Check */
	if (ac != 1)
	  return(-1);

	/* Get dest address */
	if (!strcasecmp(av[0], "default")) {
	  u_rangeclear(&range);
	  range.addr.family=AF_INET;
	}
	else if (!ParseRange(av[0], &range, ALLOW_IPV4|ALLOW_IPV6)) {
	  Log(LG_ERR, ("[%s] IFACE: Bad route dest address \"%s\"", ctx->bund->name, av[0]));
	  return(-1);
	}
	r = Malloc(MB_IFACE, sizeof(struct ifaceroute));
	r->dest = range;
	r->ok = 0;
	SLIST_INSERT_HEAD(&iface->routes, r, next);
      }
      break;

    case SET_MTU:
      {
	int	max_mtu;

	max_mtu = atoi(av[0]);
	if (max_mtu < IFACE_MIN_MTU || max_mtu > IFACE_MAX_MTU) {
	  Log(LG_ERR, ("[%s] IFACE: Invalid interface mtu %d", ctx->bund->name, max_mtu));
	  return(-1);
	}
	iface->max_mtu = max_mtu;
      }
      break;

    case SET_UP_SCRIPT:
      switch (ac) {
	case 0:
	  *iface->up_script = 0;
	  break;
	case 1:
	  snprintf(iface->up_script,
	    sizeof(iface->up_script), "%s", av[0]);
	  break;
	default:
	  return(-1);
      }
      break;

    case SET_DOWN_SCRIPT:
      switch (ac) {
	case 0:
	  *iface->down_script = 0;
	  break;
	case 1:
	  snprintf(iface->down_script,
	    sizeof(iface->down_script), "%s", av[0]);
	  break;
	default:
	  return(-1);
      }
      break;

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

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

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

/*
 * IfaceStat()
 */

int
IfaceStat(Context ctx, int ac, char *av[], void *arg)
{
  IfaceState	const iface = &ctx->bund->iface;
  IfaceRoute	r;
  int		k;
  char          buf[64];

  Printf("Interface configuration:\r\n");
  Printf("\tName            : %s\r\n", iface->ifname);
  Printf("\tMaximum MTU     : %d bytes\r\n", iface->max_mtu);
  Printf("\tIdle timeout    : %d seconds\r\n", iface->idle_timeout);
  Printf("\tSession timeout : %d seconds\r\n", iface->session_timeout);
  Printf("\tEvent scripts\r\n");
  Printf("\t  up-script     : \"%s\"\r\n",
    *iface->up_script ? iface->up_script : "<none>");
  Printf("\t  down-script   : \"%s\"\r\n",
    *iface->down_script ? iface->down_script : "<none>");
  Printf("Interface options:\r\n");
  OptStat(ctx, &iface->options, gConfList);
  if (!SLIST_EMPTY(&iface->routes)) {
    Printf("Static routes via peer:\r\n");
    SLIST_FOREACH(r, &iface->routes, next) {
	Printf("\t%s\r\n", u_rangetoa(&r->dest,buf,sizeof(buf)));
    }
  }
  Printf("Interface status:\r\n");
  Printf("\tAdmin status    : %s\r\n", iface->open ? "OPEN" : "CLOSED");
  Printf("\tStatus          : %s\r\n", iface->up ? "UP" : "DOWN");
  if (iface->up)
    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 && !SLIST_EMPTY(&ctx->bund->params.routes)) {
    Printf("Dynamic routes via peer:\r\n");
    SLIST_FOREACH(r, &ctx->bund->params.routes, next) {
	Printf("\t%s\r\n", u_rangetoa(&r->dest,buf,sizeof(buf)));
    }
  }
  if (iface->up && (ctx->bund->params.acl_limits[0] || ctx->bund->params.acl_limits[1])) {
    struct acl	*a;
    Printf("Traffic filters:\r\n");
    for (k = 0; k < ACL_FILTERS; k++) {
	a = ctx->bund->params.acl_filters[k];
	while (a) {
	    Printf("\t%d#%d\t: '%s'\r\n", (k + 1), a->number, a->rule);
	    a = a->next;
	}
    }
    Printf("Traffic limits:\r\n");
    for (k = 0; k < 2; k++) {
	a = ctx->bund->params.acl_limits[k];
	while (a) {
	    Printf("\t%s#%d\t: '%s'\r\n", (k?"out":"in"), a->number, a->rule);
	    a = a->next;
	}
    }
  }
  return(0);
}

/*
 * IfaceSetMTU()
 *
 * Set MTU and bandwidth on bundle's interface
 */

void
IfaceSetMTU(Bund b, int mtu)
{
  IfaceState	const iface = &b->iface;
  struct ifreq	ifr;
  int		s;

  /* Get socket */
  if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
    Perror("[%s] IFACE: Can't get socket to set MTU!", b->name);
    return;
  }

  if ((b->params.mtu > 0) && (mtu > b->params.mtu)) {
    mtu = b->params.mtu;
    Log(LG_IFACE2, ("[%s] IFACE: forcing MTU of auth backend: %d bytes",
      b->name, mtu));
  }

  /* Limit MTU to configured maximum */
  if (mtu > iface->max_mtu) {
      mtu = iface->max_mtu;
  }

  /* Set MTU on interface */
  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, b->iface.ifname, sizeof(ifr.ifr_name));
  ifr.ifr_mtu = mtu;
  Log(LG_IFACE2, ("[%s] IFACE: setting %s MTU to %d bytes",
    b->name, b->iface.ifname, mtu));
  if (ioctl(s, SIOCSIFMTU, (char *)&ifr) < 0)
    Perror("[%s] IFACE: ioctl(%s, %s)", b->name, b->iface.ifname, "SIOCSIFMTU");
  close(s);

  /* Save MTU */
  iface->mtu = mtu;
}

void
IfaceChangeFlags(Bund b, int clear, int set)
{
    struct ifreq ifrq;
    int s, new_flags;

    Log(LG_IFACE2, ("[%s] IFACE: Change interface flags: -%d +%d",
	b->name, clear, set)); 

    if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
	Perror("[%s] IFACE: Can't get socket to change interface flags!", b->name);
	return;
    }

    memset(&ifrq, '\0', sizeof(ifrq));
    strncpy(ifrq.ifr_name, b->iface.ifname, sizeof(ifrq.ifr_name) - 1);
    ifrq.ifr_name[sizeof(ifrq.ifr_name) - 1] = '\0';
    if (ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
	Perror("[%s] IFACE: ioctl(%s, %s)", b->name, b->iface.ifname, "SIOCGIFFLAGS");
	close(s);
	return;
    }
    new_flags = (ifrq.ifr_flags & 0xffff) | (ifrq.ifr_flagshigh << 16);

    new_flags &= ~clear;
    new_flags |= set;

    ifrq.ifr_flags = new_flags & 0xffff;
    ifrq.ifr_flagshigh = new_flags >> 16;

    if (ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
	Perror("[%s] IFACE: ioctl(%s, %s)", b->name, b->iface.ifname, "SIOCSIFFLAGS");
	close(s);
	return;
    }
    close(s);
}

#if defined(__KAME__) && !defined(NOINET6)
static void
add_scope(struct sockaddr *sa, int ifindex)
{
  struct sockaddr_in6 *sa6;

  if (sa->sa_family != AF_INET6)
    return;
  sa6 = (struct sockaddr_in6 *)sa;
  if (!IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) &&
      !IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr))
    return;
  if (*(u_int16_t *)&sa6->sin6_addr.s6_addr[2] != 0)
    return;
  *(u_int16_t *)&sa6->sin6_addr.s6_addr[2] = htons(ifindex);
}
#endif

void
IfaceChangeAddr(Bund b, int add, struct u_range *self, struct u_addr *peer)
{
    struct ifaliasreq ifra;
    struct in6_aliasreq ifra6;
    struct sockaddr_in *me4, *msk4, *peer4;
    struct sockaddr_storage ssself, sspeer, ssmsk;
    int res = 0;
    int s;
    char buf[64], buf1[64];

    Log(LG_IFACE2, ("[%s] IFACE: %s address %s->%s %s %s",
	b->name, add?"Add":"Remove", u_rangetoa(self, buf, sizeof(buf)), 
	((peer != NULL)?u_addrtoa(peer, buf1, sizeof(buf1)):""),
	add?"to":"from", b->iface.ifname));

    u_rangetosockaddrs(self, &ssself, &ssmsk);
    if (peer)
	u_addrtosockaddr(peer, 0, &sspeer);

    if ((s = socket(self->addr.family, SOCK_DGRAM, 0)) < 0) {
	Perror("[%s] IFACE: Can't get socket to change interface address!", b->name);
	return;
    }

    switch (self->addr.family) {
      case AF_INET:
	memset(&ifra, '\0', sizeof(ifra));
	strncpy(ifra.ifra_name, b->iface.ifname, sizeof(ifra.ifra_name) - 1);

	me4 = (struct sockaddr_in *)&ifra.ifra_addr;
	memcpy(me4, &ssself, sizeof(*me4));

	msk4 = (struct sockaddr_in *)&ifra.ifra_mask;
	memcpy(msk4, &ssmsk, sizeof(*msk4));

	peer4 = (struct sockaddr_in *)&ifra.ifra_broadaddr;
	if (peer == NULL || peer->family == AF_UNSPEC) {
    	    peer4->sin_family = AF_INET;
    	    peer4->sin_len = sizeof(*peer4);
    	    peer4->sin_addr.s_addr = INADDR_NONE;
	} else
    	    memcpy(peer4, &sspeer, sizeof(*peer4));

	res = ioctl(s, add?SIOCAIFADDR:SIOCDIFADDR, &ifra);
	if (res == -1) {
	    Perror("[%s] IFACE: %s IPv4 address %s %s failed", 
		b->name, add?"Adding":"Removing", add?"to":"from", b->iface.ifname);
	}
	break;

      case AF_INET6:
	memset(&ifra6, '\0', sizeof(ifra6));
	strncpy(ifra6.ifra_name, b->iface.ifname, sizeof(ifra6.ifra_name) - 1);

	memcpy(&ifra6.ifra_addr, &ssself, sizeof(ifra6.ifra_addr));
	memcpy(&ifra6.ifra_prefixmask, &ssmsk, sizeof(ifra6.ifra_prefixmask));
	if (peer == NULL || peer->family == AF_UNSPEC)
    	    ifra6.ifra_dstaddr.sin6_family = AF_UNSPEC;
	else if (memcmp(&((struct sockaddr_in6 *)&ssmsk)->sin6_addr, &in6mask128,
		    sizeof(in6mask128)) == 0)
    	    memcpy(&ifra6.ifra_dstaddr, &sspeer, sizeof(ifra6.ifra_dstaddr));
	ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
	ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;

	res = ioctl(s, add?SIOCAIFADDR_IN6:SIOCDIFADDR_IN6, &ifra6);
	if (res == -1) {
	    Perror("[%s] IFACE: %s IPv6 address %s %s failed", 
		b->name, add?"Adding":"Removing", add?"to":"from", b->iface.ifname);
	}
	break;
    }
    close(s);
}

struct rtmsg {
  struct rt_msghdr m_rtm;
  char m_space[256];
};

static size_t
memcpy_roundup(char *cp, const void *data, size_t len)
{
  size_t padlen;

#define ROUNDUP(x) ((x) ? (1 + (((x) - 1) | (sizeof(long) - 1))) : sizeof(long))
  padlen = ROUNDUP(len);
  memcpy(cp, data, len);
  if (padlen > len)
    memset(cp + len, '\0', padlen - len);

  return padlen;
}

int
IfaceSetRoute(Bund b, int cmd, struct u_range *dst,
       struct u_addr *gw)
{
    struct rtmsg rtmes;
    int s, nb, wb;
    char *cp;
    const char *cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
    struct sockaddr_storage sadst, samask, sagw;
    char buf[64], buf1[64];

    s = socket(PF_ROUTE, SOCK_RAW, 0);
    if (s < 0) {
	Perror("[%s] IFACE: Can't get route socket!", b->name);
	return (-1);
    }
    memset(&rtmes, '\0', sizeof(rtmes));
    rtmes.m_rtm.rtm_version = RTM_VERSION;
    rtmes.m_rtm.rtm_type = cmd;
    rtmes.m_rtm.rtm_addrs = RTA_DST;
    rtmes.m_rtm.rtm_seq = ++gRouteSeq;
    rtmes.m_rtm.rtm_pid = gPid;
    rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;

    u_rangetosockaddrs(dst, &sadst, &samask);
#if defined(__KAME__) && !defined(NOINET6)
    add_scope((struct sockaddr *)&sadst, b->iface.ifindex);
#endif

    cp = rtmes.m_space;
    cp += memcpy_roundup(cp, &sadst, sadst.ss_len);
    if (gw != NULL) {
	u_addrtosockaddr(gw, 0, &sagw);
#if defined(__KAME__) && !defined(NOINET6)
	add_scope((struct sockaddr *)&sagw, b->iface.ifindex);
#endif
    	cp += memcpy_roundup(cp, &sagw, sagw.ss_len);
    	rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
    } else if (cmd == RTM_ADD) {
    	Log(LG_ERR, ("[%s] IfaceSetRoute: gw is not set\n", b->name));
    	close(s);
    	return (-1);
    }

    if (!u_rangehost(dst)) {
	cp += memcpy_roundup(cp, &samask, samask.ss_len);
	rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
    }

    nb = cp - (char *)&rtmes;
    rtmes.m_rtm.rtm_msglen = nb;
    wb = write(s, &rtmes, nb);
    if (wb < 0) {
    	Log(LG_ERR, ("[%s] IFACE: %s route %s %s failed: %s",
	    b->name, cmdstr, u_rangetoa(dst, buf, sizeof(buf)), 
	    ((gw != NULL)?u_addrtoa(gw, buf1, sizeof(buf1)):""),
	    (rtmes.m_rtm.rtm_errno != 0)?strerror(rtmes.m_rtm.rtm_errno):strerror(errno)));
	close(s);
	return (-1);
    }
    close(s);
    Log(LG_IFACE2, ("[%s] IFACE: %s route %s %s",
	    b->name, cmdstr, u_rangetoa(dst, buf, sizeof(buf)), 
	    ((gw != NULL)?u_addrtoa(gw, buf1, sizeof(buf1)):"")));
    return (0);
}

#ifndef USE_NG_TCPMSS
void
IfaceCorrectMSS(Mbuf pkt, uint16_t maxmss)
{
  struct ip	*iphdr;
  struct tcphdr	*tc;
  int		pktlen, hlen, olen, optlen, accumulate;
  uint16_t	*mss;
  u_char	*opt;

  if (pkt == NULL)
    return;

  iphdr = (struct ip *)MBDATAU(pkt);
  hlen = iphdr->ip_hl << 2;
  pktlen = plength(pkt) - hlen;
  tc = (struct tcphdr *)(MBDATAU(pkt) + hlen);
  hlen = tc->th_off << 2;

  /* Invalid header length or header without options. */
  if (hlen <= sizeof(struct tcphdr) || hlen > pktlen)
    return;

  /* MSS option only allowed within SYN packets. */  
  if (!(tc->th_flags & TH_SYN))
    return;

  for (olen = hlen - sizeof(struct tcphdr), opt = (u_char *)(tc + 1);
	olen > 0; olen -= optlen, opt += optlen) {
    if (*opt == TCPOPT_EOL)
      break;
    else if (*opt == TCPOPT_NOP)
      optlen = 1;
    else {
      optlen = *(opt + 1);
      if (optlen <= 0 || optlen > olen)
	break;
      if (*opt == TCPOPT_MAXSEG) {
	if (optlen != TCPOLEN_MAXSEG)
	  continue;
	mss = (u_int16_t *)(opt + 2);
	if (ntohs(*mss) > maxmss) {
	  accumulate = *mss;
	  *mss = htons(maxmss);
	  accumulate -= *mss;
	  ADJUST_CHECKSUM(accumulate, tc->th_sum);
	}
      }
    }
  }
}
#endif

static int
IfaceNgIpInit(Bund b, int ready)
{
    struct ngm_connect	cn;
    char		path[NG_PATHLEN + 1];
    char		hook[NG_HOOKLEN + 1];

    if (!ready) {
	/* Dial-on-Demand mode */
	/* Use demand hook of the socket node */
	snprintf(path, sizeof(path), ".:");
	strcpy(hook, MPD_HOOK_DEMAND_TAP);

    } else {

	snprintf(path, sizeof(path), "%s", MPD_HOOK_PPP);
	strcpy(hook, NG_PPP_HOOK_INET);

#ifdef USE_NG_NAT
	/* Add a nat node if configured */
	if (Enabled(&b->iface.options, IFACE_CONF_NAT)) {
	    if (IfaceInitNAT(b, path, hook))
		goto fail;
	    b->iface.nat_up = 1;
	}
#endif

	/* Add a tee node if configured */
	if (Enabled(&b->iface.options, IFACE_CONF_TEE)) {
	    if (IfaceInitTee(b, path, hook))
		goto fail;
	    b->iface.tee_up = 1;
	}
  
#ifdef USE_NG_IPACCT
	/* Connect a ipacct node if configured */
	if (Enabled(&b->iface.options, IFACE_CONF_IPACCT)) {
	    if (IfaceInitIpacct(b, path, hook))
		goto fail;
	    b->iface.ipacct_up = 1;
	}
#endif	/* USE_NG_IPACCT */

#ifdef USE_NG_NETFLOW
	/* Connect a netflow node if configured */
	if (Enabled(&b->iface.options, IFACE_CONF_NETFLOW_IN)) {
	    if (IfaceInitNetflow(b, path, hook, 0))
		goto fail;
	    b->iface.nfin_up = 1;
	}

	if (Enabled(&b->iface.options, IFACE_CONF_NETFLOW_OUT)) {
	    if (IfaceInitNetflow(b, path, hook, 1))
		goto fail;
	    b->iface.nfout_up = 1;
	}
#endif	/* USE_NG_NETFLOW */

    }

    if (Enabled(&b->iface.options, IFACE_CONF_TCPMSSFIX)) {
	if (IfaceInitMSS(b, path, hook))
    	    goto fail;
	b->iface.mss_up = 1;
    }

    if (IfaceInitLimits(b, path, hook))
	goto fail;

    /* Connect graph to the iface node. */
    strcpy(cn.ourhook, hook);
    snprintf(cn.path, sizeof(cn.path), "%s:", b->iface.ifname);
    snprintf(cn.peerhook, sizeof(cn.peerhook), "%s", NG_IFACE_HOOK_INET);
    if (NgSendMsg(b->csock, path,
    	    NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
	Log(LG_ERR, ("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s",
    	    b->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
	goto fail;
    }

    if (ready) {
#ifdef USE_NG_NETFLOW
	if (b->iface.nfin_up)
	    IfaceSetupNetflow(b, 0);

	if (b->iface.nfout_up)
	    IfaceSetupNetflow(b, 1);
#endif /* USE_NG_NETFLOW */

	if (b->iface.mss_up)
	    IfaceSetupMSS(b, MAXMSS(b->iface.mtu));
    }
    
    IfaceSetupLimits(b);

    /* OK */
    return(0);

fail:
    return(-1);
}

/*
 * IfaceNgIpShutdown()
 */

static void
IfaceNgIpShutdown(Bund b)
{
    char		path[NG_PATHLEN + 1];

#ifdef USE_NG_NAT
    if (b->iface.nat_up)
	IfaceShutdownNAT(b);
    b->iface.nat_up = 0;
#endif
    if (b->iface.tee_up)
	IfaceShutdownTee(b);
    b->iface.tee_up = 0;
#ifdef USE_NG_NETFLOW
    if (b->iface.nfin_up)
	IfaceShutdownNetflow(b, 0);
    b->iface.nfin_up = 0;
    if (b->iface.nfout_up)
	IfaceShutdownNetflow(b, 1);
    b->iface.nfout_up = 0;
#endif
#ifdef USE_NG_IPACCT
    if (b->iface.ipacct_up)
	IfaceShutdownIpacct(b);
    b->iface.ipacct_up = 0;
#endif
    if (b->iface.mss_up)
	IfaceShutdownMSS(b);
    b->iface.mss_up = 0;

    IfaceShutdownLimits(b);
    NgFuncDisconnect(b->csock, b->name, MPD_HOOK_PPP, NG_PPP_HOOK_INET);

    snprintf(path, sizeof(path), "%s:", b->iface.ifname);
    NgFuncDisconnect(b->csock, b->name, path, NG_IFACE_HOOK_INET);
}

static int
IfaceNgIpv6Init(Bund b, int ready)
{
    struct ngm_connect	cn;
    char		path[NG_PATHLEN + 1];

    if (!ready) {
    } else {
	/* Connect ipv6 hook of ng_ppp(4) node to the ng_iface(4) node. */
	snprintf(path, sizeof(path), "%s", MPD_HOOK_PPP);
	snprintf(cn.ourhook, sizeof(cn.ourhook), "%s", NG_PPP_HOOK_IPV6);
	snprintf(cn.path, sizeof(cn.path), "%s:", b->iface.ifname);
	snprintf(cn.peerhook, sizeof(cn.peerhook), "%s", NG_IFACE_HOOK_INET6);
	if (NgSendMsg(b->csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT, &cn,
		sizeof(cn)) < 0) {
    	    Log(LG_ERR, ("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s", 
    		b->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
    	    goto fail;
	}
    }

    /* OK */
    return(0);

fail:
    return(-1);
}

/*
 * IfaceNgIpv6Shutdown()
 */

static void
IfaceNgIpv6Shutdown(Bund b)
{
    char		path[NG_PATHLEN + 1];

    NgFuncDisconnect(b->csock, b->name, MPD_HOOK_PPP, NG_PPP_HOOK_IPV6);

    snprintf(path, sizeof(path), "%s:", b->iface.ifname);
    NgFuncDisconnect(b->csock, b->name, path, NG_IFACE_HOOK_INET);
}

#ifdef USE_NG_NAT
static int
IfaceInitNAT(Bund b, char *path, char *hook)
{
    NatState      const nat = &b->iface.nat;
    struct ngm_mkpeer	mp;
    struct ngm_name	nm;
    struct in_addr	ip;
#ifdef NG_NAT_LOG
    struct ng_nat_mode	mode;
#endif  
    Log(LG_IFACE2, ("[%s] IFACE: Connecting NAT", b->name));
  
    snprintf(mp.type, sizeof(mp.type), "%s", NG_NAT_NODE_TYPE);
    strcpy(mp.ourhook, hook);
    strcpy(mp.peerhook, NG_NAT_HOOK_IN);
    if (NgSendMsg(b->csock, path,
	NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
      Log(LG_ERR, ("[%s] can't create %s node at \"%s\"->\"%s\": %s",
	b->name, NG_NAT_NODE_TYPE, path, mp.ourhook, strerror(errno)));
      return(-1);
    }
    strlcat(path, ".", NG_PATHLEN);
    strlcat(path, hook, NG_PATHLEN);
    snprintf(nm.name, sizeof(nm.name), "mpd%d-%s-nat", gPid, b->name);
    if (NgSendMsg(b->csock, path,
	NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
      Log(LG_ERR, ("[%s] can't name %s node: %s",
	b->name, NG_NAT_NODE_TYPE, strerror(errno)));
      return(-1);
    }
    strcpy(hook, NG_NAT_HOOK_OUT);

    /* Set NAT IP */
    if (u_addrempty(&nat->alias_addr)) {
	ip.s_addr = 1; // Set something just to make it ready
    } else {
	ip = nat->alias_addr.u.ip4;
    }
    if (NgSendMsg(b->csock, path,
	    NGM_NAT_COOKIE, NGM_NAT_SET_IPADDR, &ip, sizeof(ip)) < 0) {
	Log(LG_ERR, ("[%s] can't set NAT ip: %s",
    	    b->name, strerror(errno)));
    }

#ifdef NG_NAT_LOG
    /* Set NAT mode */
    mode.flags = 0;
    if (Enabled(&nat->options, NAT_CONF_LOG))
	mode.flags |= NG_NAT_LOG;
    if (!Enabled(&nat->options, NAT_CONF_INCOMING))
	mode.flags |= NG_NAT_DENY_INCOMING;
    if (Enabled(&nat->options, NAT_CONF_SAME_PORTS))
	mode.flags |= NG_NAT_SAME_PORTS;
    if (Enabled(&nat->options, NAT_CONF_UNREG_ONLY))
	mode.flags |= NG_NAT_UNREGISTERED_ONLY;
    
    mode.mask = NG_NAT_LOG | NG_NAT_DENY_INCOMING | 
	NG_NAT_SAME_PORTS | NG_NAT_UNREGISTERED_ONLY;
    if (NgSendMsg(b->csock, path,
	    NGM_NAT_COOKIE, NGM_NAT_SET_MODE, &mode, sizeof(mode)) < 0) {
	Log(LG_ERR, ("[%s] can't set NAT mode: %s",
    	    b->name, strerror(errno)));
    }

    /* Set NAT target IP */
    if (!u_addrempty(&nat->target_addr)) {
	ip = nat->target_addr.u.ip4;
	if (NgSendMsg(b->csock, path,
		NGM_NAT_COOKIE, NGM_NAT_SET_IPADDR, &ip, sizeof(ip)) < 0) {
	    Log(LG_ERR, ("[%s] can't set NAT target IP: %s",
    		b->name, strerror(errno)));
	}
    }
#endif

    return(0);
}

static int
IfaceSetupNAT(Bund b)
{
    NatState	const nat = &b->iface.nat;
    char	path[NG_PATHLEN+1];

    if (u_addrempty(&nat->alias_addr)) {
	snprintf(path, sizeof(path), "mpd%d-%s-nat:", gPid, b->name);
	if (NgSendMsg(b->csock, path,
    		NGM_NAT_COOKIE, NGM_NAT_SET_IPADDR,
		&b->iface.self_addr.addr.u.ip4,
		sizeof(b->iface.self_addr.addr.u.ip4)) < 0) {
	    Log(LG_ERR, ("[%s] can't set NAT ip: %s",
    		b->name, strerror(errno)));
	    return (-1);
	}
    }
    return (0);
}

static void
IfaceShutdownNAT(Bund b)
{
    char	path[NG_PATHLEN+1];

    snprintf(path, sizeof(path), "mpd%d-%s-nat:", gPid, b->name);
    NgFuncShutdownNode(b->csock, b->name, path);
}
#endif

static int
IfaceInitTee(Bund b, char *path, char *hook)
{
    struct ngm_mkpeer	mp;
    struct ngm_name	nm;

    Log(LG_IFACE2, ("[%s] IFACE: Connecting tee", b->name));
  
    snprintf(mp.type, sizeof(mp.type), "%s", NG_TEE_NODE_TYPE);
    strcpy(mp.ourhook, hook);
    strcpy(mp.peerhook, NG_TEE_HOOK_RIGHT);
    if (NgSendMsg(b->csock, path,
	NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
      Log(LG_ERR, ("[%s] can't create %s node at \"%s\"->\"%s\": %s",
	b->name, NG_TEE_NODE_TYPE, path, mp.ourhook, strerror(errno)));
      return(-1);
    }
    strlcat(path, ".", NG_PATHLEN);
    strlcat(path, hook, NG_PATHLEN);
    snprintf(nm.name, sizeof(nm.name), "%s-tee", b->iface.ifname);
    if (NgSendMsg(b->csock, path,
	NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
      Log(LG_ERR, ("[%s] can't name %s node: %s",
	b->name, NG_TEE_NODE_TYPE, strerror(errno)));
      return(-1);
    }
    strcpy(hook, NG_TEE_HOOK_LEFT);

    return(0);
}

static void
IfaceShutdownTee(Bund b)
{
    char	path[NG_PATHLEN+1];

    snprintf(path, sizeof(path), "%s-tee:", b->iface.ifname);
    NgFuncShutdownNode(b->csock, b->name, path);
}

#ifdef USE_NG_IPACCT
static int
IfaceInitIpacct(Bund b, char *path, char *hook)
{
    struct ngm_mkpeer	mp;
    struct ngm_name	nm;
    struct ngm_connect  cn;
    char		path1[NG_PATHLEN+1];
    struct {
	struct ng_ipacct_mesg m;
	int		data;
    } ipam;

    Log(LG_IFACE2, ("[%s] IFACE: Connecting ipacct", b->name));
  
    snprintf(mp.type, sizeof(mp.type), "%s", NG_TEE_NODE_TYPE);
    strcpy(mp.ourhook, hook);
    strcpy(mp.peerhook, NG_TEE_HOOK_RIGHT);
    if (NgSendMsg(b->csock, path,
	NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
      Log(LG_ERR, ("[%s] can't create %s node at \"%s\"->\"%s\": %s",
	b->name, NG_TEE_NODE_TYPE, path, mp.ourhook, strerror(errno)));
      return(-1);
    }
    strlcat(path, ".", NG_PATHLEN);
    strlcat(path, hook, NG_PATHLEN);
    snprintf(nm.name, sizeof(nm.name), "%s_acct_tee", b->iface.ifname);
    if (NgSendMsg(b->csock, path,
	NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
      Log(LG_ERR, ("[%s] can't name %s node: %s",
	b->name, NG_TEE_NODE_TYPE, strerror(errno)));
      return(-1);
    }
    strcpy(hook, NG_TEE_HOOK_LEFT);

    snprintf(mp.type, sizeof(mp.type), "%s", NG_IPACCT_NODE_TYPE);
    strcpy(mp.ourhook, NG_TEE_HOOK_RIGHT2LEFT);
    snprintf(mp.peerhook, sizeof(mp.peerhook), "%s_in", b->iface.ifname);
    if (NgSendMsg(b->csock, path,
	NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
      Log(LG_ERR, ("[%s] can't create %s node at \"%s\"->\"%s\": %s",
	b->name, NG_IPACCT_NODE_TYPE, path, mp.ourhook, strerror(errno)));
      return(-1);
    }
    snprintf(path1, sizeof(path1), "%s.%s", path, NG_TEE_HOOK_RIGHT2LEFT);
    snprintf(nm.name, sizeof(nm.name), "%s_ip_acct", b->iface.ifname);
    if (NgSendMsg(b->csock, path1,
	NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
      Log(LG_ERR, ("[%s] can't name %s node: %s",
	b->name, NG_IPACCT_NODE_TYPE, strerror(errno)));
      return(-1);
    }
    strcpy(cn.ourhook, NG_TEE_HOOK_LEFT2RIGHT);
    strcpy(cn.path, NG_TEE_HOOK_RIGHT2LEFT);
    snprintf(cn.peerhook, sizeof(cn.peerhook), "%s_out", b->iface.ifname);
    if (NgSendMsg(b->csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT, &cn,
	sizeof(cn)) < 0) {
      Log(LG_ERR, ("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s", 
        b->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
      return (-1);
    }
    
    snprintf(ipam.m.hname, sizeof(ipam.m.hname), "%s_in", b->iface.ifname);
    ipam.data = DLT_RAW;
    if (NgSendMsg(b->csock, path1, NGM_IPACCT_COOKIE, NGM_IPACCT_SETDLT, 
	&ipam, sizeof(ipam)) < 0) {
      Log(LG_ERR, ("[%s] can't set DLT \"%s\"->\"%s\": %s", 
        b->name, path, ipam.m.hname, strerror(errno)));
      return (-1);
    }
    ipam.data = 10000;
    if (NgSendMsg(b->csock, path1, NGM_IPACCT_COOKIE, NGM_IPACCT_STHRS, 
	&ipam, sizeof(ipam)) < 0) {
      Log(LG_ERR, ("[%s] can't set DLT \"%s\"->\"%s\": %s", 
        b->name, path, ipam.m.hname, strerror(errno)));
      return (-1);
    }
    
    snprintf(ipam.m.hname, sizeof(ipam.m.hname), "%s_out", b->iface.ifname);
    ipam.data = DLT_RAW;
    if (NgSendMsg(b->csock, path1, NGM_IPACCT_COOKIE, NGM_IPACCT_SETDLT, 
	&ipam, sizeof(ipam)) < 0) {
      Log(LG_ERR, ("[%s] can't set DLT \"%s\"->\"%s\": %s", 
        b->name, path, ipam.m.hname, strerror(errno)));
      return (-1);
    }
    ipam.data = 10000;
    if (NgSendMsg(b->csock, path1, NGM_IPACCT_COOKIE, NGM_IPACCT_STHRS, 
	&ipam, sizeof(ipam)) < 0) {
      Log(LG_ERR, ("[%s] can't set DLT \"%s\"->\"%s\": %s", 
        b->name, path, ipam.m.hname, strerror(errno)));
      return (-1);
    }

    return(0);
}

static void
IfaceShutdownIpacct(Bund b)
{
    char	path[NG_PATHLEN+1];

    snprintf(path, sizeof(path), "%s_acct_tee:", b->iface.ifname);
    NgFuncShutdownNode(b->csock, b->name, path);
}
#endif

#ifdef USE_NG_NETFLOW
static int
IfaceInitNetflow(Bund b, char *path, char *hook, char out)
{
    struct ngm_connect	cn;

    Log(LG_IFACE2, ("[%s] IFACE: Connecting netflow (%s)", b->name, out?"out":"in"));
  
    /* Create global ng_netflow(4) node if not yet. */
    if (gNetflowNode == FALSE) {
	if (NgFuncInitGlobalNetflow(b))
	    return(-1);
    }

    /* Connect ng_netflow(4) node to the ng_bpf(4)/ng_tee(4) node. */
    strcpy(cn.ourhook, hook);
    snprintf(cn.path, sizeof(cn.path), "%s:", gNetflowNodeName);
    if (out) {
	snprintf(cn.peerhook, sizeof(cn.peerhook), "%s%d", NG_NETFLOW_HOOK_OUT,
	    gNetflowIface + b->id*2 + out);
    } else {
	snprintf(cn.peerhook, sizeof(cn.peerhook), "%s%d", NG_NETFLOW_HOOK_DATA,
	    gNetflowIface + b->id*2 + out);
    }
    if (NgSendMsg(b->csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT, &cn,
	sizeof(cn)) < 0) {
      Log(LG_ERR, ("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s", 
        b->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
      return (-1);
    }
    strlcat(path, ".", NG_PATHLEN);
    strlcat(path, hook, NG_PATHLEN);
    if (out) {
	snprintf(hook, NG_HOOKLEN, "%s%d", NG_NETFLOW_HOOK_DATA,
	    gNetflowIface + b->id*2 + out);
    } else {
	snprintf(hook, NG_HOOKLEN, "%s%d", NG_NETFLOW_HOOK_OUT,
	    gNetflowIface + b->id*2 + out);
    }
    return (0);
}

static int
IfaceSetupNetflow(Bund b, char out)
{
    char path[NG_PATHLEN + 1];
    struct ng_netflow_setdlt	 nf_setdlt;
    struct ng_netflow_setifindex nf_setidx;
    
    /* Configure data link type and interface index. */
    snprintf(path, sizeof(path), "%s:", gNetflowNodeName);
    nf_setdlt.iface = gNetflowIface + b->id*2 + out;
    nf_setdlt.dlt = DLT_RAW;
    if (NgSendMsg(b->csock, path, NGM_NETFLOW_COOKIE, NGM_NETFLOW_SETDLT,
	&nf_setdlt, sizeof(nf_setdlt)) < 0) {
      Log(LG_ERR, ("[%s] can't configure data link type on %s: %s", b->name,
	path, strerror(errno)));
      goto fail;
    }
    if (!out) {
	nf_setidx.iface = gNetflowIface + b->id*2 + out;
	nf_setidx.index = if_nametoindex(b->iface.ifname);
	if (NgSendMsg(b->csock, path, NGM_NETFLOW_COOKIE, NGM_NETFLOW_SETIFINDEX,
	    &nf_setidx, sizeof(nf_setidx)) < 0) {
    	  Log(LG_ERR, ("[%s] can't configure interface index on %s: %s", b->name,
	    path, strerror(errno)));
    	  goto fail;
	}
    }

    return 0;
fail:
    return -1;
}

static void
IfaceShutdownNetflow(Bund b, char out)
{
    char	path[NG_PATHLEN+1];
    char	hook[NG_HOOKLEN+1];

    snprintf(path, NG_PATHLEN, "%s:", gNetflowNodeName);
    snprintf(hook, NG_HOOKLEN, "%s%d", NG_NETFLOW_HOOK_DATA,
	    gNetflowIface + b->id*2 + out);
    NgFuncDisconnect(b->csock, b->name, path, hook);
    snprintf(hook, NG_HOOKLEN, "%s%d", NG_NETFLOW_HOOK_OUT,
	    gNetflowIface + b->id*2 + out);
    NgFuncDisconnect(b->csock, b->name, path, hook);
}
#endif

static int
IfaceInitMSS(Bund b, char *path, char *hook)
{
	struct ngm_mkpeer	mp;
#if NG_NODESIZ>=32
	struct ngm_name		nm;
#endif
#ifndef USE_NG_TCPMSS
	struct ngm_connect	cn;
#endif

	Log(LG_IFACE2, ("[%s] IFACE: Connecting tcpmssfix", b->name));
  
#ifdef USE_NG_TCPMSS
	/* Create ng_tcpmss(4) node. */
	snprintf(mp.type, sizeof(mp.type), "%s", NG_TCPMSS_NODE_TYPE);
	snprintf(mp.ourhook, sizeof(mp.ourhook), "%s", hook);
	snprintf(mp.peerhook, sizeof(mp.peerhook), "in");
	if (NgSendMsg(b->csock, path,
    		NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
    	    Log(LG_ERR, ("can't create %s node at \"%s\"->\"%s\": %s", 
    		NG_TCPMSS_NODE_TYPE, path, mp.ourhook, strerror(errno)));
	    goto fail;
	}

	strlcat(path, ".", NG_PATHLEN);
	strlcat(path, hook, NG_PATHLEN);
	snprintf(hook, NG_HOOKLEN, "out");

	/* Set the new node's name. */
	snprintf(nm.name, sizeof(nm.name), "mpd%d-%s-mss", gPid, b->name);
	if (NgSendMsg(b->csock, path,
    		NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
    	    Log(LG_ERR, ("can't name %s node: %s", NG_TCPMSS_NODE_TYPE,
    		strerror(errno)));
	    goto fail;
	}

#else
    /* Create a bpf node for SYN detection. */
    snprintf(mp.type, sizeof(mp.type), "%s", NG_BPF_NODE_TYPE);
    snprintf(mp.ourhook, sizeof(mp.ourhook), "%s", hook);
    snprintf(mp.peerhook, sizeof(mp.peerhook), "ppp");
    if (NgSendMsg(b->csock, path,
	    NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
    	Log(LG_ERR, ("can't create %s node at \"%s\"->\"%s\": %s", 
    	    NG_BPF_NODE_TYPE, path, mp.ourhook, strerror(errno)));
	goto fail;
    }

    strlcat(path, ".", NG_PATHLEN);
    strlcat(path, hook, NG_PATHLEN);
    strcpy(hook, "iface");

#if NG_NODESIZ>=32
    /* Set the new node's name. */
    snprintf(nm.name, sizeof(nm.name), "mpd%d-%s-mss", gPid, b->name);
    if (NgSendMsg(b->csock, path,
	    NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
    	Log(LG_ERR, ("can't name tcpmssfix %s node: %s", NG_BPF_NODE_TYPE,
    	    strerror(errno)));
	goto fail;
    }
#endif

    /* Connect to the bundle socket node. */
    snprintf(cn.path, sizeof(cn.path), "%s", path);
    snprintf(cn.ourhook, sizeof(cn.ourhook), "%s", MPD_HOOK_TCPMSS_IN);
    snprintf(cn.peerhook, sizeof(cn.peerhook), "%s", MPD_HOOK_TCPMSS_IN);
    if (NgSendMsg(b->csock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &cn,
    	    sizeof(cn)) < 0) {
    	Log(LG_ERR, ("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s", 
    	    b->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
    	goto fail;
    }

    snprintf(cn.path, sizeof(cn.path), "%s", path);
    snprintf(cn.ourhook, sizeof(cn.ourhook), "%s", MPD_HOOK_TCPMSS_OUT);
    snprintf(cn.peerhook, sizeof(cn.peerhook), "%s", MPD_HOOK_TCPMSS_OUT);
    if (NgSendMsg(b->csock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &cn,
    	    sizeof(cn)) < 0) {
    	Log(LG_ERR, ("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s", 
    	    b->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
    	goto fail;
    }
#endif

    return (0);
fail:
    return (-1);
}

/*
 * BundConfigMSS()
 *
 * Configure the tcpmss node to reduce MSS to given value.
 */

static void
IfaceSetupMSS(Bund b, uint16_t maxMSS)
{
#ifdef USE_NG_TCPMSS
  struct	ng_tcpmss_config tcpmsscfg;
  char		path[NG_PATHLEN];

  snprintf(path, sizeof(path), "mpd%d-%s-mss:", gPid, b->name);

  /* Send configure message. */
  memset(&tcpmsscfg, 0, sizeof(tcpmsscfg));
  tcpmsscfg.maxMSS = maxMSS;

  snprintf(tcpmsscfg.inHook, sizeof(tcpmsscfg.inHook), "in");
  snprintf(tcpmsscfg.outHook, sizeof(tcpmsscfg.outHook), "out");
  if (NgSendMsg(b->csock, path, NGM_TCPMSS_COOKIE, NGM_TCPMSS_CONFIG,
      &tcpmsscfg, sizeof(tcpmsscfg)) < 0) {
    Log(LG_ERR, ("[%s] can't configure %s node program: %s", b->name,
      NG_TCPMSS_NODE_TYPE, strerror(errno)));
  }
  snprintf(tcpmsscfg.inHook, sizeof(tcpmsscfg.inHook), "out");
  snprintf(tcpmsscfg.outHook, sizeof(tcpmsscfg.outHook), "in");
  if (NgSendMsg(b->csock, path, NGM_TCPMSS_COOKIE, NGM_TCPMSS_CONFIG,
      &tcpmsscfg, sizeof(tcpmsscfg)) < 0) {
    Log(LG_ERR, ("[%s] can't configure %s node program: %s", b->name,
      NG_TCPMSS_NODE_TYPE, strerror(errno)));
  }
#else
    union {
	u_char			buf[NG_BPF_HOOKPROG_SIZE(TCPSYN_PROG_LEN)];
	struct ng_bpf_hookprog	hprog;
    }				u;
    struct ng_bpf_hookprog	*const hp = &u.hprog;

    /* Setup programs for ng_bpf hooks */
    memset(&u, 0, sizeof(u));
    strcpy(hp->thisHook, "ppp");
    hp->bpf_prog_len = TCPSYN_PROG_LEN;
    memcpy(&hp->bpf_prog, &gTCPSYNProg,
        TCPSYN_PROG_LEN * sizeof(*gTCPSYNProg));
    strcpy(hp->ifMatch, MPD_HOOK_TCPMSS_IN);
    strcpy(hp->ifNotMatch, "iface");

    if (NgSendMsg(b->csock, MPD_HOOK_TCPMSS_IN, NGM_BPF_COOKIE,
	    NGM_BPF_SET_PROGRAM, hp, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) < 0) {
	Log(LG_ERR, ("[%s] can't set %s node program: %s",
    	    b->name, NG_BPF_NODE_TYPE, strerror(errno)));
    }

    memset(&u, 0, sizeof(u));
    strcpy(hp->thisHook, "iface");
    hp->bpf_prog_len = TCPSYN_PROG_LEN;
    memcpy(&hp->bpf_prog, &gTCPSYNProg,
        TCPSYN_PROG_LEN * sizeof(*gTCPSYNProg));
    strcpy(hp->ifMatch, MPD_HOOK_TCPMSS_OUT);
    strcpy(hp->ifNotMatch, "ppp");

    if (NgSendMsg(b->csock, MPD_HOOK_TCPMSS_OUT, NGM_BPF_COOKIE,
	    NGM_BPF_SET_PROGRAM, hp, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) < 0) {
	Log(LG_ERR, ("[%s] can't set %s node program: %s",
    	    b->name, NG_BPF_NODE_TYPE, strerror(errno)));
    }

    memset(&u, 0, sizeof(u));
    strcpy(hp->thisHook, MPD_HOOK_TCPMSS_IN);
    hp->bpf_prog_len = NOMATCH_PROG_LEN;
    memcpy(&hp->bpf_prog, &gNoMatchProg,
        NOMATCH_PROG_LEN * sizeof(*gNoMatchProg));
    strcpy(hp->ifMatch, "ppp");
    strcpy(hp->ifNotMatch, "ppp");

    if (NgSendMsg(b->csock, MPD_HOOK_TCPMSS_IN, NGM_BPF_COOKIE,
	    NGM_BPF_SET_PROGRAM, hp, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) < 0) {
	Log(LG_ERR, ("[%s] can't set %s node program: %s",
    	    b->name, NG_BPF_NODE_TYPE, strerror(errno)));
    }

    memset(&u, 0, sizeof(u));
    strcpy(hp->thisHook, MPD_HOOK_TCPMSS_OUT);
    hp->bpf_prog_len = NOMATCH_PROG_LEN;
    memcpy(&hp->bpf_prog, &gNoMatchProg,
        NOMATCH_PROG_LEN * sizeof(*gNoMatchProg));
    strcpy(hp->ifMatch, "iface");
    strcpy(hp->ifNotMatch, "iface");

    if (NgSendMsg(b->csock, MPD_HOOK_TCPMSS_OUT, NGM_BPF_COOKIE,
	    NGM_BPF_SET_PROGRAM, hp, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) < 0) {
	Log(LG_ERR, ("[%s] can't set %s node program: %s",
    	    b->name, NG_BPF_NODE_TYPE, strerror(errno)));
    }

#endif /* USE_NG_TCPMSS */
}

static void
IfaceShutdownMSS(Bund b)
{
#ifdef USE_NG_TCPMSS
	char	path[NG_PATHLEN+1];

	snprintf(path, sizeof(path), "mpd%d-%s-mss:", gPid, b->name);
	NgFuncShutdownNode(b->csock, b->name, path);
#else
	NgFuncShutdownNode(b->csock, b->name, MPD_HOOK_TCPMSS_IN);
#endif
}

static int
IfaceInitLimits(Bund b, char *path, char *hook)
{
    struct ngm_mkpeer	mp;
#if NG_NODESIZ>=32
    struct ngm_name	nm;
#endif

    if (b->params.acl_limits[0] || b->params.acl_limits[1]) {

	Log(LG_IFACE2, ("[%s] IFACE: Connecting limits", b->name));
  
	/* Create a bpf node for traffic filtering. */
	snprintf(mp.type, sizeof(mp.type), "%s", NG_BPF_NODE_TYPE);
	snprintf(mp.ourhook, sizeof(mp.ourhook), "%s", hook);
	snprintf(mp.peerhook, sizeof(mp.peerhook), "ppp");
	if (NgSendMsg(b->csock, path,
		NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
    	    Log(LG_ERR, ("can't create %s node at \"%s\"->\"%s\": %s", 
    		NG_BPF_NODE_TYPE, path, mp.ourhook, strerror(errno)));
	    goto fail;
	}

	strlcat(path, ".", NG_PATHLEN);
	strlcat(path, hook, NG_PATHLEN);
	strcpy(hook, "iface");

#if NG_NODESIZ>=32
	/* Set the new node's name. */
	snprintf(nm.name, sizeof(nm.name), "mpd%d-%s-lim", gPid, b->name);
	if (NgSendMsg(b->csock, path,
		NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
    	    Log(LG_ERR, ("can't name limits %s node: %s", NG_BPF_NODE_TYPE,
    		strerror(errno)));
	    goto fail;
	}
#endif

    }

    return (0);
fail:
    return (-1);
}

/*
 * BundConfigLimits()
 *
 * Configure the bpf & car nodes.
 */

static void
IfaceSetupLimits(Bund b)
{
#define	ACL_MAX_PROGLEN	4096
    union {
	u_char			buf[NG_BPF_HOOKPROG_SIZE(ACL_MAX_PROGLEN)];
	struct ng_bpf_hookprog	hprog;
    }				u;
    struct ng_bpf_hookprog	*const hp = &u.hprog;
    
    struct ngm_connect  cn;
    
    char		path[NG_PATHLEN + 1];
    char		inhook[2][NG_HOOKLEN+1];
    char		inhookn[2][NG_HOOKLEN+1];
    char		outhook[NG_HOOKLEN+1];
    struct acl		*l;
    char		str[ACL_LEN];
#define	ACL_MAX_PARAMS	5
    int			ac;
    char		*av[ACL_MAX_PARAMS];
    int			num, dir;
    int			i, p;

    if (b->params.acl_limits[0] || b->params.acl_limits[1]) {

	snprintf(path, sizeof(path), "mpd%d-%s-lim:", gPid, b->name);
	
	for (dir = 0; dir < 2; dir++) {
	    if (dir == 0) {
		strcpy(inhook[0], "ppp");
		strcpy(inhook[1], "");
		strcpy(outhook, "iface");
	    } else {
		strcpy(inhook[0], "iface");
		strcpy(inhook[1], "");
		strcpy(outhook, "ppp");
	    }
	    num = 0;
	    l = b->params.acl_limits[dir];
	
	    while (l) {
		Log(LG_IFACE2, ("[%s] IFACE: limit %s#%d: '%s'",
        	    b->name, (dir?"out":"in"), l->number, l->rule));
		strncpy(str, l->rule, sizeof(str));
    		ac = ParseLine(str, av, ACL_MAX_PARAMS, 0);
	        if (ac >= 2) {
	    	    memset(&u, 0, sizeof(u));
		    if (l->next) {
			sprintf(hp->ifNotMatch, "%d-%d-n", dir, num);
		        sprintf(inhookn[1], "%d-%d-ni", dir, num);

		        /* Connect bpf to itself. */
			strcpy(cn.ourhook, hp->ifNotMatch);
		        strcpy(cn.path, path);
		        strcpy(cn.peerhook, inhookn[1]);
			if (NgSendMsg(b->csock, path,
		    	        NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
			    Log(LG_ERR, ("[%s] IFACE: can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s",
		    		b->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
			}
		    } else {
			strcpy(hp->ifNotMatch, outhook);
			strcpy(inhookn[1], "");
		    }
		
		    if (strcasecmp(av[0], "all") == 0) {
			hp->bpf_prog_len = MATCH_PROG_LEN;
			memcpy(&hp->bpf_prog, &gMatchProg,
    			    MATCH_PROG_LEN * sizeof(*gMatchProg));
		    } else if (strncasecmp(av[0], "flt", 3) == 0) {
			int	flt;
		    
			flt = atoi(av[0] + 3);
		        if (flt <= 0 || flt > ACL_FILTERS || b->params.acl_filters[flt - 1] == NULL) {
		    	    Log(LG_ERR, ("[%s] IFACE: incorrect filter: '%s'",
    				b->name, av[0]));
			} else {
			    struct bpf_program pr;
		    	    char	buf[16384], sbuf[256];
		    	    int		bufbraces;
		    	    struct acl	*f;
			    
			    buf[0] = 0;
			    bufbraces = 0;
			    f = b->params.acl_filters[flt - 1];
			    while (f) {
				char	*b1, *b2;
				strlcpy(sbuf, f->rule, sizeof(sbuf));
				b2 = sbuf;
				b1 = strsep(&b2, " ");
				if (b2 != NULL) {
				    if (strcasecmp(b1, "match") == 0) {
					strncat(buf, "( ", sizeof(buf));
					strncat(buf, b2, sizeof(buf));
				        strncat(buf, " ) ", sizeof(buf));
				        if (f->next) {
					    strncat(buf, "|| ( ", sizeof(buf));
					    bufbraces++;
					}
				    } else if (strcasecmp(b1, "nomatch") == 0) {
					strncat(buf, "( not ( ", sizeof(buf));
					strncat(buf, b2, sizeof(buf));
					strncat(buf, " ) ) ", sizeof(buf));
					if (f->next) {
					    strncat(buf, "&& ( ", sizeof(buf));
					    bufbraces++;
					}
				    } else {
					Log(LG_ERR, ("[%s] IFACE: filter action '%s' is unknown",
        				    b->name, b1));
				    }
				};
				f = f->next;
			    }
			    for (i = 0; i < bufbraces; i++)
				strncat(buf, ") ", sizeof(buf));
			    Log(LG_IFACE2, ("[%s] IFACE: flt%d: '%s'",
        			b->name, flt, buf));
				
			    if (pcap_compile_nopcap((u_int)-1, DLT_RAW, &pr, buf, 1, 0xffffff00)) {
				Log(LG_ERR, ("[%s] IFACE: filter '%s' compilation error",
    				    b->name, av[0]));
			    } else if (pr.bf_len > ACL_MAX_PROGLEN) {
				Log(LG_ERR, ("[%s] IFACE: filter '%s' is too long",
        			    b->name, av[0]));
				pcap_freecode(&pr);
			    } else {
				hp->bpf_prog_len = pr.bf_len;
				memcpy(&hp->bpf_prog, pr.bf_insns,
    				    pr.bf_len * sizeof(struct bpf_insn));
				pcap_freecode(&pr);
			    }
			}
		    } else {
			Log(LG_ERR, ("[%s] IFACE: incorrect filter: '%s'",
    			    b->name, av[0]));
			hp->bpf_prog_len = NOMATCH_PROG_LEN;
			memcpy(&hp->bpf_prog, &gNoMatchProg,
    			    NOMATCH_PROG_LEN * sizeof(*gNoMatchProg));
		    }
		
		    p = 1;
		    if (strcasecmp(av[p], "pass") == 0) {
			strcpy(hp->ifMatch, outhook);
			strcpy(inhookn[0], "");
		    } else if (strcasecmp(av[p], "deny") == 0) {
			strcpy(hp->ifMatch, "deny");
			strcpy(inhookn[0], "");
#ifdef USE_NG_CAR
		    } else if ((strcasecmp(av[p], "shape") == 0) ||
			       (strcasecmp(av[p], "rate-limit") == 0)) {
			struct ngm_mkpeer mp;
			struct ng_car_bulkconf car;
			char		tmppath[NG_PATHLEN + 1];

			union {
			    u_char	buf[NG_BPF_HOOKPROG_SIZE(ACL_MAX_PROGLEN)];
			    struct ng_bpf_hookprog	hprog;
			} u1;
			struct ng_bpf_hookprog	*const hp1 = &u1.hprog;

		        sprintf(hp->ifMatch, "%d-%d-m", dir, num);

			snprintf(tmppath, sizeof(tmppath), "%s%d-%d-m", path, dir, num);

			/* Create a car node for traffic shaping. */
			snprintf(mp.type, sizeof(mp.type), "%s", NG_CAR_NODE_TYPE);
			snprintf(mp.ourhook, sizeof(mp.ourhook), "%d-%d-m", dir, num);
			strcpy(mp.peerhook, ((dir == 0)?NG_CAR_HOOK_LOWER:NG_CAR_HOOK_UPPER));
			if (NgSendMsg(b->csock, path,
				NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
		    	    Log(LG_ERR, ("[%s] IFACE: can't create %s node at \"%s\"->\"%s\": %s", 
		    		b->name, NG_CAR_NODE_TYPE, path, mp.ourhook, strerror(errno)));
			}

		        /* Connect car to bpf. */
			snprintf(cn.ourhook, sizeof(cn.ourhook), "%d-%d-mi", dir, num);
			snprintf(cn.path, sizeof(cn.path), "%s", tmppath);
		        strcpy(cn.peerhook, ((dir == 0)?NG_CAR_HOOK_UPPER:NG_CAR_HOOK_LOWER));
			if (NgSendMsg(b->csock, path,
		    	        NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
			    Log(LG_ERR, ("[%s] IFACE: can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s",
		    		b->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
			}
			
			bzero(&car, sizeof(car));
			
			if (strcasecmp(av[p], "shape") == 0) {
			    car.upstream.mode = NG_CAR_SHAPE;
			} else {
			    car.upstream.mode = NG_CAR_RED;
			}
			p++;

			if ((ac > p) && (av[p][0] >= '0') && (av[p][0] <= '9')) {
			    car.upstream.cir = atol(av[p]);
			    p++;
			    if ((ac > p) && (av[p][0] >= '0') && (av[p][0] <= '9')) {
				car.upstream.cbs = atol(av[p]);
				p++;
				if ((ac > p) && (av[p][0] >= '0') && (av[p][0] <= '9')) {
				    car.upstream.ebs = atol(av[p]);
				    p++;
				} else {
				    car.upstream.ebs = car.upstream.cbs * 2;
				}
			    } else {
				car.upstream.cbs = car.upstream.cir / 8;
				car.upstream.ebs = car.upstream.cbs * 2;
			    }
			} else {
			    car.upstream.cir = 8000;
			    car.upstream.cbs = car.upstream.cir / 8;
			    car.upstream.ebs = car.upstream.cbs * 2;
			}
			car.upstream.green_action = NG_CAR_ACTION_FORWARD;
			car.upstream.yellow_action = NG_CAR_ACTION_FORWARD;
			car.upstream.red_action = NG_CAR_ACTION_DROP;
			
			car.downstream = car.upstream;
						
			if (NgSendMsg(b->csock, tmppath,
		    	        NGM_CAR_COOKIE, NGM_CAR_SET_CONF, &car, sizeof(car)) < 0) {
			    Log(LG_ERR, ("[%s] IFACE: can't set %s configuration: %s",
		    		b->name, NG_CAR_NODE_TYPE, strerror(errno)));
			}
			
			if (ac > p) {
			    if (strcasecmp(av[p], "pass") == 0) {
				memset(&u1, 0, sizeof(u1));
				strcpy(hp1->ifMatch, outhook);
			        strcpy(hp1->ifNotMatch, outhook);
			        hp1->bpf_prog_len = MATCH_PROG_LEN;
			        memcpy(&hp1->bpf_prog, &gMatchProg,
    				    MATCH_PROG_LEN * sizeof(*gMatchProg));
		    		sprintf(hp1->thisHook, "%d-%d-mi", dir, num);
			        if (NgSendMsg(b->csock, path, NGM_BPF_COOKIE, NGM_BPF_SET_PROGRAM,
					hp1, NG_BPF_HOOKPROG_SIZE(hp1->bpf_prog_len)) < 0) {
				    Log(LG_ERR, ("[%s] IFACE: can't set %s node program: %s",
	    				b->name, NG_BPF_NODE_TYPE, strerror(errno)));
				}
			    			    
				strcpy(inhookn[0], "");
			    } else {
				Log(LG_ERR, ("[%s] IFACE: unknown action: '%s'",
    				    b->name, av[p]));
				strcpy(inhookn[0], "");
			    }
			} else {
		    	    sprintf(inhookn[0], "%d-%d-mi", dir, num);
			}
#endif /* USE_NG_CAR */
		    } else {
			Log(LG_ERR, ("[%s] IFACE: unknown action: '%s'",
    			    b->name, av[1]));
			strcpy(inhookn[0], "");
		    }

		    for (i = 0; i < 2; i++) {
			if (inhook[i][0] != 0) {
			    strcpy(hp->thisHook, inhook[i]);
			    if (NgSendMsg(b->csock, path, NGM_BPF_COOKIE, NGM_BPF_SET_PROGRAM,
				    hp, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) < 0) {
				Log(LG_ERR, ("[%s] IFACE: can't set %s node program: %s",
	    			    b->name, NG_BPF_NODE_TYPE, strerror(errno)));
			    }
			}
			strcpy(inhook[i], inhookn[i]);
		    }

		    num++;
		} else {
		    Log(LG_ERR, ("[%s] IFACE: incorrect limit: '%s'",
    			b->name, l->rule));
		}
		l = l->next;
	    }
	
	    for (i = 0; i < 2; i++) {
		if (inhook[i][0] != 0) {
		    memset(&u, 0, sizeof(u));
		    strcpy(hp->thisHook, inhook[i]);
		    hp->bpf_prog_len = MATCH_PROG_LEN;
		    memcpy(&hp->bpf_prog, &gMatchProg,
    			MATCH_PROG_LEN * sizeof(*gMatchProg));
		    strcpy(hp->ifMatch, outhook);
		    strcpy(hp->ifNotMatch, outhook);
		    if (NgSendMsg(b->csock, path, NGM_BPF_COOKIE, NGM_BPF_SET_PROGRAM, 
			    hp, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) < 0) {
			Log(LG_ERR, ("[%s] IFACE: can't set %s node %s %s program (2): %s",
	    		    b->name, NG_BPF_NODE_TYPE, path, hp->thisHook, strerror(errno)));
		    }
		}
	    }
	}
    }
}

static void
IfaceShutdownLimits(Bund b)
{
    char path[NG_PATHLEN + 1];

    if (b->params.acl_limits[0] || b->params.acl_limits[1]) {
	snprintf(path, sizeof(path), "mpd%d-%s-lim:", gPid, b->name);
	NgFuncShutdownNode(b->csock, b->name, path);
    }
}


syntax highlighted by Code2HTML, v. 0.9.1