/* 
 * $Id: idrp_ps_rtmatch.c,v 1.6 1996/05/25 13:31:41 skh Exp $
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

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

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

/*
 * This is the routine for testing IDRP path attributes not already
 * defined in the struct _adv_entry in policy.  This module is
 * called by 'export()'.
 */

int
idrp_ps_rtmatch __PF2(adv_ps_field, void *,
		      p_rt, rt_entry *)
{
	int 
	    i,
            match = FALSE;
        idrp_ps_t *idrp_ps;
        idrpRoute *p_idrp_rt;
	idrpRoute_options *route_opts;
	snpa_entry
	    *p_snpa_entry;

        idrp_ps = (idrp_ps_t *) adv_ps_field;
	if (!p_rt) {
		trace_tf(idrp_trace_options, TR_ALL, 0, ("idrp_ps_rtmatch (%d):  no p_rt!", __LINE__));
		exit(-1);
	}

        p_idrp_rt = p_rt->rt_idrp;

	if (!p_idrp_rt || !p_idrp_rt->p_attr || !p_idrp_rt->p_attr->p_opts) {
		trace_tf(idrp_trace_options, TR_ALL, 0, ("idrp_ps_rtmatch (%d):  no p_idrp_rt (0x%lx) or no p_idrp_rt->p_attr (0x%lx) or no p_idrp_rt->p_attr->p_opts!", __LINE__, p_idrp_rt, (p_idrp_rt) ? p_idrp_rt->p_attr : 0UL, ((p_idrp_rt) && (p_idrp_rt->p_attr)) ? p_idrp_rt->p_attr->p_opts : 0UL ));
		exit(-1);
	}

	route_opts = p_rt->rt_idrp->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 check this replace &(route_opts->next_hop[IDRP_NLRI_ISO].net))) { */
        		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):
    		if (   (socktype(idrp_ps->ps_gw) == socktype(route_opts->next_hop[IDRP_NLRI_IP].p_net))
    		    && (socktype(idrp_ps->ps_gw) == AF_INET)
    		    && (sockaddrcmp_in(idrp_ps->ps_gw,route_opts->next_hop[IDRP_NLRI_IP].p_net))) {

    		/*if (   (socktype(idrp_ps->ps_gw) == socktype((sockaddr_un *) &(route_opts->next_hop[IDRP_NLRI_IP].p_net)))
    		    && (socktype(idrp_ps->ps_gw) == AF_INET)
    		    && (sockaddrcmp_in(idrp_ps->ps_gw, (sockaddr_un *) &(route_opts->next_hop[IDRP_NLRI_IP].net)))) */
        	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 (!(route_opts->p_qos)) 
			break;
		
		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);
}

