/*
 *
 * Real-Time and Multimedia Systems Laboratory
 * Copyright (c) 2000 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Real-Time and Multimedia Systems Laboratory
 *  Attn: Prof. Raj Rajkumar
 *  Electrical and Computer Engineering, and Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 *  or via email to raj@ece.cmu.edu
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 *
 *
 */

/* $Author: dionisio $	 
 * $Id: socketfilter.c,v 1.1 2001/05/14 18:13:49 dionisio Exp $
 *
 * socketfilter.c
 * 
 * Carnegie Mellon University
 * miyos@cs.cmu.edu
 *
 */

#define __NO_VERSION__

/*
#define SOCKDEBUG 1
#define SOCKDEBUG2 1 
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/smp_lock.h>



#include <sys/syscall.h>
#include <linux/sched.h>  
#include <asm/uaccess.h>

#include <rk/rk_linux.h>
#include <rk/rk_error.h>
#include <rk/rk.h>

#include <linux/skbuff.h>
#include <linux/ip.h>
#include <net/sock.h>

#include "socketfilter.h"
#include "rclmodule.h"

//#define PCP_DEBUG_INH 1


#define PRINT_TSK_RS_LIST(tsk) \
{\
 struct list_head *head;\
 struct list_head *ptr;\
 struct resource_set_list *rs_list;\
 if (tsk->rs_list.resource_set_list.next != NULL) {\
    head = &((&tsk->rs_list)->resource_set_list); \
    for (ptr = head->next; ptr != head; ptr = ptr->next) { \
	rs_list = \
	    list_entry(ptr, struct resource_set_list, resource_set_list);\
         printk("rs(%p)\n", rs_list->rk_resource_set);\
    }\
 } \
}\


/*
 * RCL configuration for communication
 * 
 * Temporarily hard coded
 *     Currently, I only treat one IPC connection as one resource principal.
 *     TODO: rather than specifiyng the program-name, I should specify
 *           resource principal ID.
 */
static struct sock_access_control sock_acl[SOCK_ACL_LEN] = {
  /* server,     client, s_pid,   r_pid */

  {RCL0,   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/server-select",
   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/client-select",  -1,     -1},

  {RCL0,   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/server", 
   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/client2",  -1,     -1},

  {RCL0,   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/server",  
   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/client3",  -1,     -1},

  {RCL0,   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/server", 
   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/client4",  -1,     -1},
  
  {RCL0,   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/server", 
   "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/client5",  -1,     -1}
};



/* Argument list sizes for sys_socketcall */
#define AL(x) ((x) * sizeof(unsigned long))
static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
				AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
				AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
#undef AL



/*
 *	Display an IP address in readable format. 
 */
char *in_ntoa(__u32 in)
{
	static char buff[18];
	char *p;

	p = (char *) &in;
	sprintf(buff, "%d.%d.%d.%d",
		(p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
	return(buff);
}


/*
 *	Convert an ASCII string to binary IP. 
 */
__u32 in_aton(const char *str)
{
	unsigned long l;
	unsigned int val;
	int i;

	l = 0;
	for (i = 0; i < 4; i++) 
	{
		l <<= 8;
		if (*str != '\0') 
		{
			val = 0;
			while (*str != '\0' && *str != '.') 
			{
				val *= 10;
				val += *str - '0';
				str++;
			}
			l |= val;
			if (*str != '\0') 
				str++;
		}
	}
	return(htonl(l));
}




/***********************************************************************
 * PF_UNIX, SOCK_STREAM
 ***********************************************************************/
/*********************************************************************
 * replaces sock->ops->release
 * called when connection closed     
 *********************************************************************/
int my_us_release(struct socket *sock, struct socket *peer){
#ifdef SOCKDEBUG2
  printk("my_release: *socket=%x sock=%x, sock->pair=%x\n", 
	 sock, sock->sk, sock->sk->pair);
#endif
  sock_dequeue(sock_search_queue(sock->sk));
  return us_release(sock, peer);
}



/*************************************************************
 * replaces sock->ops->sendmsg
 *************************************************************/
int my_us_sendmsg(struct socket *sock, struct msghdr *m, 
		  int total_len, struct scm_cookie *scm){
 
  //#ifdef I_THIK_INHERITANCE_ONLY_AT_RECEIVE_IS_OK
  struct sockentry          *serversock_en;
  struct task_struct        *server;  
  struct resource_principal *rpal, *currpal, *rpal_org;
  struct rs_proc_list       *proc_list;
  static int targetted=0;
  
  rpal = currpal = NULL;
  proc_list = NULL;


  serversock_en = sock_search_queue(sock->sk->pair);
  
  if(serversock_en == NULL)
    {
#ifdef PCP_DEBUG
      printk("sockfilter:sendmsg no queued pair current->pid(%d)\n", current->pid);
#endif PCP_DEBUG
      goto out;
    }

  
#ifdef SOCKDEBUG2
  printk("SENDMSG ");
  //printk("*socket=%x sock=%x, sock->pair=%x\n", sock, sock->sk, sock->sk->pair);
  printk("[%d] -> [%d]\n", current->pid,  serversock_en->pid);
#endif

  /*
   *     1) need to take reserve off when other communication take place to 
   *         this server (not for send mesg)
   *           eg)  server <-- cli (reserved)
   *                server <-- cli (non reserved) <- do not want to take reserve off.
   *     2) sometimes reserve is attached to server when it isn't supposed to (bug?)
   */

  if((server = find_task_by_pid(serversock_en->pid)) == NULL) {
    //#ifdef SOCKDEBUG2
    printk("my_sendmsg: ERROR: find_task_by_pid == NULL pid(%d)\n", serversock_en->pid);
    //#endif
    goto out;
  }

  /* Dio: PCP un-inheritance */
  {
    rk_resource_set_t pcp_rs;
#ifdef PCP_DEBUG
    printk("KERNEL FILTER: sendmsg: ceiling_rs_from_pid(%d,%d)\n", current->pid, serversock_en->pid);
#endif
    pcp_rs = ceiling_rs_from_pid(current->pid, serversock_en->pid);

    if (pcp_rs != NULL)
      {
	/* Undo the inheritance */
	targetted = serversock_en->pid;
#ifdef PCP_DEBUG
	printk("before detach\n");
	PRINT_TSK_RS_LIST(current);
#endif
	__rk_resource_set_detach_process(pcp_rs, current);   	

#ifdef PCP_DEBUG
	printk("after detach\n");
	PRINT_TSK_RS_LIST(current);
#endif

#ifdef  PCP_DEBUG_INH
	printk("sockfilter: sendmsg: uninheriting rs(%p), srv(%d), clt(%d)\n",pcp_rs, current->pid, serversock_en->pid);
#endif
	/* overwrite resource principal rs propagation */
      }
    else
#ifdef PCP_DEBUG
      {
	if (targetted == current->pid)
	  {
	    printk("socketfilter:sendmsg: searching for srv(%d), clt(%d)\n", serversock_en->pid, current->pid);
	  }
	pcp_rs = ceiling_rs_from_pid(serversock_en->pid, current->pid);
	if (pcp_rs != NULL)
	  {
	    printk("sockfilter: sendmsg: FOUND OPPOSIT DIRECTION rs(%p), clt(%d), srv(%d)\n",
		   pcp_rs, serversock_en->pid, current->pid);
	  }
	printk("ceiling_rs_from_pid(%d, %d)\n", current->pid, serversock_en->pid);
      }
#endif

    /* inherint at sending */
    pcp_rs = ceiling_rs_from_pid(serversock_en->pid, current->pid);

    if (pcp_rs != NULL)
      {

	if (__rk_resource_set_is_process_attached(pcp_rs, server) == 0)
	  {
#ifdef PCP_DEBUG
	    printk("sockfilter: inheriting at sending rs(%p) srv(%d), clt(%d)\n",pcp_rs, serversock_en->pid, current->pid);
	    printk("socketfilter: attaching rs\n");

	    printk("before attaching\n");
	    PRINT_TSK_RS_LIST(server);
#endif
	    resource_set_attach_process(pcp_rs, server);	
#ifdef PCP_DEBUG
	    printk("after attachment\n");
	    PRINT_TSK_RS_LIST(server);
#endif
	  }
      }
    goto out;
  }

  rpal    = search_rpal_from_pid(current->pid);      
  currpal = search_rpal_from_pid(serversock_en->pid);

  if(currpal == NULL || rpal == NULL){      //sanity check
    //printk("currpal or rpal == NULL\n");
    goto out;
  }

  /* if client has reserves */
  if(current->rk_resource_set && inherit_sock_acl(serversock_en->pid, current->pid)){
#ifdef SOCKDEBUG
    printk("    INHERIT: [%d] <--- [%d, rset=%x]\n", 
	   serversock_en->pid, current->pid, (int)current->rk_resource_set);
#endif
    if(server->rk_resource_set && server->rk_resource_set != current->rk_resource_set)
      __rk_resource_set_detach_process(server->rk_resource_set, server);   
    if(server->rk_resource_set != current->rk_resource_set)
      if(sys_resource_set_attach_process(current->rk_resource_set, serversock_en->pid) != RK_SUCCESS ){
	;
#ifdef SOCKDEBUG
	printk("INHERIT: attach process failed\n");
#endif
      }

    serversock_en->rset = current->rk_resource_set;
      
    /* put proc_list into appropriate resource principal */
    if(currpal != NULL){
      proc_list = rs_proc_list_search_pid(&currpal->rs_proc_list, serversock_en->pid);
      if(proc_list && currpal != rpal ){ 
	list_del(&proc_list->rs_proc_list);	
	list_add(&proc_list->rs_proc_list, &rpal->rs_proc_list);
      }
    }
#if 0 
    if(!proc_list){
      if((proc_list = rs_proc_list_search_pid(&rpal->rs_proc_list, serversock_en->pid)) == NULL){
	proc_list = create_new_proc_entry(serversock_en->pid, server);
	list_add(&proc_list->rs_proc_list, &rpal->rs_proc_list);
      }
    }
#endif
  }

  // if not to inherit, go back to original setting
  else{
    rpal_org =  search_rpal_org_from_pid(serversock_en->pid);
    if(rpal_org == NULL) //sanity check
      goto out;
    if(currpal != rpal_org){
      proc_list = rs_proc_list_search_pid(&currpal->rs_proc_list, serversock_en->pid);
      list_del(&proc_list->rs_proc_list);
      list_add(&proc_list->rs_proc_list, &rpal_org->rs_proc_list);
    }
    if(rpal_org->rset && rpal_org->rset != serversock_en->rset){
      __rk_resource_set_detach_process(server->rk_resource_set, server);   
      sys_resource_set_attach_process(rpal_org->rset, serversock_en->pid);
    }
    serversock_en->rset = rpal_org->rset;
  }
 out:
  //#endif  
  return us_sendmsg(sock, m, total_len, scm);
}









/*************************************************************************
 * replaces sock->ops->recvmsg
 *************************************************************************/
int my_us_recvmsg(struct socket *sock, struct msghdr *m, 
		  int total_len, int flags, struct scm_cookie *scm){
  
  struct sockentry *clientsock_en, *serversock_en;
  struct task_struct *client;  
  struct resource_principal *rpal, *currpal, *rpal_org;
  struct rs_proc_list       *proc_list;
  int retval;
  rk_resource_set_t pcp_rs;
  
  clientsock_en = sock_search_queue(sock->sk->pair);
  serversock_en = sock_search_queue(sock->sk);


#ifdef SOCKDEBUG2
  printk("RECVMSG ");
  printk("*socket=%x sock=%x, sock->pair=%x\n", 
	 (int)sock, (int)sock->sk, (int)sock->sk->pair);
#endif
  if(clientsock_en == NULL){
    //printk("ERROR rcvmsg: clientsock_en == NULL\n");
    goto out;
  }
  if(serversock_en == NULL){
    //printk("ERROR rcvmsg: serversock_en == NULL\n");
    goto out;
  }
  else{
    ;
    //printk("[%d] <- [%d]\n", current->pid,  clientsock_en->pid);
  }

  if(((client = find_task_by_pid(clientsock_en->pid))==NULL)){
    //printk("my_rcvsg: ERROR: find_task_by_pid == NULL\n");
     goto out;
  }  
  
#ifdef SOCKDEBUG
  printk(" client->rk_resource_set = %x\n", (int)client->rk_resource_set);
  printk("     [%d] <--> [%d]\n", current->pid, clientsock_en->pid);
#endif

  /* Dio : This is the PCP inheritance point
   */

  {
    /* current (client) is blocked waiting for a response from 
     * the server (clientsock_en) so we propoagate the special
     * server attache pcp reserve.
     */

#ifdef PCP_DEBUG
    printk("KERNEL FILTER: recvmsg: ceiling_rs_from_pid(%d,%d)\n", clientsock_en->pid, current->pid);
#endif 

    pcp_rs= ceiling_rs_from_pid(clientsock_en->pid, current->pid);

    if (pcp_rs != NULL)
      {
	/* This call verifies that we don't have double insertion */
	if (__rk_resource_set_is_process_attached(pcp_rs, client) == 0)
	  {
	    //resource_set_attach_process(pcp_rs, client);
	    //printk("sockfilter: inheriting rs(%p), srv(%d), clt(%d)\n",pcp_rs, clientsock_en->pid, current->pid);
	  }
	/* for now we overwrite the resource principal rs propagation */
	goto out;
      }
    else
      {

#ifdef PCP_DEBUG_INH
	pcp_rs = ceiling_rs_from_pid(current->pid, clientsock_en->pid);
	if (pcp_rs != NULL)
	  {
	    if (__rk_resource_set_is_process_attached(pcp_rs, current) == 0)
	      {
		/*
		printk("sockfilter: FOUND inherit opposite direction rs(%p), clt(%d), srv(%d)\n", 
		       pcp_rs, current->pid, clientsock_en->pid);
		*/
		//resource_set_attach_process(pcp_rs, current);
	      }
	  }
#endif
#ifdef PCP_DEBUG
	printk("KERNEL FILTER: recvmsg: ceiling not found\n");
#endif
      }
    goto out;
  }

  currpal    = search_rpal_from_pid(current->pid);   // server
  rpal = search_rpal_from_pid(clientsock_en->pid);   // client

  if(currpal == NULL || rpal == NULL){   //sanity check
    //printk("currpal or rpal == NULL\n");
    goto out;
  }
  
  if(client->rk_resource_set && inherit_sock_acl(current->pid, clientsock_en->pid)){
#ifdef SOCKDEBUG
    printk("    INHERIT: [%d] <--- [%d, rset=%x]\n", 
	   current->pid, clientsock_en->pid, (int)clientsock_en->rset);
#endif
      if(current->rk_resource_set && current->rk_resource_set != clientsock_en->rset)
	__rk_resource_set_detach_process(current->rk_resource_set, current);   
      if(current->rk_resource_set != clientsock_en->rset)
	if(sys_resource_set_attach_process(clientsock_en->rset, current->pid ) != RK_SUCCESS){
	  ;
#ifdef SOCKDEBUG
	  printk("INHERIT: rcvmsg: attach process failed\n");
#endif
	}
      serversock_en->rset = clientsock_en->rset;      

      /* put proc_list into appropriate resource principal */
      if(currpal != NULL){
	proc_list = rs_proc_list_search_pid(&currpal->rs_proc_list, current->pid);
	if(proc_list && currpal != rpal ){  
	  list_del(&proc_list->rs_proc_list);
	  list_add(&proc_list->rs_proc_list, &rpal->rs_proc_list);
	}
      }
  }

  // if not to inherit, go back to original setting
  else{
    rpal_org =  search_rpal_org_from_pid(current->pid);
    if(rpal_org == NULL){
      //printk("rpal_org == NULL\n");
      goto out; //sanity check
    }
    if(currpal != rpal_org){
      proc_list = rs_proc_list_search_pid(&currpal->rs_proc_list, current->pid);
      list_del(&proc_list->rs_proc_list);
      list_add(&proc_list->rs_proc_list, &rpal_org->rs_proc_list);
    }
    if(rpal_org->rset == NULL_RESOURCE_SET)
      __rk_resource_set_detach_process(current->rk_resource_set, current);   
    else if(rpal_org->rset != current->rk_resource_set){
      __rk_resource_set_detach_process(current->rk_resource_set, current);   
      sys_resource_set_attach_process(rpal_org->rset, current->pid);
    }
    serversock_en->rset = rpal_org->rset;
  }
out:
  retval =  us_recvmsg(sock, m, total_len, flags, scm);

#if 0
  if (clientsock_en != NULL)
    {
      pcp_rs = ceiling_rs_from_pid(current->pid, clientsock_en->pid);
      if (pcp_rs != NULL)
	{
	  if (__rk_resource_set_is_process_attached(pcp_rs, current) == 0)
	    {
	      printk("sockfilter: FOUND inherit opposite direction rs(%p), clt(%d), srv(%d)\n", 
		     pcp_rs, current->pid, clientsock_en->pid);
	      resource_set_attach_process(pcp_rs, current);
	    }
	}
    }
#endif
  
  return retval;
}







/***********************************************************************
 * PF_INET, SOCK_STREAM
 ***********************************************************************/

int my_is_release(struct socket *sock, struct socket *peer){
  return is_release(sock, peer);
}


int my_is_sendmsg(struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm){
  char *addr; 

  regulate_load(); //is this the right place?
  
#ifdef SOCKDEBUG2
  printk("is_sendmsg\n");
  printk("pid = %d ", current->pid);
  addr = in_ntoa(sock->sk->daddr);
  printk("foreign addr = %s\n", addr);
  addr = in_ntoa(sock->sk->rcv_saddr);
  printk("local addr = %s\n", addr);
#endif  

  return is_sendmsg(sock, m, total_len, scm);
}

int my_is_recvmsg(struct socket *sock, struct msghdr *m, int total_len, 
		  int flags, struct scm_cookie *scm){
  struct sock               *sk;
  struct resource_control   *rcl = NULL; 
  struct resource_principal *rpal, *currpal, *rpal_org;
  struct rs_proc_list       *proc_list;
  int ret;

  
  rpal = currpal = NULL;
  proc_list      = NULL;
  
#ifdef SOCKDEBUG2
  printk("is_recvmsg\n");
#endif



  // I need to sync this and the receipt....9.10.00 miyos
  if((sk = sock->sk) != NULL){
    char *addr; 
    addr = in_ntoa(sk->daddr);

#ifdef SOCKDEBUG
    printk("pid = %d ", current->pid);
    printk("src = %s ", addr);
#endif
    if((rcl = search_rcl(0,0,NULL,addr))){
      rpal = rcl->rpal;
#ifdef SOCKDEBUG
      if(rpal == NULL)
	printk("ERROR: rpal==NULL!!! ");
      printk("rcl->saddrip = [%s] ", rcl->saddrip);
#endif
      currpal = search_rpal_from_pid(current->pid);

      if(currpal == NULL || rpal == NULL){ //sanity check
	//printk("currpal or rpal  == NULL");
	goto out; 
      }

      //deal with proc_list here?
      if(currpal && rpal != currpal){
	proc_list = rs_proc_list_search_pid(&currpal->rs_proc_list, current->pid);
#ifdef SOCKDEBUG
	if(proc_list==NULL)
	  printk("my_is_recvmsg ERROR: proc_list == NULL!\n");
#endif
	if(proc_list){
	  list_del(&proc_list->rs_proc_list);	
	  list_add(&proc_list->rs_proc_list, &rpal->rs_proc_list);
	}
      }
      
      if(rpal->use_reserv){
	if(rpal->rset == NULL_RESOURCE_SET)
	  rpal->rset = rpal->rsetorg = create_rset_from_rcl(rcl);
	if(current->rk_resource_set && current->rk_resource_set != rpal->rset){
	  __rk_resource_set_detach_process(current->rk_resource_set, current);
	}
	if(current->rk_resource_set != rpal->rset)
	  if(( sys_resource_set_attach_process(rpal->rset, current->pid)) != RK_SUCCESS){
	    printk("register_packet: failed at resource set attach\n");
	  }
#ifdef SOCKDEBUG
	printk("attached to resource set %x ", (int)rpal->rset);
#endif
      } // if(rpal->use_reserv) 
      else if(current->rk_resource_set){
	__rk_resource_set_detach_process(current->rk_resource_set, current);
#ifdef SOCKDEBUG
	printk("detach resource set %x ", (int)current->rk_resource_set);
#endif
      }
    } //    if((rcl = search_rcl(0,0,NULL,addr))){

    // go back to original resource principal.
    else{
      rpal_org =  search_rpal_org_from_pid(current->pid);
      rpal     =  search_rpal_from_pid(current->pid);

      if(rpal_org == NULL || rpal == NULL){
	//printk("rpal_org or rpal == NULL\n");
	goto out;
      }
      
      if(rpal != rpal_org){
	proc_list = rs_proc_list_search_pid(&rpal->rs_proc_list, current->pid);
	list_del(&proc_list->rs_proc_list);
	list_add(&proc_list->rs_proc_list, &rpal_org->rs_proc_list);
      }
      if(rpal_org->rset == NULL_RESOURCE_SET && current->rk_resource_set){
	__rk_resource_set_detach_process(current->rk_resource_set, current);   
#ifdef SOCKDEBUG
	printk("detach resource set %x ", (int)current->rk_resource_set);
#endif
      }
      else if(rpal_org->rset != current->rk_resource_set){
	__rk_resource_set_detach_process(current->rk_resource_set, current);   
	sys_resource_set_attach_process(rpal_org->rset, current->pid);
#ifdef SOCKDEBUG
	printk("resource set %x to original resource set %x ", 
	       (int)current->rk_resource_set, (int)rpal_org->rset);
#endif
      }
    }
    //addr = in_ntoa(skb->nh.iph->daddr);
#ifdef SOCKDEBUG
    printk("exiting... ");
    addr = in_ntoa(sk->rcv_saddr);
    printk("dest = [%s] \n", addr);
#endif
  }
  
out:
  ret =   is_recvmsg(sock, m, total_len, flags, scm); //inet_recvmsg()
  regulate_load();//miyos  
  return  ret;

}







/***********************************************************************
 * PF_INET, SOCK_DGRAM
 ***********************************************************************/
int my_id_release(struct socket *sock, struct socket *peer){
  return id_release(sock, peer);
}

int my_id_sendmsg(struct socket *sock, struct msghdr *m, 
		  int total_len, struct scm_cookie *scm){
  return id_sendmsg(sock, m, total_len, scm);
}

int my_id_recvmsg(struct socket *sock, struct msghdr *m, 
		  int total_len, int flags, struct scm_cookie *scm){
  return id_recvmsg(sock, m, total_len, flags, scm);
}




static struct proto_ops my_unix_stream_ops, unix_stream_ops, *unix_stream_addr;
static struct proto_ops my_inet_stream_ops, inet_stream_ops, *inet_stream_addr;
static struct proto_ops my_inet_dgram_ops,  inet_dgram_ops,  *inet_dgram_addr;

static int unix_stream_init = 0;  
static int inet_stream_init = 0;  
static int inet_dgram_init = 0;  

void cleanup_socket(void){
  if(unix_stream_init)
    *unix_stream_addr  = unix_stream_ops;
  if(inet_stream_init)
    *inet_stream_addr  = inet_stream_ops;
  if(inet_dgram_init)
    *inet_dgram_addr   = inet_dgram_ops;
}



/*************************************************************************
 *  main socket filter function
 ******************** *****************************************************/
asmlinkage int sys_socketcall_trapper(int call, unsigned long *args)
{
  	unsigned long a[6];
	unsigned long a0,a1;
	int uid;
	int tmp=0, err = 0;
	struct sockentry *sock_en, *othersock_en;	
	struct socket *sock, *othersock;
	
	if(call<1||call>SYS_RECVMSG)
		return -EINVAL;
	
	/* copy_from_user should be SMP safe. */
	if (copy_from_user(a, args, nargs[call]))
	  return -EFAULT;
	a0=a[0];
	a1=a[1];
	othersock = NULL;
	
	/*
	 * original system call
	 */
	err =  sys_socketcall(call, args);

	switch(call) 
	  {
	  case SYS_SOCKET:
	    sock = sockfd_lookup(err, &tmp);
	    fput(sock->file);

	    if(sock->ops == NULL){
	      //printk("sock->ops == NULL!\n");
	      break;
	    }
	    
	    /*************************************************************
	      net/ipv4/af_inet.c
	      a0: family  PF_UNIX (local), PF_INET (internet)
	      a1: type    SOCK_STREAM (tcp), SOCK_DGRAM (udp), 
                          SOCK_SEQPACKET, SOC_RAW, SOCK_RDM, SOCK_WEB...
	      a[2]: protocol

             SOCK_STREAM/IPPROTO_TCP =>  inet_stream_ops;
             SOCK_DGRAM/IPPROTO_UDP  =>  inet_dgram_ops;
	    *************************************************************/
#ifdef SOCKDEBUG2
	    printk("family:%d type:%d ", (int)a0, (int)a1);
#endif
	    /*
	     * PF_UNIX, SOCK_STREAM
	     */
	    if(a0 == PF_UNIX && a1 == SOCK_STREAM){
	      if(unix_stream_init == 0 && sock->ops != NULL){
		//check: sock->ops->{sock_read, sock_write} 
		us_release         = sock->ops->release;
		us_sendmsg         = sock->ops->sendmsg;
		us_recvmsg         = sock->ops->recvmsg;

		unix_stream_ops    = *(sock->ops);
		unix_stream_addr   = sock->ops; 
		my_unix_stream_ops = *(sock->ops);
		my_unix_stream_ops.release = my_us_release;
		my_unix_stream_ops.sendmsg = my_us_sendmsg;
		my_unix_stream_ops.recvmsg = my_us_recvmsg;
		*(sock->ops)  = my_unix_stream_ops;
		unix_stream_init = 1;
	      }
#ifdef SOCKDEBUG2
	      printk("BSD  ");
#endif
	      if(sock->ops != NULL){
		sock->ops = &my_unix_stream_ops; 
		sock_en = sockentry_alloc();
		sock_en->sock = sock->sk;
		sock_en->other = NULL;
		sock_en->pid = current->pid;
		sock_en->rset = NULL;
		sock_enqueue(sock_en);
	      }
	    }
	    
	    /*
	     * PF_INET, SOCK_STREAM
	     */
	    if(a0 == PF_INET && a1 == SOCK_STREAM){
	      if(inet_stream_init == 0 && sock->ops != NULL){
		is_release         = sock->ops->release;
		is_sendmsg         = sock->ops->sendmsg;
		is_recvmsg         = sock->ops->recvmsg;

		inet_stream_ops    = *(sock->ops);
		inet_stream_addr   =   sock->ops;
		my_inet_stream_ops = *(sock->ops);
		my_inet_stream_ops.release = my_is_release;
		my_inet_stream_ops.sendmsg = my_is_sendmsg;
		my_inet_stream_ops.recvmsg = my_is_recvmsg;
		*(sock->ops) = my_inet_stream_ops;
		inet_stream_init = 1;
	      }
#ifdef SOCKDEBUG2
	      printk("TCPIP  ");
#endif
	      //if(sock->ops != NULL)
	      //sock->ops = &my_inet_stream_ops;
	    }

	    /*
	     * PF_INET, SOCK_DGRAM
	     */
	    if(a0 == PF_INET && a1 == SOCK_DGRAM){
	      if(inet_dgram_init == 0 && sock->ops != NULL){
		id_release         = sock->ops->release;
		id_sendmsg         = sock->ops->sendmsg;
		id_recvmsg         = sock->ops->recvmsg;

		inet_dgram_ops    = *(sock->ops);
		inet_dgram_addr   =   sock->ops;
		my_inet_dgram_ops = *(sock->ops);
		my_inet_dgram_ops.release = my_id_release;
		my_inet_dgram_ops.sendmsg = my_id_sendmsg;
		my_inet_dgram_ops.recvmsg = my_id_recvmsg;
		*(sock->ops)  = my_inet_dgram_ops;
		inet_dgram_init = 1;
	      }
#ifdef SOCKDEBUG2
	      printk("UDPIP  ");
#endif
	    }
#ifdef SOCKDEBUG2
	    printk("SYS_SOCKET: ");
	    printk("*socket=%x sock=%x, sock->pair=%x\n", (int)sock, (int)sock->sk, (int)sock->sk->pair);
#endif
	    
	    break;


	  case SYS_BIND:
#ifdef SOCKDEBUG2
	    printk("SYS_BIND:\n");
#endif
	    break;

	  case SYS_CONNECT:
#ifdef SOCKDEBUG2
	    printk("SYS_CONNECT: \n");
#endif
	    /*
	    if(err == 0){
	      //
	      // to use 'sockfd_lookup', 
	      // I had to export this at linux/kernel/ksysm.c
	      //
	      sock = sockfd_lookup(a0, &tmp); 
	      //
	      // kernel deadlocks, if I don't sockfd_put. (this is not
	      // exported, so use fput instead.)  
	      //
	      fput(sock->file); 
	    }
	    */
	    break;
	    

	  case SYS_LISTEN:
#ifdef SOCKDEBUG2
	    printk("SYS_LISTEN:\n");
#endif
	    break;


	  case SYS_ACCEPT:
	    sock = sockfd_lookup(err, &tmp);
	    fput(sock->file); 
#ifdef SOCKDEBUG2
	    printk("SYS_ACCEPT: ");
	    printk("SYS_ACCEPT NEWSOCK: ");
	    printk("socket=%x, sock=%x, sock->pair=%x] \n", (int)sock, (int)sock->sk, (int)sock->sk->pair);
#endif
	    
	    othersock_en = sock_search_queue(sock->sk->pair);
	    if(othersock_en == NULL){
#ifdef SOCKDEBUG
	      printk("ERROR: SYS_ACCEPT: othersock_en == NULL\n");
#endif
	      break;
	    }
	    sock_en = sockentry_alloc();
	    sock_en->sock = sock->sk;
	    sock_en->other = othersock_en;
	    sock_en->pid = current->pid;
	    sock_en->rset = NULL;
	    sock_enqueue(sock_en);
	   
#ifdef PCP_DEBUG
	    printk("KERNEL FILTER: accept: enqueueing socket sock(%p)\n",sock->sk);
#endif

	    othersock_en->other = sock_en;
#ifdef SOCKDEBUG2
	    printk("ACCEPT: [%d]<-->[%d]\n", current->pid, othersock_en->pid);
#endif
	    {
	      struct resource_principal *s_rpal, *c_rpal;

	      uid = getuid_call();
	      s_rpal = search_rpal_from_pid(current->pid);
	      c_rpal = search_rpal_from_pid(othersock_en->pid);

#ifdef SOCKDEBUG2
	      if(s_rpal != NULL)
		printk("   [%s]", s_rpal->rcl->progname);
	      else
		printk("s_utats = NULL");
	      if(c_rpal != NULL)
		printk("<-->[%s]\n", c_rpal->rcl->progname);
	      else
		printk("r_utats=NULL\n");
#endif
	      if(s_rpal !=NULL && c_rpal != NULL){
		register_sock_acl(s_rpal->rcl->progname, current->pid, 
				  c_rpal->rcl->progname, othersock_en->pid);
	      }
	    }
	    break;	    

	    
	  case SYS_GETSOCKNAME:
#ifdef SOCKDEBUG2
	    printk("SYS_GETSOCKNAME:\n");
#endif
	    break;



	  case SYS_GETPEERNAME:
#ifdef SOCKDEBUG2
	    printk("SYS_GETPEERNAME:\n");
#endif
	    break;



	  case SYS_SOCKETPAIR:
#ifdef SOCKDEBUG2
	    printk("SYS_SOCKETPAIR:\n");
#endif
	    break;



	  case SYS_SEND:
#ifdef SOCKDEBUG2
	    printk("SYS_SEND:\n");
#endif
	    break;



	  case SYS_SENDTO:
#ifdef SOCKDEBUG2
	    printk("SYS_SENDTO:\n");
#endif
	    break;



	  case SYS_RECV:
#ifdef SOCKDEBUG2
	    printk("SYS_RECV:\n");
#endif
	    break;


	  case SYS_RECVFROM:
#ifdef SOCKDEBUG2
	    printk("SYS_RECVFROM:\n");
#endif
	    break;



	  case SYS_SHUTDOWN:
	    sock = sockfd_lookup(a0, &tmp);
	    fput(sock->file);
#ifdef SOCKDEBUG2
	    printk("SYS_SHUTDOWN:\n");
	    printk(" *socket=%x sock=%x, sock->pair=%x\n", (int)sock, (int)sock->sk, (int)sock->sk->pair);
#endif
	    sock_dequeue(sock_search_queue(sock->sk));
#ifdef SOCDEBUG2
	    printk("SOCKDEQUE\n");
#endif
	    break;



	  case SYS_SETSOCKOPT:
#ifdef SOCKDEBUG2
	    printk("SYS_SETSOCKOPT:\n");
#endif
	    break;


	  case SYS_GETSOCKOPT:
#ifdef SOCKDEBUG2
	    printk("SYS_GETSOCKOPT:\n");
#endif
	    break;


	  case SYS_SENDMSG:
#ifdef SOCKDEBUG2
	    printk("SYS_SENDMSG:\n");
#endif
	    break;


	  case SYS_RECVMSG:
#ifdef SOCKDEBUG2
	    printk("SYS_RECVMSG:\n");
#endif
	    break;


	  default:
	    break;
	  }
	

	regulate_load();//miyos

	return err;
}



/*
 * registers the pid for appropriate sock_acl entry.
 * (then, from next time on, one can search using pid for identifing connection)
 *  returns 0 on sucess, -1 otherwise.
 */
int register_sock_acl(char *server, pid_t s_pid, char *client, pid_t c_pid){
  int i;
  for(i=0; i<SOCK_ACL_LEN ; i++){
    if( strcmp( sock_acl[i].srv_progname, server) == 0 && 
	strcmp( sock_acl[i].cli_progname, client) == 0){
      sock_acl[i].srv_pid = s_pid;
      sock_acl[i].cli_pid = c_pid;
      return 0;
    }
  }
  return -1;
}
       

/*
 * if server should inherit reservation from client, return 1
 *  c_pid = -1 is a wild card.
 */
int inherit_sock_acl(pid_t s_pid, pid_t c_pid){
  int i;
  for(i=0; i<SOCK_ACL_LEN; i++){
    if(c_pid == -1){
      if(sock_acl[i].srv_pid == s_pid)
	return 1;
    }
    else if(sock_acl[i].srv_pid == s_pid &&
	    sock_acl[i].cli_pid == c_pid)
      return 1;
  }
  return 0;
}


#include "ipfwc_kernel_headers.h"
#include <linux/in.h>
#include <linux/socket.h>
#include <linux/ip_fw.h>
static int sockfd = -1;
static int init = 0;

static int ipfwc_init()
{
  init = 1;

  sockfd = sys_socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
  printk("sockfd = %d\n", sockfd);
  
  return 0;
  //return ((sockfd = sys_socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) != -1);
}


static int denied_addr[2];

int deny_ip(unsigned int addr){
  int ret;
  struct  ip_fwuser *ipfw;
  struct ip_fwchange new;

  char *ipaddr;

  //if(denied_addr[1] == addr || denied_addr[2] == addr){
  //printk("already denied\n");
  //return 0;
  //}
  ipaddr = in_ntoa(addr);  
  //printk("ipaddr=%s\n",ipaddr);
#if 0
  if(!init)
    ipfwc_init();
#endif

  ipfw = &new.fwc_rule;
  ipfw->ipfw.fw_src.s_addr = addr;
  ipfw->ipfw.fw_dst.s_addr = 0;
  ipfw->ipfw.fw_smsk.s_addr = 0xFFFFFFFF;
  ipfw->ipfw.fw_dmsk.s_addr = 0;
  ipfw->ipfw.fw_mark        = 0;
  ipfw->ipfw.fw_proto       = 0;
  ipfw->ipfw.fw_flg         = 0;
  ipfw->ipfw.fw_invflg      = 0;
  ipfw->ipfw.fw_spts[0]     = 0;
  ipfw->ipfw.fw_spts[1]     = 0xFFFF;
  ipfw->ipfw.fw_dpts[0]     = 0;
  ipfw->ipfw.fw_dpts[1]     = 0xFFFF;
  ipfw->ipfw.fw_redirpt     = 0;  
  ipfw->ipfw.fw_outputsize  = 0;
  bzero(ipfw->ipfw.fw_vianame, sizeof(ipfw->ipfw.fw_vianame));
  ipfw->ipfw.fw_tosand      = 0xFF;  
  ipfw->ipfw.fw_tosxor      = 0x00;
  strcpy(ipfw->label, "DENY"); //DENY?

  memcpy(new.fwc_label, "input", sizeof(new.fwc_label));

  ret = ip_fw_ctl(IP_FW_APPEND, &new, sizeof(new));

  //printk("ret=%d\n", ret);
  //if(denied_addr[1] != addr && denied_addr[2] != addr)
  //denied_addr[1] = addr;
  //else if(denied_addr[1] != addr)
  //denied_addr[2] = addr;
  return ret;
#if 0
  ret = sys_setsockopt(sockfd, IPPROTO_IP, IP_FW_APPEND, (char *)&new, sizeof(new));
  printk("sockfd = %d, ret=%d\n", sockfd, ret);
  return ret;
#endif

}





int accept_ip(unsigned int addr){
  int ret;
  struct  ip_fwuser *ipfw;
  struct ip_fwchange new;
  static int accept_addr = 0;
  char *ipaddr;

  //if(accept_addr == addr){
  //printk("already accepted\n");
  //return 0;
  //}
  ipaddr = in_ntoa(addr);  
  //printk("ipaddr=%s\n",ipaddr);
#if 0
  if(!init)
    ipfwc_init();
#endif

  ipfw = &new.fwc_rule;
  ipfw->ipfw.fw_src.s_addr = addr;
  ipfw->ipfw.fw_dst.s_addr = 0;
  ipfw->ipfw.fw_smsk.s_addr = 0xFFFFFFFF;
  ipfw->ipfw.fw_dmsk.s_addr = 0;
  ipfw->ipfw.fw_mark        = 0;
  ipfw->ipfw.fw_proto       = 0;
  ipfw->ipfw.fw_flg         = 0;
  ipfw->ipfw.fw_invflg      = 0;
  ipfw->ipfw.fw_spts[0]     = 0;
  ipfw->ipfw.fw_spts[1]     = 0xFFFF;
  ipfw->ipfw.fw_dpts[0]     = 0;
  ipfw->ipfw.fw_dpts[1]     = 0xFFFF;
  ipfw->ipfw.fw_redirpt     = 0;  
  ipfw->ipfw.fw_outputsize  = 0;
  bzero(ipfw->ipfw.fw_vianame, sizeof(ipfw->ipfw.fw_vianame));
  ipfw->ipfw.fw_tosand      = 0xFF;  
  ipfw->ipfw.fw_tosxor      = 0x00;
  strcpy(ipfw->label, "DENY");

  memcpy(new.fwc_label, "input", sizeof(new.fwc_label));

  ret = ip_fw_ctl(IP_FW_DELETE, &new, sizeof(new));

  //printk("ret=%d\n", ret);
  //accept_addr = addr;
  return ret;
#if 0
  ret = sys_setsockopt(sockfd, IPPROTO_IP, IP_FW_APPEND, (char *)&new, sizeof(new));
  printk("sockfd = %d, ret=%d\n", sockfd, ret);
  return ret;
#endif

}


/* 
 * regulate_load()
 *    Temporary implementation.
 *    I need to merge this with the official network bandwidth reservation
 *    as soon as it is implemented
 */
void regulate_load(){
  static int   prv1[5],prv2[5], used, period;
  unsigned int        addr1, addr2;
  struct resource_control    *rcl1, *rcl2;
  cpu_reserve_t        cpu1, cpu2;
  rk_resource_set_t   rset1,rset2;
  char                *straddr1, *straddr2;
  static int           count = 0;
  int numprocs1, numprocs2;
  
  static char          denied_addr1 = 0;
  static char          denied_addr2 = 0;
  
  return;      // comment this out if you want to do network bandwidth access control

  straddr1 = "128.2.220.93";
  straddr2 = "128.2.222.155";
  
  addr1 = in_aton(straddr1);  //bauhaus
  addr2 = in_aton(straddr2);  //sharaku
  
  rcl1 = search_rcl(0,0,NULL,straddr1); 
  rcl2 = search_rcl(0,0,NULL,straddr2);

  if(!rcl1||!rcl2)
    return;
  if(!rcl1->rpal->rset||!rcl2->rpal->rset)
    return;
  
  rset1 = rcl1->rpal->rset;
  numprocs1 = rk_resource_set_get_num_procs(rset1); //bauhaus

  rset2 = rcl2->rpal->rset;
  numprocs2 = rk_resource_set_get_num_procs(rset2); //bauhaus
  
  cpu1 = rset1->rs_cpu->rsv_rsv;
  cpu2 = rset2->rs_cpu->rsv_rsv;
  // 99.99% is represented as 9999

  used = cpu1->cpu_period_prev_used_ticks;
  period = cpu1->cpu_period_ticks;
  prv1[0] = (int)(((float)used/(float)period)*100.0);
  

  used = cpu2->cpu_period_prev_used_ticks;
  period = cpu2->cpu_period_ticks;
  prv2[0] = (int)(((float)used/(float)period)*100.0);

  used = cpu2->cpu_period_used_ticks;
  prv2[1] = (int)(((float)used/(float)period)*100.0);


  
  /* I don't want to deny traffic from bauhaus.
  if(prv2[0] < 0 && prv1[0] > 40){
    if(!denied_addr1){
      deny_ip(addr1);
      if(denied_addr2) accept_ip(addr2);
      denied_addr1 = 0x1; 
      denied_addr2 = 0x0; 
      count = 0;
    }
    else
      count++;

    if(count > 1){
      if(denied_addr1){
	accept_ip(addr1);
	denied_addr1 = 0x0;
      }
      count = 0;
    }
  }
  */
  
  /*else if(prv1 < 10 && prv2 > 38){*/
  if(numprocs1 < 15 || numprocs2 > 60){
    if(!denied_addr2){
      deny_ip(addr2); 
      if(denied_addr1) accept_ip(addr1);
      denied_addr2 = 0x1;
      denied_addr1 = 0x0;
      count = 0;
    }
    else
      count++;

    if(count > 30){
      if(denied_addr2){
	accept_ip(addr2);
	denied_addr2 = 0x0;
      }
      count = 0;
    }
  }
  
  
  else if(prv1[0] < 39 && prv2[0] > 10){
    if(!denied_addr2){
      deny_ip(addr2); 
      if(denied_addr1) accept_ip(addr1);
      denied_addr2 = 0x1;
      denied_addr1 = 0x0;
      count = 0;
    }
    else
      count++;

    if(count > 30){
      if(denied_addr2){
	accept_ip(addr2);
	denied_addr2 = 0x0;
      }
      count = 0;
    }
  }


  /*else if(prv1 < 10 && prv2 > 38){*/
  else if(prv2[1] > 17 && prv2[0] > 17){
    if(!denied_addr2){
      deny_ip(addr2); 
      if(denied_addr1) accept_ip(addr1);
      denied_addr2 = 0x1;
      denied_addr1 = 0x0;
      count = 0;
    }
    else
      count++;

    if(count > 50){
      if(denied_addr2){
	accept_ip(addr2);
	denied_addr2 = 0x0;
      }
      count = 0;
    }
  }

  
  else{
    if(prv2[0] == 0 && count > 60){
      if(denied_addr2) accept_ip(addr2);      
    }
    else if(count > 80){
      if(denied_addr1) accept_ip(addr1);
      if(denied_addr2) accept_ip(addr2);
      denied_addr1 = denied_addr2 = 0x0;
      count = 0;
    }
    count++;
  }
}



