/* 
 * $Id: idrp_sock.c,v 1.5 1995/08/01 20:26:44 sjr Exp $
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

/* idrp_sock.c
 *
 *  routines to handle
 *  4 types of sockets used by IDRP 
 * 
 * idrp_ip_raw_sock_recv(pdu_proto)
 * idrp_clnp_sock_recv(pdu_proto)
 * idrp_idrp_sock_recv(pdu_proto,p_srcaddr,p_dstaddr)
 * idrp_udp_sock_recv(pdu)
 * idrp_send_clnp_pdu(peer,p_buf,len,whereto)
 * idrp_send_idrp_pdu(peer,p_buf,len,whereto)
 *
 */

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


/* 
 * Socket initialization routine for IP PIDs 
 */

idrpPeer *
idrp_ip_raw_sock_recv(pdu_proto)
idrp_proto_header	*pdu_proto;
{
idrp_fixed_ip_header	*p_ip;
idrpPeer		*peer;
struct	in_addr		dst;

	p_ip = (idrp_fixed_ip_header *) &pdu_proto->hdr.ip;
	
	/* is it really destined for me or is there some mistake?
	 * Check to see that it belongs to the interface I
	 * stuff thing out on, if not check all possible interfaces 
	 *    
	 */
	if (bcmp((caddr_t) &p_ip->hdr.ip_dst,(caddr_t) &idrp_this_node.gw.gw_addr->in.sin_addr,sizeof(struct in_addr)))
		 {
		if (!idrp_ip_interface(p_ip))
			{
	               	tracef("IDRP IP PDU with bad destination %x",p_ip->hdr.ip_dst);
			tracef(" from %x",p_ip->hdr.ip_src);
			return ((idrpPeer *) 0);
			}
		}

	IDRP_LIST(peer, idrp_peers)
		{
		if (!bcmp(&p_ip->hdr.ip_src,&peer->gw.gw_addr->in.sin_addr,sizeof(struct in_addr)))
			{
			/* match on IP address - we've got a
			 * peer - return peer
			 */
			return (peer);
			}	

		} IDRP_LIST_END;

	trace_tf(idrp_trace_options, TR_NORMAL,0,("IDRP PID with unconfigured neighbor from %x",p_ip->hdr.ip_src));

	return ((idrpPeer *) 0);
}

idrpPeer *
idrp_udp_sock_recv(pdu_proto)
idrp_proto_header *pdu_proto;
{
idrpPeer	*peer; 
idrp_fixed_udp_header	*p_udp;

	p_udp = (idrp_fixed_udp_header *) &pdu_proto->hdr.udp;

	/* validate the ISO destination in UDP wrapper */

	if (isoaddr_netiso_cmp(&p_udp->hdr.dest, &rt_net[0]))
		 {
               	tracef("IDRP PDU with bad destination %A", sockbuild_iso((byte *) &p_udp->hdr.dest.isoa_genaddr,
                	p_udp->hdr.dest.isoa_len));
		tracef(" from %A", sockbuild_iso((byte *) &p_udp->hdr.src.isoa_genaddr,
			p_udp->hdr.src.isoa_len));
		}

	IDRP_LIST(peer, idrp_peers)
		{
		if (!isoaddr_netiso_cmp(&p_udp->hdr.src, &peer->neighbor))
		return (peer);
                break;
        } IDRP_LIST_END;

        tracef("IDRP PDU with good destination %A", sockbuild_iso((byte *) &p_udp->hdr.dest.isoa_genaddr,
                	p_udp->hdr.dest.isoa_len));
	tracef(" no-configured source: %A", sockbuild_iso((byte *) &p_udp->hdr.src.isoa_genaddr,
			p_udp->hdr.src.isoa_len));
	
	return ((idrpPeer *) 0);
}


idrpPeer *
idrp_clnp_sock_recv(pdu_proto)
idrp_proto_header	*pdu_proto;
{
idrpPeer	*peer;
idrp_fixed_clnp_header	*p_clnp;


	/* This code - should be ignored for now
	 * - raw clnp does not work.
	 * I need kernel support for this
	 */
	
	/* Is this pdu destinated for me ? 
	 *  task_recv_srcaddr needs to be pulled here or in idrp_recv
	*/

	p_clnp = (idrp_fixed_clnp_header *) &pdu_proto->hdr.clnp;

	/* validate the ISO destination in UDP wrapper */

	if (isoaddr_netiso_cmp(&p_clnp->dest, &rt_net[0]))
		 {
               	tracef("IDRP PDU with bad destination %A", sockbuild_iso((byte *) &p_clnp->dest.isoa_genaddr, 
                	p_clnp->dest.isoa_len));
		tracef(" from %A", sockbuild_iso((byte *) &p_clnp->src.isoa_genaddr,p_clnp->src.isoa_len));
		}
	 

	IDRP_LIST(peer, idrp_peers) 
		{
		if (!isoaddr_netiso_cmp(&p_clnp->src, &peer->neighbor))
			{
			/* a match - so we have a peer
			 * - set length of iso addresses 
			 * in length
			 * return peer pointer
			 */
			return (peer);
			break;
			}
		} IDRP_LIST_END;


       /* trace invalid pdu on  this socket */

	trace_tf (idrp_trace_options, TR_NORMAL, 0, ("IDRP PDU arrived from unconfigured neighbor %A", 
		sockbuild_iso((byte *) &p_clnp->src.isoa_genaddr, p_clnp->src.isoa_len)));
	

}

idrpPeer *
idrp_idrp_sock_recv(pdu_proto,p_srcaddr,p_dstaddr)
idrp_proto_header	*pdu_proto;
sockaddr_un		*p_srcaddr;
sockaddr_un		*p_dstaddr;
{
idrpPeer	*peer;
idrp_fixed_idrp_header	*p_idrp;
struct iso_net_addr	*p_dst;
struct iso_net_addr	*p_src;
int	i_net = -1 ;		/* NET that I found this on */

	p_dst = (struct iso_net_addr *)p_dstaddr;
	p_src = (struct iso_net_addr *)p_srcaddr;
	/* Is this pdu destinated for me ? */

	p_idrp = (idrp_fixed_idrp_header *) pdu_proto;

	/* The source address has two address concatenated
	 * 1) gated cuts out my dst address, so for 
	 *    round 1 I will not use destination check
	 * 
	 *    round 2 - fix task_receive_packet to
	 * 		do a case statement and correctly
	 * 		handle the setting of the task_recv_dstaddr
	 *		just like bgp does.
	 *		(smile dennis has been a busy bee!!)
	 *		I wonder if I'll get hit with code
	 *		corruption/slow down comments when
	 * 		I add changes.)
	 *
	 *  
	 *   	remember to re-add this code to test the destination
	 *	address so we can use multiple sockets.
	 *  
	 *  round 3 - get the multiple socket code working!!
	 */


#ifdef IDRPFIX 
	/* here is the destination address length hack
	 * **** please fix this later (skh 8/4/93)
	 */	
	p_dst->isoa_len = p_dst->isoa_len - 2;

	/* compare the destination address */
	
	for (i = 0; i < rt_net_cnt; i++)
		{
		if (iso_netiso_cmp(p_dst, &rt_net[i]) == 0)
			{
			i_net = i;
			break;
			}
		}	

	if (i_net < 0)
		{
       	        tracef("IDRP PDU with bad destination %A", sockbuild_iso((byte *) p_idrp->idrp_header.dest.isoa_genaddr, 
       	        	p_idrp->idrp_header.dest.isoa_len));
		tracef(" from %A", sockbuild_iso((byte *) &p_idrp->idrp_header.src.isoa_genaddr, 
			p_idrp->idrp_header.src.isoa_len));
		return((idrpPeer *) 0);
		}

	trace_tf(idrp_trace_options, TR_NORMAL,0,(" packet address to NET[%d] = %A",i_net, sockbuild_iso((byte *) rt_net[i].isoa_gneaddr,rt_net[i].isoa_len))); 
#endif		
	 

	/* here is the destination address length hack
	 * **** please fix this later (skh 8/4/93)
	 */	

	p_src->isoa_len = p_src->isoa_len -2;

	/* look up the source */

	IDRP_LIST(peer, idrp_peers) 
		{
		if (!isoaddrcmp(p_src, &peer->neighbor))
			{
			p_src->isoa_len = p_src->isoa_len +2;
			return(peer);
			break;
			}
        	} IDRP_LIST_END;

       /* trace invalid pdu on  this socket */

	trace_tf (idrp_trace_options, TR_NORMAL, 0, ("IDRP PDU arrived from unconfigured neighbor %A ", 
		sockbuild_iso((byte *) &p_src->isoa_genaddr, p_src->isoa_len)));

	return(peer);
}

void
idrp_send_clnp_pdu(peer,peer_to,p_buf,len,where_to)
idrpPeer	*peer;		/* local peer */
idrpPeer	*peer_to;	/* peer to has local interface it goes out */
u_char	*p_buf;
int	len;
sockaddr_un	*where_to;
{
int 	i;
int	addr_len;
struct sockaddr_iso	*p_iso_whereto,*p_iso_from;
u_char			*p;

	/* Turn this into sockadd_iso....
	* It goes to the clnp_output via sendto
	* com/sys/upic
	*/

#ifdef	AIX_IDRP
	p_iso_whereto = (struct sockaddr_iso *) idrp_local_mem_fit (sizeof(struct sockaddr_iso));
	bzero(p_iso_whereto,sizeof(struct sockaddr_iso));

	/* setting the sockaddr_iso length to
	 * the length plus the psel, ssel, tsel 
	 *
	 */
	p_iso_whereto->siso_family = where_to->iso.siso_family;

	/* AIX code - make it 5 bytes of hearder minus one byte to  make net */

	p_iso_whereto->siso_len = where_to->iso.siso_len + 6 ;

        /* take off family and length */

	p_iso_whereto->siso_addr.isoa_len = where_to->iso.siso_len;
	bcopy(&where_to->iso.siso_addr,&p_iso_whereto->siso_addr.isoa_genaddr,where_to->iso.siso_len);
	i = sendto(peer->task->task_socket,p_buf,len,0,p_iso_whereto,(sizeof(struct sockaddr_iso)));
	IDRP_MEM_FIT_FREE(p_iso_whereto);
#endif

#ifdef 	BSD_IDRP
	p_iso_whereto = (struct sockaddr_iso *) idrp_local_mem_fit((2* sizeof(struct sockaddr_iso)));	
	bzero(p_iso_whereto,(2*sizeof(struct sockaddr_iso)));

	/* setting the sockaddr_iso length to
	 * the length plus the psel, ssel, tsel 
	 *
	 */

	/* PC hard code now - plus 11 bytes of header minus one bytes to make net */ 
	p_iso_whereto->siso_len = where_to->iso.siso_len + 12 ; 

        /* take off family and length */

	p_iso_whereto->siso_addr.isoa_len = where_to->iso.siso_len;
	bcopy(&where_to->iso.siso_addr,&p_iso_whereto->siso_addr.isoa_genaddr,where_to->iso.siso_len);

	p = (u_char *) p_iso_whereto;
	p += sizeof(struct sockaddr_iso);
	p_iso_from = (struct sockaddr_iso *) p;

	/* take off family and length */

	p_iso_from->siso_family =  where_to->iso.siso_family; 

	/* PC hard code now - plus 11 bytes of header minus one bytes to make net
	 * 			minus 2 for sockaddr of iso  
	 */ 
	p_iso_from->siso_len = peer_to->iso_intf_addr.isoa_len  + 10 ; 
	addr_len = p_iso_from->siso_addr.isoa_len = peer_to->iso_intf_addr.isoa_len - 2 ;
	bcopy(&peer_to->iso_intf_addr.isoa_genaddr,&p_iso_from->siso_addr.isoa_genaddr,addr_len);
	i = sendto(peer->task->task_socket,p_buf,len,0,(struct sockaddr *) p_iso_whereto,(int)(2*sizeof(struct sockaddr_iso)));
	idrp_mem_fit_free((void **)&p_iso_whereto,(2*sizeof(struct sockaddr_iso)));
#endif



	if (i < 0 || i != len)
		{
		if (i < 0)
			{
			trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("IDRP cannot send CLNP buffer to peer %s errno %d",peer->name,errno));
			return;
			}
		}
	return;
}
	


void idrp_send_idrp_pdu(peer,peer_to,p_buf,len,whereto)
idrpPeer	*peer;
idrpPeer	*peer_to;
u_char	*p_buf;
int	len;
sockaddr_un	*whereto;
{
int i;

	idrp_send_clnp_pdu(peer,peer_to,p_buf,len,whereto);
}


int
proto_offset(p_pdu,pdu_proto,proto_sock)
idrpPdu	**p_pdu;
idrp_proto_header	*pdu_proto;
int			proto_sock;
{
idrpPeer	*peer;
int		header_len;

	switch (proto_sock)
		{
		case IDRP_PDU_PROTO_IP_RAW:
			/* no header needed for protocol for IP send */
			*p_pdu = (idrpPdu *) pdu_proto;
                        header_len = 0;
                        break;

		case IDRP_PDU_PROTO_UDP:
			/* UDP always has ISO addresses */

			*p_pdu = (idrpPdu *) &pdu_proto->hdr.udp.pdu;
			header_len = IDRP_UDP_HEADER_LEN;
			break;

		case IDRP_PDU_PROTO_CLNP:
			/* CLNP has just pdu - I hope */
			
			*p_pdu = (idrpPdu *) pdu_proto;
			header_len = 0;
			break;
	
		case IDRP_PDU_PROTO_IDRP:
			/* IDRP has just pdu - I hope */
		
			*p_pdu = (idrpPdu *) pdu_proto;
			header_len = 0;
			break;
		}
return (header_len);
}

		
int
idrp_ip_interface(p_ip)
idrp_fixed_ip_header 	*p_ip;
{
sockaddr_un	ip;

	ip.in.sin_len = 8;
	ip.in.sin_family = AF_INET;
	ip.in.sin_port = 0;
	bcopy(&p_ip->hdr.ip_dst,&ip.in.sin_addr,sizeof(struct in_addr));
	/*if(if_myaddr(&ip))
		{
		return(TRUE);
		}
	return(FALSE);	CHL -- what are we douing here????*/
        return (TRUE);
}


boolean
idrp_same_subnet(p_att,peer)
idrp_attribute_record   *p_att;
idrpPeer        *peer;
{
struct iso_net_addr     dest;
int             i,len;
sockaddr_un     *p_dest;


        /* loop for all NLRI's in the attribute list
         * - any route on the next hop
         * skh - can this be replaced by if_iso_withsubnet??
         */

        NLRI_FAMILY_LOOP(i)
                {
                if (p_att->route_id_list->p_route[i] != NULL)
                        {
                        /* if the
                         */

                        switch (i)
                                {
                                case IDRP_NLRI_ISO:
                                        p_dest = (sockaddr_un *) &peer->neighbor;
                                        break;

                                case IDRP_NLRI_IP:
                                       p_dest = (sockaddr_un *) &peer->ip_neighbor;
                                        break;
                                }

                        if (if_withsubnet(p_dest))
                                {
                                return(TRUE);
                                }
                        }
                } NLRI_FAMILY_LOOP_END;

        return(FALSE);

}
