/************************************************************************/
/*	Routing Protocol Simulator	Relese 1.0	1994/3/17	*/
/*									*/
/*		module 	: common module					*/
/*		file	: socket.c			       		*/
/*									*/
/*   Copyright (c) 1994 by Systems Development Laboratory Hitachi,Ltd.	*/
/*   All rights reserved.						*/
/*----------------------------------------------------------------------*/
/*	UPDATE HISTORY							*/
/*									*/
/************************************************************************/
static char copyright[]=
  "@(#)Copyright (c) 1994 by Systems Development Laboratory Hitachi,Ltd.\n All rights researved.\n";

#include "rps.h"

#ifdef VXWORKS

#include "errno.h"
#include "systime.h"

#else

#include <sys/errno.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
/*#include <sys/stropts.h>*/
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>

#endif

struct ip {
#ifndef BSD
    u_char  ip_v:4,                 /* version */
 	    ip_hl:4;                /* header length */
#else
    u_char  ip_hl:4,
            ip_v:4;
#endif	 
    u_char  ip_tos;                 /* type of service */
    short   ip_len;                 /* total length */
    u_short ip_id;                  /* identification */
    short   ip_off;                 /* fragment offset field */
#define IP_DF 0x4000                    /* dont fragment flag */
#define IP_MF 0x2000                    /* more fragments flag */
    u_char  ip_ttl;                 /* time to live */
    u_char  ip_p;                   /* protocol */
    u_short ip_sum;                 /* checksum */
    struct  in_addr ip_src,ip_dst;  /* source and dest address */
};

struct udphdr {
    u_short uh_sport;
    u_short uh_dport;
    short   uh_ulen;
    u_short uh_sum;
};

struct SOCK_LIST {
    struct SOCK_LIST *next;
    int sock;
    int port;
    u_long addr;
    void (*recv_proc)();
};

void add_sock_list();

int max_sock=-1;

int clkrate;

#define 	RECV_BUFF_SIZE	1500

char recvbuf[1500];
int frame_recv=0;
int ether_fd;
byte *wp = recvbuf;
byte *rp = recvbuf;
int buf_len = 0;

u_long recv_fd;

fd_set read_sock, write_sock;

struct SOCK_LIST *sock_list = NULL;

/*

  sock_init()

*/
void sock_init()
{
    FD_ZERO(&read_sock);
    FD_ZERO(&write_sock);

#ifdef VXWORKS
    clkrate = sysClkRateGet();
#endif
}

/*
 * ospf_sock_init()
 */
/*int ospf_sock_init(func)
void (*func)();
{
    int sock;
    struct SOCK_LIST *sl;
    
    sock = socket(AF_INET, SOCK_RAW, IPPROTO_OSPF);
    if( sock < 0) {
	perror("raw socket");
	exit(1);
    }

    MALLOC(sl,SOCK_LIST);
    sl->sock = sock;
    sl->recv_proc = func;

    FD_SET(sock,&read_sock);
    FD_SET(sock,&write_sock);

    add_sock_list(sl);

    return sock;
}

*/

/*
 * get_socket
 */
int get_socket(type,protocol,func)
int type;
int protocol;
void (*func)();
{
    int sock;
    struct SOCK_LIST *sl;
    
    sock = socket(AF_INET, type, protocol);
    if( sock < 0) {
	perror("get socket");
	exit(1);
    }

    MALLOC(sl,SOCK_LIST);
    sl->sock = sock;
    sl->recv_proc = func;

    FD_SET(sock,&read_sock);
    FD_SET(sock,&write_sock);

    add_sock_list(sl);

    return sock;
}

/*

  socket_bind

*/
void socket_bind(sock,port,intf)
int sock;
int port;
u_long intf;
{
    struct sockaddr_in addr;
    struct SOCK_LIST *sl;

    for(sl=sock_list;sl;sl=sl->next)
      if(sl->sock == sock) break;

    sl->port = port;
    sl->addr = intf;
    
    bzero((char *)&addr,sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(sock,(struct sockaddr *)&addr, sizeof(addr))<0)
       perror("bind");
}
    
/*
 * ospf_txpkt
 */
void ospf_txpkt(sock,pkt, pktlen, dst)
int sock;
char	*pkt;
u_short	pktlen;
u_long	dst;
{
    u_short	destlen;
    struct sockaddr_in	dest;
    int flag;

    destlen = sizeof(dest);
    
    flag = 0;

    /* destination address set */
    bzero((char *) &dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_addr.s_addr = dst;

    /*  printf("\n<<<<< OSPF PACKET SEND >>>>>\n\n");*/
    if(sendto(sock, (char *)pkt,(int)pktlen, flag, (struct sockaddr *)&dest, destlen) < 0)
      perror("sendto");
}

/*

  udp_txpkt

*/
void udp_txpkt(sock,port,pkt, pktlen, dst)
int sock;
int port;
char	*pkt;
u_short	pktlen;
u_long	dst;
{
    u_short	destlen;
    struct sockaddr_in	src,dest;
  
/*    src.sin_family = AF_INET;
    src.sin_port = htons(port);
    src.sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(sock,(struct sockaddr *)&src, sizeof(src))<0)
       perror("bind");
*/
    destlen = sizeof(dest);

    /* destination address set */
    bzero((char *)&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_addr.s_addr = dst;
    dest.sin_port = port;
    
    if(sendto(sock, (char *)pkt, (int)pktlen, 0, (struct sockaddr *)&dest, destlen) < 0)
      perror("sendto");
}

/*
 * ospf_rxpkt()
 */
void ospf_rxpkt(sock,recv, s, d)
int sock;
char *recv;
u_long *s,*d;
{
    struct ip *ip;
    struct sockaddr from;
    int	cc, fromlen, hlen;

    fromlen = sizeof(from);
    ip = NULL;
    
    cc = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, &from, &fromlen);
    if (cc < 0) perror("recvfrom");
/*    printf("\n<<<<< OSPF PACKET RECEIVE >>>>>\n\n");*/

    ip = (struct ip *) recvbuf;
    hlen = ip->ip_hl << 2;

    *s = ntohl(ip->ip_src.s_addr);
    *d = ntohl(ip->ip_dst.s_addr);
/*    *recv = *recv + hlen;*/

    bcopy(recvbuf+hlen,recv,cc-hlen);

}

/*

  udp_rxpkt()
  
*/
void udp_rxpkt(sock,recv, s, d)
int sock;
char *recv;
u_long *s,*d;
{
    struct ip *ip;
    struct sockaddr_in from;
    int	cc, fromlen, hlen;
    struct SOCK_LIST *sl;

    fromlen = sizeof(from);
    
    cc = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&from, &fromlen);
    if (cc < 0)
      perror("recvfrom");

/*    *recv = recvbuf;*/

/*    ip = (struct ip *) recvbuf;
    hlen = ip->ip_hl << 2;*/

    for(sl=sock_list;sl;sl=sl->next)
      if(sl->sock == sock) break;
    
    *s = from.sin_addr.s_addr;
    *d = sl->addr;
/*    *recv = *recv + hlen;*/

    bcopy(recvbuf,recv,sizeof(recvbuf));
}

/*

  recv_packet

*/
int recv_packet(src,dst,pkt)
u_long *src;
u_long *dst;
byte *pkt;
{
    fd_set read_fd,write_fd;
    int rt;
    struct SOCK_LIST *list;
    struct timeval t;

    t.tv_sec = 0;
    t.tv_usec = 0;
    
    read_fd = read_sock;
    write_fd = write_sock;

    select(max_sock+1,&read_fd,&write_fd,NULL,&t);

    /*
  printf("\n<<<<< check3 >>>>>\n\n");
*/
  for(list=sock_list;list;list=list->next) {
/*
      printf("\n<<<<< check4 >>>>>\n\n");
*/
      if(FD_ISSET(list->sock,&read_fd)) {
/*
	  printf("\n<<<<< check5 >>>>>\n\n");
*/
	  if(list->recv_proc)
	    list->recv_proc(list->sock,pkt,src,dst);
/*
	  printf("\n<<<<< check6 >>>>>\n\n");
*/
	  return 1;
	}
    }

    return 0;
}


/*

  add_sock_list

*/
void add_sock_list(sock_lp)
struct SOCK_LIST  *sock_lp;
{
    struct SOCK_LIST *list;

    if(sock_list == NULL) {
	sock_list = sock_lp;
    }
    else {
	for(list=sock_list;list->next;list=list->next);

	list->next = sock_lp;
    }

    if(sock_lp->sock > max_sock) {
	max_sock = sock_lp->sock;
    }
    
}

	    
/*

  sock_term

*/
void sock_term()
{
    struct SOCK_LIST *list,*next;

    for(list=sock_list;list;) {
	next = list->next;
	FD_CLR(list->sock,&read_sock);
	FD_CLR(list->sock,&write_sock);
	free((char *)list);
	list = next;
    }

    sock_list = NULL;

}

/*

  recv_frame

*/
#ifdef VXWORKS

int recv_frame(src,dst,pkt)
u_long *src;
u_long *dst;
byte *pkt;
{
    struct ip *ip_packet;
    int hlen;
    
    if(frame_recv) {
/*	printf("get ip\n");*/
	
	ip_packet = (struct ip *) recvbuf;
	hlen = ip_packet->ip_hl << 2;

	*src = ip_packet->ip_src.s_addr;
	*dst = ip_packet->ip_dst.s_addr;

	bcopy(recvbuf+hlen,pkt,sizeof(recvbuf)-hlen);
	frame_recv = 0;
/*	taskUnlock();*/
	return ip_packet->ip_p;
    }

    return 0;
}

#else

int recv_frame(src,dst,pkt)
u_long *src;
u_long *dst;
byte *pkt;
{
    struct ip *ip;
    int hlen,elen;
    int len;
    
/*    elen = sizeof(struct ether_header);*/

    if((len = ether_read()) > 0) {	    
	ip = (struct ip *) recvbuf;
       	hlen = ip->ip_hl << 2;
	
	*src = ip->ip_src.s_addr;
	*dst = ip->ip_dst.s_addr;

	bcopy(recvbuf+hlen,pkt,sizeof(recvbuf)-hlen);
	frame_recv = 0;
	return ip->ip_p;
    }

    return 0;
}

#endif


/*

  ip_type

*/
int ip_type(buff)
byte *buff;
{
    struct ip *ip;

    ip = (struct ip *)buff;
    
    return(ip->ip_p);
}

/*

  ip_protocol_check

*/
int ip_protocol_check(buff)
byte *buff;
{
    struct ip *ip;
    int rt;

    rt = 0;
    
    ip = (struct ip *)buff;
/*    printf("protocol = %d\n",ip->ip_p);*/
    switch(ip->ip_p) {
    case IPPROTO_IP :
    case IPPROTO_ICMP :
    case IPPROTO_RAW :
    case 89 : rt = 1;
		break;
	default : break;
    }
    
    return rt;
}


/*

  recv_frame_copy

*/
void recv_frame_copy(buff,len,fd)
byte *buff;
int len;
u_long fd;
{
    if(buff) {
/*	if(buf_len + len > RECV_BUFF_SIZE) {
	    bcopy(buff,recvbuf,len);
	    wp = recvbuf + len;
	    buf_len = len;
	}
	else {
	    bcopy(buff,wp,len);
	    wp = wp + len;
	    buf_len += len;
	}*/
	bcopy(buff,recvbuf,len);
    }

/*    printf("frame copy done bufp = %x len = %d\n",wp,buf_len); */
    
    if(fd)
      ether_fd = fd;
    
    frame_recv = 1;
    
#ifdef VXWORKS
    
  /*  taskLock();*/

#endif
}

/*

  frame_buff_clear

*/
void frame_buff_clear()
{
    wp = rp = recvbuf;
    buf_len = 0;
}

/*

  get_ip_buff

*/
char *get_ip_buff()
{
    return recvbuf;
}


/*

  sock_close

*/
void sock_close(sock)
int sock;
{
    struct SOCK_LIST *list,*pre;

    close(sock);

    return ;

/*    pre = NULL;
    max_sock = 0;
    for(list=sock_list;list;) {
	if(list->sock == sock) {
	    FD_CLR(list->sock,&read_sock);
	    FD_CLR(list->sock,&write_sock);
	    if(pre) {
		pre->next = list->next;
	    }
	    else {
		sock_list = list->next;
	    }
	    close(sock);
	    free((char *)list);
	}
	else if(max_sock < list->sock) {
	    max_sock = list->sock;
	}
	pre = list;
	list = list->next;
    }

*/
}


