/* 
 * $Source: /b/cvsroot/wu_arl/msr/rp/plugins/syn_demo/syn_demo.c,v $
 * $Author: fredk $
 * $Date: 2004/12/06 17:14:35 $
 * $Revision: 1.10 $
 *
 * plugin for the SYN-flood defense demo
 *
 */

#include "stdinc.h"

#include "syn_demo.h"

MOD_MISC("syn_demo")

/* Local definition and initialization of class object.  Not
 * necessary that it be static.  fredk*/
struct rp_class syn_demo_class;

/* 
 * XXX Could put these in an extended class struct defined as:
 * typedef struct SynRpClass {
 *   struct rp_class class;
 *   struct syn_demo_queue *synqueue;
 *   int pending_conns_count, established_conns_count;
 *   int callback_period;
 *   int instance_count;
 * } SynRpClass;
 * SynRpClass syn_demo_class;
 *
 * Then the get_class function below would return &syn_demo_class.class;
 *
 * But, I'll keep this as is.
 *
 * Note, since we are using a callback function we need to protect all 
 * global variables.
 * */
struct syn_demo_queue *synqueue;
int pending_conns_count, established_conns_count;
int callback_period;
int instance_count;
int is_running;
	/* "is_running" can set by command cell; when not "running", we
	   still track connection requests, but we do not send RST packets.
	   the idea is to allow us to enable/disable SYN-flood protection
	   easily in a demonstration setting. */

void syn_demo_init_class (void);

void syndemo_callback(void);
/* call at cplclock */
void
syndemo_callback(void)
{
  struct conn_info *p, *next_p;
  msr_bufhdr_t *rst_buf;
  u_int32_t curr_time;

  /* first, see if any pending connection attempts have timed out... */
  /* not too hard since the list is in order of time of receiving the SYN */
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
      "syn_demo_handle_packet: Processing a SYN packet.\n"));
  p = TAILQ_FIRST(&synqueue->conn_head);
  curr_time = PLUGIN_CPU_CLOCK_1MSEC_FCT();
  while(p) {
    //if ((curr_time - p->time) < CONN_TIMEOUT_RUN_MSEC)
      // (is_running ? CONN_TIMEOUT_RUN_MSEC : CONN_TIMEOUT_WALK_MSEC))
    if ((curr_time - p->time) < (is_running ? CONN_TIMEOUT_RUN_MSEC : CONN_TIMEOUT_WALK_MSEC))
       break;
    /* aha! this connection request has timed out.  prepare to Nuke it */
    /* Generate a RST packet for this connection request... */
    /* (unless we're in the "not running" state) */
    if (is_running) {
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
            "syn_demo_handle_packet: Generating a RST packet.\n"));
      if ((rst_buf = PLUGIN_IPBUF_ALLOC_FCT()) == NULL) {
         MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
               "syn_demo: Unable to allocate RST packet\n"));;
      } else {
        struct ip *rst_ip;
        struct tcphdr *rst_tcp;

        /* fill in RST packet's TCP header first */
        rst_ip = (struct ip *)msr_pkt_iph(rst_buf);
        rst_tcp = (struct tcphdr *)((char *)rst_ip + sizeof(*rst_ip));
        rst_tcp->th_sport = p->sp;
        rst_tcp->th_dport = p->dp;
        rst_tcp->th_seq = htonl(ntohl(p->seq) + 1);
        rst_tcp->th_ack = 0;
        rst_tcp->th_x2 = 0;
        rst_tcp->th_off = sizeof(*rst_tcp)/4;
        rst_tcp->th_flags = TH_RST;
        rst_tcp->th_win = 0;
        rst_tcp->th_urp = 0;
        rst_tcp->th_sum = 0;
        /* zero the IP header and fill in fields used for the TCP */
        /* pseudo-header checksum;  then compute the TCP checksum */
        {int i=sizeof(*rst_ip);  char *p=(char *)rst_ip; while(i--) *p++=0; }
        rst_ip->ip_p  = IPPROTO_TCP;
        rst_ip->ip_src.s_addr  = p->sa;
        rst_ip->ip_dst.s_addr  = p->da;
        rst_ip->ip_len  = htons(sizeof(*rst_tcp));
        rst_tcp->th_sum = PLUGIN_MSR_CKSUM_FCT ((char *)rst_ip, sizeof(*rst_tcp) + sizeof(*rst_ip));

        /* now fill in the rest of the RST packet's IP header... */
        rst_ip->ip_hl = sizeof(*rst_ip) / 4;
        rst_ip->ip_v  = 4;
        rst_ip->ip_tos  = 0;
        rst_ip->ip_len  = htons(sizeof(*rst_ip) + sizeof(*rst_tcp));
        rst_ip->ip_id  = 0;
        rst_ip->ip_off  = 0;
        rst_ip->ip_ttl  = 255;
        rst_ip->ip_sum  = 0;
        rst_ip->ip_sum  = PLUGIN_MSR_CKSUM_FCT ((char *)rst_ip, msr_iphlen(rst_ip));

        /* send packet */
        PLUGIN_IP_FWD_FCT(rst_buf);
      }
    }
    /* ...and remove it from our queue */
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
              "syn_demo_handle_packet: timed-out connection, decrementing pending_conns_count (%d)\n",
              pending_conns_count));
    next_p = TAILQ_NEXT(p, clist);
    TAILQ_REMOVE(&synqueue->conn_head, p, clist);
    TAILQ_INSERT_TAIL(&synqueue->free_head, p, clist);
    pending_conns_count--;
    p = next_p;
  }
  return;
}

void set_callback(int period);
void
set_callback(int period)
{
  int ticks;
  /* XXX Take care of the callback. I use a variable for the callback period
   * so that we can add a message from the CP to change this period.
   * */
  callback_period = period;
  ticks = PLUGIN_MSR_USEC2TICKS_FCT(1000) * callback_period;
  if (PLUGIN_MSR_CLOCK_HANDLER_FCT(syndemo_callback, MSR_CLOCK_HANDLER_PCU_ID, ticks) < 0) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
          "set_callback: Unable to install periodic callback handler\n"));
  }
  return;
}

void unset_callback(void);
void
unset_callback(void)
{
  PLUGIN_MSR_HANDLER_RESET_FCT(MSR_CLOCK_HANDLER_PCU_ID, 0);
}

/* initialize plugin class */
void 
syn_demo_init_class () 
{
  int i;

  /* Must do's: Initialize classid and create_instance() */
  syn_demo_class.classid = 54203; /* unique id 0xcafebabe */
  syn_demo_class.itype   = RP_INTERFACE_TYPE_HLIST;
  syn_demo_class.create_instance = syn_demo_create_instance;

  /* Any other class specific initialization */

  /* allocate the queue for pending connections */
  /* KLUDGE: this is really something instance-pairs should do. */
  MSR_PLUGIN_MALLOC(synqueue, struct syn_demo_queue *, sizeof(struct syn_demo_queue), M_MSR, M_WAITOK);

  if (synqueue == NULL) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
          "syn_demo_init_class: malloc failed!\n"));
  }

  /* init connection queue lists */
  /* connection queue starts out empty: */
  TAILQ_INIT(&synqueue->conn_head);
  /* free list of queue entries starts out full: */
  TAILQ_INIT(&synqueue->free_head);
  for(i=0; i<CONN_QUEUE_SIZE; i++) {
   TAILQ_INSERT_TAIL(&synqueue->free_head, &synqueue->conn_array[i], clist);
  }

  pending_conns_count = established_conns_count = 0;
  instance_count = 0;
  is_running = 1;

  return;
}

/* return the class structure to the caller */
struct rp_class *syn_demo_get_class(void) {
  /* Used by the plugin framework to get class pointer */
  return &syn_demo_class;
}

/* Shouldn't we have a class destroy or remove method - that is,
 * the inverse operation of above??? fredk */

/* ----------- class methods ----------- */

/* create and initialize an instance of syn_demo,
 * Do we really need to pass a pointer to myclass and the instance id ?? fredk 
 * */
struct rp_instance *
syn_demo_create_instance(struct rp_class *myclass, u_int32_t instanceid) 
{
  struct syn_demo_instance *myinst; 
  int i, s;

  /* Must Do's: 
   *   1) allocate memory
   *   2) Initialize class pointer with your class object
   *   3) initialize method pointers with your instances
   *   4) Other global (base class) fields in rp_instance will
   *      be initialized by teh framework. In the future these other
   *      fields may not be visible directly to instances.
   *   5) Perform any instance specific processing
   *   */
  MSR_PLUGIN_MALLOC(myinst, struct syn_demo_instance *, sizeof(struct syn_demo_instance), M_MSR, M_WAITOK);

  if (myinst == NULL)
    return NULL;

  myinst->rootinstance.rpclass = &syn_demo_class;

  myinst->rootinstance.handle_packet   = syn_demo_handle_packet;
  myinst->rootinstance.free_instance   = syn_demo_free_instance;
  myinst->rootinstance.bind_instance   = syn_demo_bind_instance;
  myinst->rootinstance.unbind_instance = syn_demo_unbind_instance;
  myinst->rootinstance.handle_msg      = syn_demo_handle_msg;

  /* Instance specific processing */

  s = PLUGIN_SPLCLOCK_FCT();
  instance_count++;
  /* I am calling this here but it may be better to call it from
   * create_instance or bind instance -- for the 1st instance only, of course.
   * Then in unbind_instance or free_instance it can be removed.
   * XXX FRED
  if (instance_count == 1)
    set_callback(CONN_TIMEOUT_RUN_MSEC);
   * */
  PLUGIN_SPLX_FCT(s);

  /* return pointer to newly created instance.  Note, the semantics are that
   * the class owns this memory and so the class (free instance) should
   * free it! fredk */
  return (struct rp_instance *)myinst;
}

/* 
 * Example using the new Header List interface. The input list is assumed to
 * only contain one packet, the one just received.
 * */
void 
syn_demo_handle_packet(struct rp_instance *this, void *plist)
{
  /* now do some cool packet processing */
  HDRQ_t *hdrs = plist;
  struct ip *iph; 
  struct tcphdr *tcph; 
  u_int32_t src_addr, dst_addr;
  u_int16_t src_port, dst_port, ip_off;
  MSR_Shim_t *shim;
  u_int32_t curr_time;      /* Current time in ms */
  struct msr_bufhdr_t *hdr;
  struct syn_demo_instance *inst = (struct syn_demo_instance *)this;
  int s, tcp_flags;
  struct conn_info *p, *next_p;
  MSR_GenFilter_t fltr;


  s = PLUGIN_SPLCLOCK_FCT();

  /* get the fist (and only) packet on the list */
  if ((hdr = TAILQ_FIRST(hdrs)) == NULL) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
          "syn_demo_handle_packet: Input Packet List does not contain a valid packet!\n"));
    goto done;
  }

  curr_time = PLUGIN_CPU_CLOCK_1MSEC_FCT();
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Got packet at time - %d\n", curr_time));

  /* Examine the IP header and TCP header... */

  /* hdr->pkt points to the start of the AAL5 frame. In general, the first
   * thing in the frame is the inter-port or intra-port shim. These are
   * of different sizes - to simplify the code use the macro
   *   msr_pkt_iph(hdr->pkt)
   * defined in msr_ip.h for accessing the IP header.
   * */
  shim = msr_pkt_shim(hdr);
  iph  = (struct ip *)msr_pkt_iph(hdr);
  ip_off = ntohs(iph->ip_off);

//XXXkenw
MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          ">>>syn_demo_handle_packet: GOT PKT W/ sa = %x, da = %x\n",
				msr_ipsaddr(iph), msr_ipdaddr(iph)));

  /* KLUDGE: Drop fragmented packets */
  if ((ip_off & IP_MF) || (ip_off & IP_OFFMASK)) {
    /* remove it from the list */
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: dropping a fragment\n"));
    TAILQ_REMOVE(hdrs, hdr, qlist);
    goto done;
  }

  src_addr = htonl(msr_ipsaddr(iph));
  dst_addr = htonl(msr_ipdaddr(iph));
  src_port = htons(msr_ipsport(iph));
  dst_port = htons(msr_ipdport(iph));

  /* Determine if it is a SYN, SYN-ACK, ACK, or what (look at TCP flags) */
  tcph      = (struct tcphdr *)((char *)iph + msr_iphlen(iph));
  tcp_flags = tcph->th_flags;

  curr_time = PLUGIN_CPU_CLOCK_1MSEC_FCT();

  switch(tcp_flags & (TH_SYN|TH_ACK|TH_FIN|TH_RST)) {
    case TH_SYN: {				 /* SYN packet */
      syndemo_callback();
#if 0
      /* XXXXXXXXXXXXXXXXXXXXXXX Moved to callback * XXXXXXXXXXXXXXXXXXXXXXXXXXXX */
      /* first, see if any pending connection attempts have timed out... */
      /* not too hard since the list is in order of time of receiving the SYN */
      msr_bufhdr_t *rst_buf;
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Processing a SYN packet.\n"));
      p = TAILQ_FIRST(&synqueue->conn_head);
      while(p) {
	if ((curr_time - p->time) < CONN_TIMEOUT_MSEC)
	  break;
        MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Generating a RST packet.\n"));
	/* aha! this connection request has timed out.  prepare to Nuke it */
	/* Generate a RST packet for this connection request... */
	if ((rst_buf = PLUGIN_IPBUF_ALLOC_FCT()) == NULL) {
          MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_WARNING,
                "syn_demo: Unable to allocate RST packet\n"));;
	} else {
	  struct ip *rst_ip;
	  struct tcphdr *rst_tcp;

	  /* fill in RST packet's TCP header first */
	  rst_ip = (struct ip *)msr_pkt_iph(rst_buf);
	  rst_tcp = (struct tcphdr *)((char *)rst_ip + sizeof(*rst_ip));
	  rst_tcp->th_sport = p->sp;
	  rst_tcp->th_dport = p->dp;
	  rst_tcp->th_seq = htonl(ntohl(p->seq) + 1);
	  rst_tcp->th_ack = 0;
	  rst_tcp->th_x2 = 0;
	  rst_tcp->th_off = sizeof(*rst_tcp)/4;
	  rst_tcp->th_flags = TH_RST;
	  rst_tcp->th_win = 0;
	  rst_tcp->th_urp = 0;
	  rst_tcp->th_sum = 0;
	  /* zero the IP header and fill in fields used for the TCP */
	  /* pseudo-header checksum;  then compute the TCP checksum */
	  {int i=sizeof(*rst_ip);  char *p=(char *)rst_ip; while(i--) *p++=0; }
	  rst_ip->ip_p  = IPPROTO_TCP;
	  rst_ip->ip_src.s_addr  = p->sa;
	  rst_ip->ip_dst.s_addr  = p->da;
	  rst_ip->ip_len  = htons(sizeof(*rst_tcp));
	  rst_tcp->th_sum = PLUGIN_MSR_CKSUM_FCT ((char *)rst_ip, sizeof(*rst_tcp) +
							sizeof(*rst_ip));

	  /* now fill in the rest of the RST packet's IP header... */
	  rst_ip->ip_hl = sizeof(*rst_ip) / 4;
	  rst_ip->ip_v  = 4;
	  rst_ip->ip_tos  = 0;
	  rst_ip->ip_len  = htons(sizeof(*rst_ip) + sizeof(*rst_tcp));
	  rst_ip->ip_id  = 0;
	  rst_ip->ip_off  = 0;
	  rst_ip->ip_ttl  = 255;
	  rst_ip->ip_sum  = 0;
	  rst_ip->ip_sum  = PLUGIN_MSR_CKSUM_FCT ((char *)rst_ip,
						 msr_iphlen(rst_ip));

	  TAILQ_INSERT_TAIL(hdrs, rst_buf, qlist);
	}
	/* ...and remove it from our queue */
        MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Removing a timed-out connection.\n"));
	next_p = TAILQ_NEXT(p, clist);
	TAILQ_REMOVE(&synqueue->conn_head, p, clist);
	TAILQ_INSERT_TAIL(&synqueue->free_head, p, clist);
	pending_conns_count--;
	p = next_p;
      }
      /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
#endif /* Moved to callback */
      /* update our list of pending connections */
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Adding a connection entry.\n"));
      p = TAILQ_FIRST(&synqueue->free_head);
      if (p) {
	struct tcphdr *th = (struct tcphdr *)((char *)iph + msr_iphlen(iph));
	p->sa = src_addr;
	p->da = dst_addr;
	p->sp = src_port;
	p->dp = dst_port;
	p->seq = th->th_seq;
	p->synack_sent = 0;
	p->time = curr_time;
	TAILQ_REMOVE(&synqueue->free_head, p, clist);
	TAILQ_INSERT_TAIL(&synqueue->conn_head, p, clist);
        MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
            "syn_demo_handle_packet: Incrementing pending_conns_count (%d)\n",
            pending_conns_count));
	pending_conns_count++;
      } else {	/* no more room in the queue */
	/* REVIEW: we might as well drop the packet, yes? */
	TAILQ_REMOVE(hdrs, hdr, qlist);
	PLUGIN_IPBUF_FREE_FCT(hdr);
	goto done;
      }
      break;
    } 
    case TH_SYN|TH_ACK: {			 /* SYN-ACK packet */
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Processing a SYN-ACK packet.\n"));
      /* update the entry for this connection, if we can find it... */
      p = TAILQ_FIRST(&synqueue->conn_head);
      while(p) {
	/* N.B. src/dst are reversed since the SYN-ACK is on the backchannel */
	if ((p->da == src_addr) && (p->sa == dst_addr) &&
		(p->dp == src_port) && (p->sp == dst_port) &&
		!p->synack_sent)
	  break;	/* found it! */
	p = TAILQ_NEXT(p, clist);
      }
      if (p) {	/* only update the entry if we could find it... duh.. */
	p->synack_sent = 1;
//XXXkenw
MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
"syn_demo_handle_packet: SYN-ACK pkt done. sa = %x, da = %x\n",
						p->sa, p->da));
      }
//XXXkenw
else {
MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
"syn_demo_handle_packet: SYN-ACK pkt done BUT NOT FWD since p = NULL\n"));
}
      break;
    }
    case TH_ACK: {				/* ACK packet */
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Processing an ACK packet.\n"));
      /* update the entry for this connection, if we can find it... */
      /* ("update" really means removing the entry and setting up a EM filter)*/
      p = TAILQ_FIRST(&synqueue->conn_head);
      while(p) {
	if ((p->sa == src_addr) && (p->da == dst_addr) &&
		(p->sp == src_port) && (p->dp == dst_port) &&
		p->synack_sent)
	  break;	/* found it! */
	p = TAILQ_NEXT(p, clist);
      }
      if (p) {	/* i.e. only do this stuff if we found an entry for this flow */
        if (is_running) {
          int tmp;
          CFY_FltrParams_t params;
          params.fid = params.qid = SYNDEMO_EMQID;
          params.prio = MSR_CFY_FLTR_PRIO_INVALID;
  
  	  /* set up Exact Match filters one for this flow */
  	  /* only set up EM filter for backchannel.  that way we can monitor */
  	  /* FIN packets in the demo */
  	  fltr.saddr    = dst_addr;
  	  fltr.daddr    = src_addr;
  	  fltr.smask    = fltr.dmask    = 0xFFFFFFFF;
  	  fltr.sp_begin = fltr.sp_end   = dst_port;
  	  fltr.dp_begin = fltr.dp_end   = src_port;
  	  fltr.proto    = IPPROTO_TCP;
          /* get forward key from shim and change the sub-port number from 0 to 1 */
          {
            u_int32_t ovin = msr_shim_get_ivin(shim);
            ovin = msr_vin_set_sp(ovin, 0);		//XXXkenw
//XXXkenw            ovin = msr_vin_set_sp(ovin, 1);
            /* XXX Broken -- looks like the IVIN is not correct!! */
            // XXX working now? ovin = msr_vin_set_pn(ovin, 5);
            ovin = msr_vin_set_pn(ovin, 4);		//XXXkenw Hard codes to go to port 4
            fltr.fwdkey = msr_rt_mkfwdkey(SYNDEMO_EMQID, ovin);
          }
  	  /* Changed from fltr.fwdkey   = MSR_RT_FWDKEY_INVALID; -- see above. fredk*/
          fltr.flags    = MSR_CFY_FLAGS_EMATCH;
  
//ZZZkenw  	  fltr.pad      = 0;
          tmp = PLUGIN_MSR_CFY_FADD_FCT(MSR_CFY_ID_EXACT, &fltr, &params);
  
  	  if (tmp == MSR_CFY_RESULT_InUse) {
            MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_WARNING,
                  "syn_demo: Trying to alloate a filter that is already there!!!\n"));
          } else if (tmp != MSR_CFY_RESULT_OK) {
  	    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_WARNING,
                  "syn_demo: Unable to add EM filter for backchannel\n"));;
            /* Take some action!! */
          }
          else {
  	    established_conns_count++;
MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_WARNING,
"syn_demo: EM filter installed.  is_running = %d\n", is_running));;
	  }
        } else {
          established_conns_count++;
        }
	/* remove from the list of pending connection requests */
	TAILQ_REMOVE(&synqueue->conn_head, p, clist);
	TAILQ_INSERT_TAIL(&synqueue->free_head, p, clist);
  	MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
                  "syn_demo: SYN, SYN_ACK, ACK: decrementing pending_conns_count (%d)\n",
                  pending_conns_count));;
	pending_conns_count--;
      }
      break;
    }
    case TH_RST: 				/* RST packet */
    case TH_FIN: 				/* FIN packet */
    case TH_FIN|TH_ACK: {
      CFY_FltrParams_t params;
      int	rc;

      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Processing a RST or FIN packet.\n"));

//XXXkenw
MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
"syn_demo_handle_packet: Setup to remove EM filter\n"));
      /* connection is being torn down.  remove the EM filter */
      /* that we installed for the backchannel, and update our statistics */
      fltr.saddr    = dst_addr;
      fltr.daddr    = src_addr;
      fltr.smask    = fltr.dmask    = 0xFFFF;
      fltr.sp_begin = fltr.sp_end   = dst_port;
      fltr.dp_begin = fltr.dp_end   = src_port;
      fltr.proto    = IPPROTO_TCP;
      fltr.fwdkey   = MSR_RT_FWDKEY_INVALID;
      fltr.flags    = MSR_CFY_FLAGS_EMATCH;
//ZZZkenw      fltr.pad      = 0;

      params.fid = params.qid = -1;
      params.prio = MSR_CFY_FLTR_PRIO_INVALID;

//XXXkenw
MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
"syn_demo_handle_packet: Remove EM filter\n"));
//YYYkenw	rc = MSR_CFY_RESULT_OK;
      rc = PLUGIN_MSR_CFY_FREM_FCT(MSR_CFY_ID_EXACT, &fltr, &params);
      if (rc == MSR_CFY_RESULT_OK)
{
	established_conns_count--;
//XXXkenw
MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
"syn_demo_handle_packet: EM filter REMOVED\n"));
}
      else {
	MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: PLUGIN_MSR_CFY_FREM_FCT failed!\n"));
      }
      break;
    }
    default:

    /* other combinations of flags: do we care??  probably not. */
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: Passing packet with unusual flags.\n"));
  }

done:
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
          "syn_demo_handle_packet: done.\n"));
  PLUGIN_SPLX_FCT(s);

  return;

}

/* Perform any necessary actions then free memory for this instance */
void 
syn_demo_free_instance(struct rp_instance *this) 
{
  int s;

  s = PLUGIN_SPLCLOCK_FCT();
  instance_count--;
  /* XXX FRED
  if (instance_count == 0)
    unset_callback();
    */
  PLUGIN_SPLX_FCT(s);

  MSR_PLUGIN_FREE(this, M_MSR);
}

/* Called when instance bound to a filter
 * */
void 
syn_demo_bind_instance(struct rp_instance *this) 
{
  struct syn_demo_instance *inst = (struct syn_demo_instance *)this;
  return;
}

/* Called when instance unbound from a filter
 * */
void 
syn_demo_unbind_instance(struct rp_instance *this) 
{
  struct syn_demo_instance *inst = (struct syn_demo_instance *)this;
  struct msr_bufhdr_t *hdr;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	    "syn_demo_unbind_instance: Instance %d Being Unbound. Packet count = %d\n",
	    inst->rootinstance.instanceid));
  return;
}


/* ----------- loadable kernel module support ----------- */


/*  This defines the kernel entry point for our loadable
 *  kernel module (LKM).  It should be of type MISC and
 *  modload(8) and modunload(8) are used to load, unload 
 *  the kernel (using the /dev/lkm device).  modstat can 
 *  be used to see if the module has been loaded and its status.
 *
 * Miscellaneous Modules - there is no existing (NetBSD) interfaces in the
 * kernel that uses these modules.  This allows crossbow to manage these
 * modules once they are loaded without the std NetBSD kernel getting in the
 * way.  
 *
 */

/* XXX int or void return type? */

#ifdef PLUGIN_DEBUG
int
syn_demo (struct lkm_table *lkmtp, int cmd, int ver)
{
  struct kernel_plugin_fct_struct *fctPtr;
  extern struct kernel_plugin_fct_struct  MSR_kernel_plugin_fcts;
  fctPtr = &MSR_kernel_plugin_fcts;
#else
int 
syn_demo (struct lkm_table *lkmtp, int cmd, int ver, struct kernel_plugin_fct_struct *fctPtr) 
{
#endif
    // Do NOT put anything before the kernel_plugin_fcts is set, especially if it uses any 
    // of the MSR_ macros!!!
    if (kernel_plugin_fcts == NULL) {
	kernel_plugin_fcts = fctPtr;
	kernel_plugin_variables = fctPtr->pluginVariables;
    }
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	       "syn_demo: entry function entered\n"));
    MSR_PLUGIN_DISPATCH(lkmtp, cmd, ver, syn_demo_load, syn_demo_unload, PLUGIN_LKM_NOFUNC_FCT);
    // We do not come back from the macro MSR_PLUGIN_DISPATCH, it has a return() in it for every case
}


/* 
 * This function is called each time the module is loaded. 
 */

int syn_demo_load(struct lkm_table *lkmtp, int cmd) 
{
  int err;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "syn_demo_load: entered\n"));

  /*
   * Don't load twice! (lkmexists() is exported by kern_lkm.c)
   */
  if (PLUGIN_LKM_EXISTS_FCT(lkmtp)) {
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
		 "syn_demo_load: returning EEXIST\n"));
      return (EEXIST);
  }

  /* initialize class */
  syn_demo_init_class();

  err = PLUGIN_PCU_REGISTER_CLASS_FCT(syn_demo_get_class());

  if (err != RP_OK) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
	  "syn_demo_load: Error (err = %d) encountered registering class.\n", err));
    return -1;
  }

  return 0;
}

/* 
 * This function is called each time the module is unloaded. 
 */

int 
syn_demo_unload(struct lkm_table *lkmtp, int cmd) 
{
  struct rp_class *rpclass;
  u_int32_t cid;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "syn_demo_unload: entered about to call syn_demo_get_class()\n"));

  rpclass = syn_demo_get_class();

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "syn_demo_unload: got rpclass = %p\n", rpclass));

  cid = rpclass->classid;


  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "syn_demo_unload: got cid = 0x%lx\n", cid));

  if (PLUGIN_PCU_FREE_ALL_INSTANCES_FCT(rpclass) != RP_OK) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
	     "syn_demo_unload: Error freeing all instances for class %d\n", cid));
    return -1;
  }

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "syn_demo_unload: back from free_all_instances, now calling deregister_class\n"));

  if (PLUGIN_PCU_DEREGISTER_CLASS_FCT(rpclass) != RP_OK) {
     MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
               "syn_demo_unload: Error deregistering class\n", cid));
  }

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	"syn_demo_unload: Unloaded class %d\n", cid));

  return 0;
}

/* Inplement your own message handling protocol here 
 * The buffer is 44 Bytes long and the same buffer will be sent back to the
 * CP.  So, overwrite the contents.
 * The first word is the Instance ID.
 * */
int  
syn_demo_handle_msg(struct rp_instance *this, void *buf, u_int8_t flags, u_int8_t seq, u_int8_t *len)
{
  struct syn_demo_instance *inst = (struct syn_demo_instance *)this;
  u_int32_t *vals = (u_int32_t *)buf;
  u_int32_t id   = (u_int32_t)ntohl(*vals);
  u_int32_t cmd  = (u_int32_t)ntohl(*(vals + 1));
  u_int32_t data = (u_int32_t)ntohl(*(vals + 2));
  struct msr_bufhdr_t *hdr;
  int i;


  /* The first 4 byte word is the Instance Number for us. */
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
         "syn_demo_handle_msg: Flags 0x%x, Len %d, Seq %d, Instance %ud\n",
	    flags, *len, seq, this->instanceid));

  switch (cmd) {
  case 0:
        MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
		   "syn_demo_handle_msg: connection counts queried\n"));
      break;
  case 1:
        MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
		   "syn_demo_handle_msg: is_running flag adjusted (new %d)\n", data));
        is_running = data;
      break;
  default:
        MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
		   "command default %d\n", cmd));
      break;
  }
  *vals++ = (u_int32_t)(htonl(pending_conns_count));
  *vals++ = (u_int32_t)(htonl(established_conns_count));
  *vals++ = (u_int32_t)(htonl(is_running));
  *len = ((char *)vals) - ((char *)buf);
  return 0;
}
