/* 
 * $Id: idrp_init_sock.c,v 1.3 1995/08/08 14:04:56 skh Exp $
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

/* idrp_init_sock.c
 *
 *  routines to initialize the
 *  4 types of socket used by IDRP
 * 
 * - hopefully most of this code will
 *   go to the gated internals
 * idrp_ip_pid_sock_init
 * idrp_upd_sock_init
 * idrp_clnp_raw_sock_init
 * idrp_idrp_sock_init
 * idrp_init_local_proto_sock
 *
 */

#include "include.h"
#include "iso.h"
#include "idrp.h"
#include "inet.h"
#ifdef KERNEL_SUPPORTS_ISO
#include <netiso/tp_user.h>
#else /* KERNEL_SUPPORTS_ISO */
#include "iso_support.h"
#endif /* KERNEL_SUPPORTS_ISO */



/* 
 * Socket initialization routine for IP PIDs 
 */

void
idrp_ip_pid_sock_init(p_task,p_addr)
task		*p_task;
sockaddr_un	*p_addr;
{
	p_task->task_proto = IPPROTO_IDRP;
	p_addr->in.sin_port = 0;
	p_task->task_addr = sockdup(p_addr);
     	if ((p_task->task_socket = task_get_socket(p_task, AF_INET, SOCK_RAW, IPPROTO_IDRP)) < 0)
		{
		task_quit(errno);
		}
	task_set_socket(p_task,p_task->task_socket);
	if (!task_create(p_task))
                {
                task_quit(EINVAL);
		}

	/* set receive buffer size */
	/* reset to plain 1500 */

       if (task_set_option(p_task, TASKOPTION_RECVBUF, (caddr_t) (10*1500)) < 0) 
		{
		task_quit(errno);
		}
	/* set transmit buffer size for kernel *?
	/* reset to 1500 flat */
       if (task_set_option(p_task, TASKOPTION_SENDBUF, (caddr_t) (10*1500)) < 0) 
		{
		task_quit(errno);
		}

	/* set non-block IO */

	if (task_set_option(p_task,TASKOPTION_NONBLOCKING,(caddr_t) TRUE) < 0)
		{
		task_quit (errno);
		}


	/* bind the task to the PID socket */
 
	/* 	if (task_addr_local(p_task,p_addr))
	 *	{
	 *	task_quit(errno);
 	 *	}
	 */

	trace_tf (idrp_trace_options, TR_NORMAL,0, ("idrp master task now supports idrp pid on the ip raw socket")); 	

}

void 
idrp_udp_sock_init(p_task,p_addr)
task	*p_task;
sockaddr_un	*p_addr;
{ 
	/* The UDP socket is broken - I'm return with error
 	 * at this point to indicate that
	 */

	trace_tf(idrp_trace_options, TR_NORMAL,0,(" IDRP UDP support broken - please try another"));
	task_quit (0);
	
	/* 
	/* here's the IDRP set-up for a single TCP port 
         * this must change to a protocol id
	 */

        p_task->task_addr = sockdup(inet_addr_any);
	sock2port(p_task->task_addr) = idrp_port;
	sock2port(p_addr) = idrp_port;

	if ((p_task->task_socket = task_get_socket(p_task, AF_INET, SOCK_DGRAM, 0)) < 0)
		{
		task_quit(errno);
		}

	if (!task_create(p_task))
                {
                task_quit(EINVAL);
		}

       if (task_set_option(p_task, TASKOPTION_RECVBUF, (caddr_t) (10*1500)) < 0) 
		{
		task_quit(errno);
		}

	/* bind the task to the  UDP socket */
 
	if (task_addr_local(p_task,p_addr))
		{
		task_quit(errno);
		}

	trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp master task now supports the udp socket")); 	
}

void 
idrp_clnp_raw_sock_init(p_task,p_addr)
task		*p_task;
sockaddr_un	*p_addr;
{
	trace_tf (idrp_trace_options, TR_NORMAL,0,("clnp_raw socket not supported at this time"));
}


void 
idrp_idrp_sock_init(p_task)
task		*p_task;
{
#ifdef	IDRP_IP_ONLY
	trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp master task cannot support the idrp socket on idrp for ip only")); 	
	task_quit(0);

#else
	trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp master task now supports the idrp socket")); 	
	
	/* need allocate with the iso_gw_addr for task 
	 *  - try for now.  If problems.  We must use
	 * older version of  sockaddr_iso
	 */


	p_task->task_proto = ISOPROTO_IDRP;
	p_task->task_addr = sockdup((sockaddr_un *) idrp_this_node.iso_gw.gw_addr);
#ifndef KERNEL_SUPPORTS_ISO
   	if ((p_task->task_socket = task_get_socket(p_task, AF_ISO, SOCK_DGRAM,0)) < 0)
#else
       if ((p_task->task_socket = task_get_socket(p_task, AF_ISO, SOCK_DGRAM,ISOPROTO_IDRP)) < 0)
#endif /* KERNEL_SUPPORTS_ISO */
		{
		task_quit(errno);
		}

	task_set_socket(p_task,p_task->task_socket);

	if (!task_create(p_task))
                {
                task_quit(EINVAL);
		}

	/* set the task receive and transmit buffer size */
#ifdef 	BSD_IDRP
	if (task_set_option(p_task, TASKOPTION_RECVBUF, (caddr_t) (IDRP_PKTSIZE*2)) < 0) 
		{
	   		task_quit(errno);
	 	}

#endif

	if (task_set_option(p_task,TASKOPTION_SENDBUF,(caddr_t) IDRP_PKTSIZE) < 0)
		{
		task_quit(errno);
		}

	/* non-blocking on the socket
	 */

	if (task_set_option(p_task,TASKOPTION_NONBLOCKING,(caddr_t) TRUE) < 0)
		{
		task_quit(errno);
		}

	 trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp master task now supports the idrp socket")); 	

#endif /* IDRP_IP_ONLY */  
}

void
idrp_init_local_proto_sock(p_task)
task	*p_task;
{
idrpPeer	*peer;
sockaddr_un	*localaddr = NULL;

	peer = &idrp_this_node;
	/* eventually this needs to be changed to be
	 * a mask of bits for the idrp_local node
	 * - however for simplicity now
	 * - we use one set either by default to IP
	 * or by the local-node configuration statement:
	 *	local-node 
	 *		proto-sock idrp;  - to override
	 *				   or ip to reset 
	 *
	 * 	when clnp or udp are reset, then the
	 *	local node proto-sock will help that as well
	 * 
	 *  
	 */


	switch(idrp_this_node.proto_sock)
		{
		case IDRP_PDU_PROTO_IP_RAW:
			/* - this takes the first address configured on peer or
			 * loopback 
			 */
			localaddr = idrp_find_interface(AF_INET);
			if (!localaddr) {
				trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("idrp_init: Cannot determine interface for neighbor %s\n", peer->name));
				task_quit(EDESTADDRREQ);
				}

			idrp_ip_pid_sock_init(p_task,localaddr);
			break;

		case IDRP_PDU_PROTO_UDP:
			localaddr = idrp_find_interface(AF_INET);
			if (!localaddr) {
				trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("idrp_init: Can't determine interface for neighbor %s", peer->name));
				task_quit(EDESTADDRREQ);
				}
			idrp_udp_sock_init(p_task,localaddr);
			break;

		case IDRP_PDU_PROTO_CLNP:
			localaddr = idrp_find_interface(AF_ISO);
			if (!localaddr) {
				trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("idrp_init: Can't determine interface for neighbor %s", peer->name));
				task_quit(EDESTADDRREQ);
				}
			idrp_clnp_raw_sock_init(p_task,localaddr);
			break;

		case IDRP_PDU_PROTO_IDRP:
			localaddr = idrp_find_interface(AF_ISO);
			if (!localaddr) {
				trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("idrp_init: Can't determine interface for neighbor %s", peer->name));
				task_quit(EDESTADDRREQ);
				}
			idrp_idrp_sock_init(p_task);
			break;
		}		

}

void
idrp_set_local_iso_intf_addr()
{
	/* this iso address must have the
	 * full length of the sockaddr
	 * - this depends on the kernel 
	 *   definition of the bind 
	 *   for clnp/idrp
	 * 
	 *   IDRP_HP requires something
	 *  
	 * we are using the 1st net as the binding for
	 * the task as of now.
	 * 
	 * - future calls may bind this with an offset into
	 * - the configured NETs to allow
	 * - berkley nodes to function 
	 */


	idrp_this_node.iso_intf_addr.isoa_len = rt_net[0].isoa_len + 2;
	idrp_this_node.iso_intf_addr.isoa_family = AF_ISO;
	bcopy(&rt_net[0].isoa_genaddr,idrp_this_node.iso_intf_addr.isoa_genaddr,rt_net[0].isoa_len);
}
