// multicast plugin
//
// Forwards packets to multiple outputs.
//

// Standard includes - do not change
#include "./stdinc.h"

#include "multicast.h"

MOD_MISC("multicast")

//
// Static plugin class structure. Defined for the CLASS, not each instance.
// Change only the name
//
static struct rp_class multicast_class;

//
// Create and initialize an instance of multicast.
//
// This is where we initialize any per instance variables.
// Most of this code is standard stuff and should be left
// unchanged, except for the plugin name.
//
struct rp_instance * multicast_create_instance(
	struct rp_class *theClass,	// pointer to class structure
	u_int32_t instanceid		// identifier for new instance
) {
  struct multicast_instance *newInst; 

  // Allocate memory for new instance of multicast.
  MSR_PLUGIN_MALLOC(newInst, struct multicast_instance *, 
                    sizeof(struct multicast_instance),
                    M_MSR, 
                    M_WAITOK);
  if (newInst == NULL) return NULL;

  // Fill in instance pointers to local methods
  newInst->rootinstance.rpclass         = &multicast_class;
  newInst->rootinstance.handle_packet   = multicast_handle_packet;
  newInst->rootinstance.free_instance   = multicast_free_instance;
  newInst->rootinstance.bind_instance   = multicast_bind_instance;
  newInst->rootinstance.unbind_instance = multicast_unbind_instance;
  newInst->rootinstance.handle_msg      = multicast_handle_msg;

  newInst->rootinstance.instanceid = instanceid;

  // Initialization of per instance variables goes here.
  newInst->destVec = 0;

  return (struct rp_instance *) newInst;
}

void
copy(char *src, char *dst, int len) {	// Copy len bytes from src to dst.
  while (len-- > 0) { *dst++ = *src++; }
}

#define VOQ_MASK 0x70000
#define VOQ_SHIFT 16

void multicast_handle_packet(		// --| Handle an incoming packet |--
	struct rp_instance *this,	// pointer to instance structure
	void *bufferList		// pointer to list of packet buffers
) {
  struct multicast_instance	*inst;
  msr_bufhdr_t			*buffer, *newBuf;
  MSR_Shim_t			*shim, *newShim;
  int				i, first, select, len;

  inst = (struct multicast_instance *) this;
  buffer = msr_firstBuffer(bufferList);
  shim = msr_pkt_shim(buffer);

  if (inst->destVec == 0) {	// remove packet from input list
    msr_removeBuffer(bufferList, buffer);     
    PLUGIN_IPBUF_FREE_FCT(buffer);
    return;
  }

  len = msr_iplen(msr_pkt_iph(buffer));
  first = 1;

  for (i = 0, select = 1; i < 8; i++, select <<= 1) {
    if ((inst->destVec & select) == 0)		continue;
    if (first == 1) {		// don't need to make a copy
      first = 0;
      msr_shim_set_ovin(shim, msr_vin_make(i, 0));	// set VOQ in shim to i
      continue;
    }
    // create copy to send to output i
    if ((newBuf = PLUGIN_IPBUF_ALLOC_FCT()) == NULL)
      return; // terminate early if no buffers
    msr_addBuffer(bufferList, newBuf);

    // copy original pkt including shim (sets reclassify bit to 0)
    newShim = msr_pkt_shim(newBuf);
    copy( (char *) shim, (char *) newShim, len + MSR_FPX_SHIM_SZ);
    msr_shim_set_ovin(newShim, msr_vin_make(i, 0));	// set VOQ in shim to i
  }
}

//
// Handle a control message.
//
// The message is simply a list of integers. The first integer defines
// a message type. The remaining values can be used to specify parameters.
// A response can be returned by over-writing the values in the message buffer.
// The length argument must be updated correctly, to indicate the size of the
// response. Length of message and response limited to 11 integer values.
//
// multicast defines three message types
//
//	1. Return destVec
//	2. Add VOQ to destVec
//	3. Remove VOQ from destVec
//

int multicast_handle_msg(
	struct rp_instance *this,	// pointer to instance structure
	void *buf,			// buffer containing integer values
					// buf[0]=instance id
					// buf[1]=command type
					// buf[2]=first parameter value
					// buf[3]=second parameter value ...
	u_int8_t flags,			// ignore
	u_int8_t seq,			// sequence number of message
	u_int8_t *len			// number of values in buf
) {
	struct multicast_instance *inst = (struct multicast_instance *) this;
	u_int32_t *vals = (u_int32_t *) buf;
	u_int32_t id  = (u_int32_t) ntohl(vals[0]);
	u_int32_t typ = (u_int32_t) ntohl(vals[1]);
	int i;

	if (typ == 1) { // Return destVec
		vals[0] = (u_int32_t) htonl(inst->destVec);
		*len = 1*sizeof(u_int32_t);
	} else if (typ == 2) { // add VOQ
		i = ntohl(vals[2]);
		if (i >= 0 && i < 8) inst->destVec |= 1 << i;
		*len = 0*sizeof(u_int32_t);
	} else if (typ == 3) { // remove VOQ
		i = ntohl(vals[2]);
		if (i >= 0 && i < 8) inst->destVec &= ~(1 << i);
		*len = 0*sizeof(u_int32_t);
	}

	return 0;
}

//
// Free instance structure.
//
void multicast_free_instance(struct rp_instance *this) {
	// perform any cleanup of per-instance data structures that
	// may be required.
}

//
// Bind/unbind plugin instance to/from a filter
//
void multicast_bind_instance(struct rp_instance *this) {
	// perform any initialization that should be done whenever the
	// plugin is bound to a filter
}

void multicast_unbind_instance(struct rp_instance *this) {
	// perform any cleanup that should be done whenever the
	// plugin is unbound a filter

}

//
// The remaining code is standard stuff. The only thing that you
// typically need to change is the various places where the
// plugin name (multicast) appears in function and type names.
//

//
// Initialize the class. 
//
void multicast_init_class() {
	multicast_class.classid = multicast_ID;
	multicast_class.itype = RP_INTERFACE_TYPE_HLIST;
	multicast_class.create_instance = multicast_create_instance;
	return;
}

//
// Return class structure
//
struct rp_class * multicast_get_class() {
	return &multicast_class;
}

//
// External kernel module entry point.
//
// It is called each time the class is loaded or unloaded.
// The stat information is not needed here, so we will 
// leave it lkm_nofunc().
//

int multicast(struct lkm_table *lkmtp, int cmd, int ver,
		struct kernel_plugin_fct_struct *fctPtr) {
	// 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_PLUGIN_DISPATCH(lkmtp, cmd, ver, multicast_load,
			multicast_unload, PLUGIN_LKM_NOFUNC_FCT);
}

//
// Load class. This function is called each time the class is loaded
//
int multicast_load(struct lkm_table *lkmtp, int cmd) {
	int err;

	if (PLUGIN_LKM_EXISTS_FCT(lkmtp)) { // avoid loading twice
		return (EEXIST);
	}
	multicast_init_class();

	err = PLUGIN_PCU_REGISTER_CLASS_FCT(multicast_get_class());
	if (err != RP_OK) {
		return -1;
	}

	return 0;
}

//
// Unload class. This function is called each time the class is unloaded.
// Remove all existing instances and then remove class.
//
int multicast_unload(struct lkm_table *lkmtp, int cmd) {
	struct rp_class *rpclass;
	u_int32_t cid;

	rpclass = multicast_get_class();
	cid = rpclass->classid;

	if (PLUGIN_PCU_FREE_ALL_INSTANCES_FCT(rpclass) != RP_OK) {
		return -1;
	}

	return 0;
}
