/* $Id: setsockopt.c,v 1.6 1996/01/19 18:33:23 jhawk Exp $ */

#include <sys/types.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/protosw.h>
#include <sys/syscall.h>
#include <sys/systm.h>
#include <sys/user.h>

#include <sundev/mbvar.h>

#include <sun/autoconf.h>
#include <sun/vddrv.h>

#include <sys/socket.h>
#include <sys/socketvar.h>

#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcpip.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>

#define tcpcbtotos(TCPCB) (*(int*)((char*)TCPCB+sizeof(struct tcpcb)/sizeof(char)))

/* #define IPTOS_DEBUG */


int (*SAVE_setsockopt)();

/*
 * fake a "system call" entry
 */
struct vdlsys iptos_vd = {
	VDMAGIC_USER,		/* Sys_magic */
	"IP TOS setsockopt",	/* *Sys_name */
	SYS_setsockopt,		/* Sys_num */
	(struct sysent *)0	/* Sys_sysent */
};

iptos_setsockopt () 
{
     struct a {
        int s;
        int level;
	int name;
	caddr_t val;
	int valsize;
      } *uap = (struct a *)u.u_ap;
     struct file *fp;
     int tos;
     
     if ((uap->level == IPPROTO_IP) && (uap->name == IP_TOS)) {
       if ((fp = (struct file *)getsock(uap->s)) == NULL)
	   	   return;
       if (uap->valsize != sizeof(int)) {
	 u.u_error = EINVAL;
	 return;
       }
       if (uap->val) {
	 struct socket *so;
	 struct inpcb *inp;
	 struct tcpcb *tcpcb;
	 
	 if (u.u_error = copyin(uap->val, &tos, sizeof(tos)))
	   return;
#ifdef IPTOS_DEBUG
	 printf("iptos_setsockopt: tos@0x%X\n", &tos);
	 printf("iptos_setsockopt: copyin returns TOS 0x%X\n", tos);
#endif
	 so = (struct socket *)fp->f_data;
#ifdef IPTOS_DEBUG
	 if (so->so_proto)
	   printf("iptos_setsockopt: pr_type=%d pr_protocol=%d\n",
		  so->so_proto->pr_type,
		  so->so_proto->pr_protocol);
#endif
	 if (so->so_proto && so->so_proto->pr_protocol==IPPROTO_TCP) {
#ifdef IPTOS_DEBUG
	   printf("iptos_setsockopt: so is at 0x%x\n", so);
#endif
	   inp = sotoinpcb(so);
#ifdef IPTOS_DEBUG
	   printf("iptos_setsockopt: sotoinpcb returns 0x%x\n", inp);
#endif
	   if (inp) {
	     tcpcb = intotcpcb(inp);
#ifdef IPTOS_DEBUG
	     printf("iptos_setsockopt: intotcpcb returns 0x%x\n", tcpcb);
#endif
	     if (tcpcb) {
#ifdef IPTOS_DEBUG
	       printf("iptos_setsockopt: tcpcb->iptos (0x%x) == 0x%X\n",
		      &tcpcbtotos(tcpcb), tcpcbtotos(tcpcb));
#endif
	       tcpcbtotos(tcpcb) = tos;
#ifdef IPTOS_DEBUG
	       printf("iptos_setsockopt: Done!\n\n");
#endif
	       return;
	     }
	   }
	 }
	 u.u_error = EINVAL;
	 return;
       }
     } else
       SAVE_setsockopt();
   }


xxxinit(function_code, vdp, vdi, vds)
    unsigned int function_code;
    struct vddrv *vdp;
    addr_t vdi;
    struct vdstat *vds;
{
    switch (function_code) {
      case VDLOAD:
#ifdef IPTOS_DEBUG
        printf("iptos_setsockopt: VDLOAD\n", function_code);
#endif
        SAVE_setsockopt = sysent[SYS_setsockopt].sy_call;
	sysent[SYS_setsockopt].sy_call = iptos_setsockopt;
	vdp->vdd_vdtab = (struct vdlinkage *)&iptos_vd;
	return (0);
      case VDUNLOAD:
#ifdef IPTOS_DEBUG
        printf("iptos_setsockopt: VDUNLOAD\n", function_code);
#endif
	sysent[SYS_setsockopt].sy_call = SAVE_setsockopt;
	return (0);
      case VDSTAT:
	return (0);
      default:
	return (EIO);
    }
}
