/* 
 * $Id: idrp_ps_dstmatch.c,v 1.6 1995/08/20 19:13:23 skh Exp $
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

/*
 *  idrp_ps_dstmatch.c -- sjr
 *
 *  Function for IDRP's matching of ps (process-specific) data w/a
 *  given destination
 */

#include "include.h"
#include "inet.h"
#include "iso.h"
#include "idrp.h"

/*
 * canon_rdpath_cmp() takes two pointers to idrp_canon_rdpath structures
 * and returns 0 if they are equal, -1 if they are not.
 */
int
canon_rdpath_cmp(idrp_canon_rdpath *path1, idrp_canon_rdpath *path2)
{
	idrp_canon_rdpath *p_1, *p_2;

	/*
	 * traverse both rdpaths in parallel; if there are any differences,
	 * then stop and return -1
	 * -- skh this section needs to be re-written (6/12/94)
	 */
	for (p_1 = path1, p_2 = path2;
		 (p_1 != (idrp_canon_rdpath *) NULL)
		  && (p_2 != (idrp_canon_rdpath *) NULL);
		 p_1 = p_1->p_next, p_2 = p_2->p_next)
	{
    	if (   !((p_1->p_rdi->rdi.isoa_family) == (p_2->p_rdi->rdi.isoa_family))
    	    || !(p_1->p_rdi->rdi.isoa_len == p_2->p_rdi->rdi.isoa_len)
    		|| (bcmp(p_1->p_rdi->rdi.isoa_genaddr, p_2->p_rdi->rdi.isoa_genaddr, 
    					 p_1->p_rdi->rdi.isoa_len))) {
        return(-1);
		}
	}

	/* if one of these still has data, then return a -1 */
	if ((p_1 != (idrp_canon_rdpath *) NULL)
		  || (p_2 != (idrp_canon_rdpath *) NULL)) {
	return(-1);
	}
	/* otherwise, we're home free */
	else {
	return (0);
	}
}

/*
 * This is the routine for testing IDRP path attributes not already
 * defined in the struct _adv_entry in policy.  This module is
 * called by 'import()'.
 */
int
idrp_ps_dstmatch __PF3(adv_ps_field, void *,
		dst, sockaddr_un *,
		ps_data, void *)
{
	int 
	    i,
            match = FALSE;
        idrp_ps_t *idrp_ps;
	idrpRoute_info *route_data;
	idrpRoute_options *route_opts;
        idrpRoute *p_idrp_rt;
		
	snpa_entry
	    *p_snpa_entry;

        idrp_ps = (idrp_ps_t *) adv_ps_field;
	route_data = (idrpRoute_info *) ps_data;
        p_idrp_rt = route_data->p_idrp_rt;
	route_opts = p_idrp_rt->p_attr->p_opts;

	switch (idrp_ps->ps_flag & IDRP_PSFT_TYPE) {
	/* Tests on the route_opts: */
    	case (IDRP_PSFT_NEXTHOP_NET):
    		if (iso_net_addr_cmp(idrp_ps->ps_nexthop_net, 
				     (struct iso_net_addr *)(route_opts->next_hop[IDRP_NLRI_ISO].p_net))) {

    		/* skh -- sjr if (iso_net_addr_cmp(idrp_ps->ps_nexthop_net, 
				     &(route_opts->next_hop[IDRP_NLRI_ISO].net))) */ 
        		match = TRUE;
        		match = TRUE;
        	}
		break;

    	case (IDRP_PSFT_NEXTHOP_SNPA):
	/* 
	 * I'll assume that the SNPA of the policy is a single SNPA, and
	 * that the SNPA list for the actual route is a real list.
	 */
		SNPA_LIST(i, p_snpa_entry, route_opts->next_hop[IDRP_NLRI_ISO].next_hop_snpas)
			if ((idrp_ps->ps_nexthop_snpas->snpa1.len == 
				p_snpa_entry->len)
		            && (!bcmp(idrp_ps->ps_nexthop_snpas->snpa1.snpa,
				p_snpa_entry->snpa, p_snpa_entry->len))) {

				match = TRUE;
				break;
			};
		SNPA_LIST_END;
		break;

		
    	case (IDRP_PSFT_NEXTHOP_IPGW):
		{
		sockaddr_un *p_gw = p_idrp_rt->p_attr->p_opts->next_hop[IDRP_NLRI_IP].p_net;
/* skh --sjr	sockaddr_un *p_gw = (sockaddr_un *) &p_idrp_rt->p_attr->p_opts->next_hop[IDRP_NLRI_IP].net; */

    		if ((socktype(idrp_ps->ps_gw) == socktype(p_gw))
    		    && (socktype(idrp_ps->ps_gw) == AF_INET)
    		    && (sockaddrcmp_in(idrp_ps->ps_gw, p_gw))) {
        		match = TRUE;
        		}
		}
		break;

    	case (IDRP_PSFT_NEXTHOP_IPINTF):
    		if (   (socktype(idrp_ps->ps_intf) == socktype(route_opts->p_intf))
    		    && (socktype(idrp_ps->ps_intf) == AF_INET)
    		    && (sockaddrcmp_in(idrp_ps->ps_intf, route_opts->p_intf))) {

        	match = TRUE;
        	}
		break;

#ifdef PROTO_RDPATHS
    	case (IDRP_PSFT_DISTINCL):
    	if (!canon_rdpath_cmp(idrp_ps->ps_DIST_LIST_INCL, 
    	    route_opts->p_DIST_LIST_INCL)) {
        match = TRUE;
        }
		break;

    	case (IDRP_PSFT_DISTEXCL):
    	if (!canon_rdpath_cmp(idrp_ps->ps_DIST_LIST_EXCL, 
    	    route_opts->p_DIST_LIST_EXCL)) {
        match = TRUE;
        }
		break;
#endif

    	case (IDRP_PSFT_MULTIEXIT):
       		if (BIT_TEST(route_opts->status, IDRP_OPTS_MULTI_EXIT)) {
	        	if (idrp_ps->ps_multi_exit) {
		  	/* do QoS test */
				if (ONE_SIDED_QOS_TEST(*idrp_ps->ps_multi_exit, route_opts->multi_exit_rcvd)) {
	        			match = TRUE;
		      		} 
       			} else {
	        	match = TRUE;
       			}
       		}
		break;

	case (IDRP_PSFT_TRANSIT_DELAY):
		if (!(route_opts->p_qos)) 
			break;
		
		if (idrp_ps->ps_qos) {
			/* do QoS test */
			if (ONE_SIDED_QOS_TEST(idrp_ps->ps_qos->delay, route_opts->p_qos->delay.value)) {
	        		match = TRUE;
		      } 
       		} else if (route_opts->p_qos->delay.operator != IDRP_QOS_OP_NO) {
			match = TRUE;
		}
		break;

	case (IDRP_PSFT_EXPENSE):
		if (!(route_opts->p_qos)) 
			break;
		
		if (idrp_ps->ps_qos) {
			/* do QoS test */
			if (ONE_SIDED_QOS_TEST(idrp_ps->ps_qos->expense, route_opts->p_qos->expense.value)) {
	        		match = TRUE;
		      } 
       		} else if (route_opts->p_qos->expense.operator != IDRP_QOS_OP_NO) {
			match = TRUE;
		}
		break;

	case (IDRP_PSFT_RESIDUAL_ERROR):
		if (!(route_opts->p_qos)) 
			break;
		
		if (idrp_ps->ps_qos) {
			/* do QoS test */
			if (ONE_SIDED_QOS_TEST(idrp_ps->ps_qos->error, route_opts->p_qos->error.value)) {
	        		match = TRUE;
		      } 
       		} else if (route_opts->p_qos->error.operator != IDRP_QOS_OP_NO) {
			match = TRUE;
		}
		break;

	case (IDRP_PSFT_PRIORITY):
		if (!(route_opts->p_qos)) 
			break;
		
		if (idrp_ps->ps_qos) {
			/* do QoS test */
			if (ONE_SIDED_QOS_TEST(idrp_ps->ps_qos->priority, route_opts->p_qos->priority.value)) {
	        		match = TRUE;
		      } 
       		} else if (route_opts->p_qos->priority.operator != IDRP_QOS_OP_NO) {
			match = TRUE;
		}
		break;

    	case (IDRP_PSFT_HOPCNT):
		if (idrp_ps->ps_hopcount) {
			/* do QoS test */
			if (ONE_SIDED_QOS_TEST(*idrp_ps->ps_hopcount, route_opts->hopcount)) {
	        		match = TRUE;
		      } 
		} 
		break;

    	case (IDRP_PSFT_CAPACITY):
	        if (idrp_ps->ps_capacity) {
			/* do QoS test */
			if (ONE_SIDED_QOS_TEST(*idrp_ps->ps_capacity, route_opts->capacity.value)) {
	        		match = TRUE;
		      } 
       		}
		else if (route_opts->capacity.operator != IDRP_QOS_OP_NO) {
	        	match = TRUE;
		}
		break;

    	case (IDRP_PSFT_ROUTESERV):
		if (BIT_TEST(route_opts->status, IDRP_OPTS_ROUTE_SERVER)) {
	        	match = TRUE;
       		}
		break;

	case(IDRP_PSFT_EXTINFO):
		if (BIT_TEST(route_opts->status, IDRP_OPTS_EXT_INFO)) {
	        	match = TRUE;
       		}
		break;

    	case (IDRP_PSFT_HIERARCH):
		if (BIT_TEST(route_opts->status, IDRP_OPTS_HIER_REC)) {
	        	match = TRUE;
       		}
		break;

	/* Tests on the p_idrp_rt information: */
	case (IDRP_PSFT_LOCAL_IP_INTF):
		if ((p_idrp_rt->peer) && 
		    (p_idrp_rt->peer->ip_neighbor) && 
		    sockaddrcmp_in(idrp_ps->ps_intf, 
				    p_idrp_rt->peer->ip_neighbor)
		    ) {
	        	match = TRUE;
		}
		break;

	/* Currently uses NET, as there is one NET per intf... */
	case (IDRP_PSFT_LOCAL_SNPA):
		if ((p_idrp_rt->peer) && 
		    iso_net_addr_cmp(idrp_ps->ps_iso_net_addr, 
				     &(p_idrp_rt->peer->neighbor))
		    ) {
	        	match = TRUE;
		}
		break;

	default:
		assert(FALSE);
		break;
	}

	if (BIT_TEST(idrp_ps->ps_flag, IDRP_PSFT_RESTRICT)) {
		trace_tf(idrp_trace_options, TR_ALL, 0, ("match was %d; now is %d", match, !match));
		match = !match;
	}

	if(match) {
		trace_tf(idrp_trace_options, TR_ALL, 0, ("MATCH on flag = %lu", idrp_ps->ps_flag));
	} else {
		trace_tf(idrp_trace_options, TR_ALL, 0, ("NO MATCH on flag = %lu", idrp_ps->ps_flag));
	}
	return(match);
}
