/*
 * $Id: Vflow.c,v 1.4 2004/05/02 23:42:09 jeffm Exp $
 *
 * Example script,
 *    vf = vflow.new()
 *    vf.open(afile)
 *    vf.find() { |r|
 *      # do something with entry.
 *    }
 *    vf.close()
 *
 *
 */
#include "ruby.h"
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <ftconfig.h>
#include <ftlib.h>
#include <strings.h>

typedef struct vf_data_struct {
  char *current_file;
  int fd;
  struct ftio ftio_data;
  struct ftver ftv;
  struct fts3rec_offsets ftoffsets;
} vf_type;

static VALUE vf_info_free(void *p) {
  xfree(((vf_type *)p)->current_file);
  xfree(p);
}

/* Mapping acsii string to values.
 * Should have been able to use
 *   struct ftxfield_table ftxfield_table[]
 * from flow-tools lib/ftxfield.c but this is not
 * in the same order as
 *    struct fts3rec_offsets
 * in flow-tool's ftlib.h which make calculation
 * for converstions difficult see
 * vfr_init() and vfr_fts3rec_to_vfrec below
 */
struct vfr_mapfields {
  char *name;
  u_int64 val;
  VALUE (*getter)();
  VALUE (*setter)();
};



// needed by libft.a
int debug = 0;

// Are these type identifiers?
VALUE vflow;
VALUE vflowrec;

/********* vflowrec ************
 * vflowrec is a pure ruby object to hold
 * the infomation normally held by
 * struct fts3rec_all in flow-tools
 */

/* private function to create a vfrec
 * input:
 *  struct ftio
 *  struct fts3rec_offsets
 *
 * returns:
 *  ruby object vfrec
 */

#define FT2UINT32(A,B) *(u_int32*)(A + (*offsets).B)
#define FT2UINT16(A,B) *(u_int16*)(A + (*offsets).B)
#define FT2UINT8(A,B)  *(u_int8*)(A + (*offsets).B)

static VALUE vfr_fts3rec_to_vfrec(char *rec, struct ftio *data,
				 struct fts3rec_offsets *offsets) {
  u_int32_t tmp32;
  VALUE rb_vfr_obj;

  rb_vfr_obj = rb_struct_new(vflowrec, 
			      FT_XFIELD_ASC_UNIX_SECS,
			      FT_XFIELD_ASC_UNIX_NSECS,
			      FT_XFIELD_ASC_SYSUPTIME,
			      FT_XFIELD_ASC_EXADDR,
			      FT_XFIELD_ASC_SRCADDR,
			      FT_XFIELD_ASC_DSTADDR,
			      FT_XFIELD_ASC_NEXTHOP,
			      FT_XFIELD_ASC_INPUT,
			      FT_XFIELD_ASC_OUTPUT,
			      FT_XFIELD_ASC_DFLOWS,
			      FT_XFIELD_ASC_DPKTS,
			      FT_XFIELD_ASC_DOCTETS,
			      FT_XFIELD_ASC_FIRST,
			      FT_XFIELD_ASC_LAST,
			      FT_XFIELD_ASC_SRCPORT,
			      FT_XFIELD_ASC_DSTPORT,
			      FT_XFIELD_ASC_PROT,
			      FT_XFIELD_ASC_TOS,
			      FT_XFIELD_ASC_TCP_FLAGS,
			      FT_XFIELD_ASC_ENGINE_TYPE,
			      FT_XFIELD_ASC_ENGINE_ID,
			      FT_XFIELD_ASC_SRC_MASK,
			      FT_XFIELD_ASC_DST_MASK,
			      FT_XFIELD_ASC_SRC_AS,
			      FT_XFIELD_ASC_DST_AS,
			      FT_XFIELD_ASC_IN_ENCAPS,
			      FT_XFIELD_ASC_OUT_ENCAPS,
			      FT_XFIELD_ASC_PEER_NEXTHOP,
			      FT_XFIELD_ASC_ROUTER_SC,
			      FT_XFIELD_ASC_SRC_TAG,
			      FT_XFIELD_ASC_DST_TAG,
			      FT_XFIELD_ASC_EXTRA_PKTS,
			      FT_XFIELD_ASC_MARKED_TOS,
			     0);

  if (!ftio_check_xfield(data, FT_XFIELD_UNIX_SECS)) {
    tmp32 = FT2UINT32(rec,unix_secs);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_UNIX_SECS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_UNIX_SECS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_UNIX_NSECS)) {
    tmp32 =  FT2UINT32(rec,unix_nsecs);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_UNIX_NSECS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_UNIX_NSECS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_SYSUPTIME)) {
    tmp32 = FT2UINT32(rec,sysUpTime);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SYSUPTIME),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SYSUPTIME),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_EXADDR)) {
    tmp32 = FT2UINT32(rec,exaddr);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_EXADDR),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_EXADDR),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_SRCADDR)) {
    tmp32 = FT2UINT32(rec,srcaddr);
    // FIXME: make this an IPv4 address object
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRCADDR),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRCADDR),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_DSTADDR)) {
    tmp32 = FT2UINT32(rec,dstaddr);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DSTADDR),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DSTADDR),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_NEXTHOP)) {
    tmp32 = FT2UINT32(rec,nexthop);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_NEXTHOP),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_NEXTHOP),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_INPUT)) {
    tmp32 = FT2UINT16(rec,input);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_INPUT),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_INPUT),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_OUTPUT)) {
    tmp32 = FT2UINT16(rec,output);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_OUTPUT),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_OUTPUT),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_DFLOWS)) {
    tmp32 = FT2UINT32(rec,dFlows);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DFLOWS),
   		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DFLOWS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_DPKTS)) {
    tmp32 = FT2UINT32(rec,dPkts);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DPKTS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DPKTS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_DOCTETS)) {
    tmp32 = FT2UINT32(rec,dOctets);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DOCTETS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DOCTETS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_FIRST)) {
    tmp32 = FT2UINT32(rec,First);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_FIRST),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_FIRST),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_LAST)) {
    tmp32 = FT2UINT32(rec,Last);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_LAST),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_LAST),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_SRCPORT)) {
    tmp32 = FT2UINT16(rec,srcport);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRCPORT),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRCPORT),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_DSTPORT)) {
    tmp32 = FT2UINT16(rec,dstport);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DSTPORT),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DSTPORT),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_PROT)) {
    tmp32 = FT2UINT8(rec,prot);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_PROT),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_PROT),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_TOS)) {
    tmp32 = FT2UINT8(rec,tos);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_TOS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_TOS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_TCP_FLAGS)) {
    tmp32 = FT2UINT8(rec,tcp_flags);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_TCP_FLAGS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_TCP_FLAGS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_ENGINE_TYPE)) {
    tmp32 = FT2UINT8(rec,engine_type);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_ENGINE_TYPE),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_ENGINE_TYPE),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_ENGINE_ID)) {
    tmp32 = FT2UINT8(rec,engine_id);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_ENGINE_ID),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_ENGINE_ID),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_SRC_MASK)) {
    tmp32 = FT2UINT8(rec,src_mask);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRC_MASK),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRC_MASK),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_DST_MASK)) {
    tmp32 = FT2UINT8(rec,dst_mask);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DST_MASK),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DST_MASK),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_SRC_AS)) {
    tmp32 = FT2UINT16(rec,src_as);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRC_AS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRC_AS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_DST_AS)) {
    tmp32 = FT2UINT16(rec,dst_as);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DST_AS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DST_AS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_IN_ENCAPS)) {
    tmp32 = FT2UINT8(rec,in_encaps);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_IN_ENCAPS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_IN_ENCAPS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_OUT_ENCAPS)) {
    tmp32 = FT2UINT8(rec,out_encaps);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_OUT_ENCAPS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_OUT_ENCAPS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_PEER_NEXTHOP)) {
    tmp32 = FT2UINT32(rec,peer_nexthop);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_PEER_NEXTHOP),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_PEER_NEXTHOP),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_ROUTER_SC)) {
    tmp32 = FT2UINT32(rec,router_sc);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_ROUTER_SC),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_ROUTER_SC),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_SRC_TAG)) {
    tmp32 = FT2UINT32(rec,src_tag);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRC_TAG),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_SRC_TAG),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_DST_TAG)) {
    tmp32 = FT2UINT32(rec,dst_tag);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DST_TAG),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_DST_TAG),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_EXTRA_PKTS)) {
    tmp32 = FT2UINT32(rec,extra_pkts);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_EXTRA_PKTS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_EXTRA_PKTS),
		   Qnil);
  }

  if (!ftio_check_xfield(data, FT_XFIELD_MARKED_TOS)) {
    tmp32 = FT2UINT32(rec,marked_tos);
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_MARKED_TOS),
		   INT2NUM(tmp32));
  } else {
    rb_struct_aset(rb_vfr_obj, rb_str_new2(FT_XFIELD_ASC_MARKED_TOS),
		   Qnil);
  }
  return rb_vfr_obj;
}

/*********** vflow ********/
// initialize()
static VALUE vf_init(VALUE rbself) {
  return rbself;
}

// new()
static VALUE vf_new(VALUE rbself) {
  vf_type *info = xmalloc(sizeof(vf_type));

  /* set to sensible defaults */
  info->current_file = NULL;
  VALUE vf_info = Data_Wrap_Struct(rbself, NULL, vf_info_free, info);
  rb_obj_call_init(vf_info, 0, NULL);
  return vf_info;
}

// header() - return header of current file
static VALUE vf_header(VALUE rbself) {

}

// currentfile() 
static VALUE vf_currentfile(VALUE rbself) {
  vf_type *info;
  Data_Get_Struct(rbself, vf_type, info);
  return rb_str_new2(info->current_file);
}

/* open(string)
 * opens a single file for reading
 */
static VALUE vf_open(VALUE rbself, VALUE file) {
  struct stat statdata;
  int fd;
  vf_type *info;

  Data_Get_Struct(rbself, vf_type, info);
  if (rb_type(file) == T_STRING) {
    info->current_file = STR2CSTR(file);

    /* determine file type */
    if ((fd = open(info->current_file, O_RDONLY, 0)) < 0)
      rb_raise(rb_eIOError, "Cannot open file: %s", info->current_file);
    info->fd = fd;
    if (fstat(fd, & statdata) < 0)
      rb_raise(rb_eIOError, "Cannot stat file: %s", info->current_file);

    if (!S_ISREG(statdata.st_mode)) {
      rb_raise(rb_eIOError, "File Type (0x%x) not handled for file %s",
	       statdata.st_mode, info->current_file);
    }

    if (ftio_init(&info->ftio_data, fd, FT_IO_FLAG_READ) < 0)
      rb_raise(rb_eIOError, "ftio_init() error");

    // store values needed to make calls to ftio_read and alike
    ftio_get_ver(&info->ftio_data, &info->ftv);
    fts3rec_compute_offsets(&info->ftoffsets, &info->ftv);

    return Qnil;
  }
  rb_raise(rb_eTypeError,"Not a string");
}


// close one file
static VALUE vf_close(VALUE rbself) {
  vf_type *info;

  Data_Get_Struct(rbself, vf_type, info);
  ftio_close(&info->ftio_data);  // FIXME: should check return code
  close(info->fd);
  free(info->current_file);
  info->fd = 0;
}

// next() - return next entry in file
static VALUE vf_next(VALUE rbself) {
  char *ent;
  struct fts3rec_all cur;
  vf_type *info;
  VALUE vfr_rec;

  Data_Get_Struct(rbself, vf_type, info);
  
  if ((ent = ftio_read(&info->ftio_data)) == NULL) 
    return Qnil;
  // create flow record object
  return vfr_fts3rec_to_vfrec(ent, &info->ftio_data, &info->ftoffsets);
}


/* for each recoard call block
 * Examples,
 *    each() {|flowrec| ....}
 */
static VALUE vf_each(VALUE rbself) {
  VALUE vfr_rec;

  // debugging
  //ftio_header_print(&info->ftio_data, stdout, 'f');
  while ((vfr_rec = vf_next(rbself)) != Qnil) {
    rb_yield(vfr_rec);     // call block
  }
  return Qnil;
}


void Init_Vflow() {
  vflow = rb_define_class("Vflow", rb_cObject);
  rb_define_singleton_method(vflow, "new", vf_new, 0);
  rb_define_method(vflow, "initialize", vf_init, 0);
  rb_define_method(vflow, "currentfile", vf_currentfile, 0);
  rb_define_method(vflow, "open", vf_open, 1);
  rb_define_method(vflow, "close", vf_close, 0);
  rb_define_method(vflow, "next", vf_next, 0);
  rb_define_method(vflow, "each", vf_each, 0);

  vflowrec = rb_struct_define("Vflowrec",
  			      FT_XFIELD_ASC_UNIX_SECS,
  			      FT_XFIELD_ASC_UNIX_NSECS,
  			      FT_XFIELD_ASC_SYSUPTIME,
  			      FT_XFIELD_ASC_EXADDR,
  			      FT_XFIELD_ASC_SRCADDR,
  			      FT_XFIELD_ASC_DSTADDR,
  			      FT_XFIELD_ASC_NEXTHOP,
  			      FT_XFIELD_ASC_INPUT,
  			      FT_XFIELD_ASC_OUTPUT,
  			      FT_XFIELD_ASC_DFLOWS,
  			      FT_XFIELD_ASC_DPKTS,
  			      FT_XFIELD_ASC_DOCTETS,
  			      FT_XFIELD_ASC_FIRST,
  			      FT_XFIELD_ASC_LAST,
  			      FT_XFIELD_ASC_SRCPORT,
  			      FT_XFIELD_ASC_DSTPORT,
  			      FT_XFIELD_ASC_PROT,
  			      FT_XFIELD_ASC_TOS,
  			      FT_XFIELD_ASC_TCP_FLAGS,
  			      FT_XFIELD_ASC_ENGINE_TYPE,
  			      FT_XFIELD_ASC_ENGINE_ID,
  			      FT_XFIELD_ASC_SRC_MASK,
  			      FT_XFIELD_ASC_DST_MASK,
  			      FT_XFIELD_ASC_SRC_AS,
  			      FT_XFIELD_ASC_DST_AS,
  			      FT_XFIELD_ASC_IN_ENCAPS,
  			      FT_XFIELD_ASC_OUT_ENCAPS,
  			      FT_XFIELD_ASC_PEER_NEXTHOP,
  			      FT_XFIELD_ASC_ROUTER_SC,
  			      FT_XFIELD_ASC_SRC_TAG,
  			      FT_XFIELD_ASC_DST_TAG,
  			      FT_XFIELD_ASC_EXTRA_PKTS,
  			      FT_XFIELD_ASC_MARKED_TOS,
  			      0);
}
