⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xorprtm.c

📁 xorp源码hg
💻 C
📖 第 1 页 / 共 4 页
字号:
/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
 * vim:set sts=4 ts=8:
 *
 * Copyright (c) 2001-2007 International Computer Science Institute
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software")
 * to deal in the Software without restriction, subject to the conditions
 * listed in the XORP LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the XORP LICENSE file; the license in that file is
 * legally binding.
 *
 * $XORP: xorp/contrib/win32/xorprtm/xorprtm.c,v 1.3 2007/02/16 22:45:33 pavlin Exp $
 */

/* XXX: SORT FUNCTIONS IN THIS FILE */

/* XXX: RATIONALIZE INCLUDES */

#include "pchsample.h"
#include <iphlpapi.h>
#pragma hdrstop

/* XXX: move to headers */
typedef union _sockunion_t {
    struct sockaddr	    sa;
    struct sockaddr_in	    sin;
    struct sockaddr_in6	    sin6;
    struct sockaddr_storage ss;
} sockunion_t;

/* XXX: move to headers */
#define XORPRTM_PIPETIMEOUT 2000 /* 2 seconds */
#define XORPRTM_RI_PREF	    1
#define XORPRTM_RI_METRIC   20

/* XXX: run cproto */
/* XXX: move to headers */
pipe_instance_t *pipe_new(void);
void pipe_destroy(pipe_instance_t *pp);
int pipe_listen(pipe_instance_t *pp);
void pipe_disconnect(pipe_instance_t *pp);
void CALLBACK pipe_connect_cb(PVOID lpParameter, BOOLEAN TimerOrWaitFired);
void CALLBACK pipe_read_cb(PVOID lpParameter, BOOLEAN TimerOrWaitFired);
void WINAPI pipe_reread_cb(void *ctx);
void WINAPI pipe_relisten_cb(void *ctx);
DWORD APIENTRY RTM_CallbackEvent (RTM_ENTITY_HANDLE hRtmHandle, RTM_EVENT_TYPE   retEvent, PVOID pvContext1, PVOID pvContext2);
int rtm_add_route(struct rt_msghdr *rtm, int msgsize);
void broadcast_pipe_message(void *msg, int msgsize);

/*
 * XXX: The only global variable.
 */
/* XXX: move to headers */
CONFIGURATION_ENTRY g_ce;

/*
 * Issue a routing socket message for a single changed destination.
 */
DWORD
rtm_send_dest_change(RTM_ENTITY_HANDLE reh, PRTM_DEST_INFO prdi)
{
#ifdef IPV6_DLL
    struct in6_addr dst;
    struct in6_addr ip;
    struct in6_addr nhip;
#else
    struct in_addr dst;
    struct in_addr ip;
    struct in_addr nhip;
#endif
    int i;
    int	dstprefix;
    int nhprefix;
    int type;
    DWORD result;

    if (!prdi)
        return NO_ERROR;

    TRACE1(NETWORK, "RtmDestInfo Destination %p", prdi);

#ifdef IPV6_DLL
    RTM_IPV6_GET_ADDR_AND_LEN(dst.s6_addr, dstprefix, &prdi->DestAddress);
#else
    RTM_IPV4_GET_ADDR_AND_LEN(dst.s_addr, dstprefix, &prdi->DestAddress);
#endif

    /*
     * Determine the nature of the change; whether a route has
     * been added, changed or deleted for the given situation.
     * We look only at the unicast routing view.
     */
    for (i = 0; i < prdi->NumberOfViews; i++) {
    	if (prdi->ViewInfo[i].ViewId == RTM_VIEW_ID_UCAST) {
#ifdef IPV6_DLL
	    /*
	     * XXX: Don't filter IPv6 routes [yet].
	     */
#else	/* IPv4 */
	    /*
	     * Ignore routes to the all-ones broadcast destination.
	     */
	    if ((dst.s_addr == INADDR_BROADCAST && dstprefix == 32)) {
		TRACE0(NETWORK, "ignoring all-ones broadcast");
		break;
	    }
#ifdef notyet
	    /*
	     * XXX: Ignore multicast routes (for now).
	     */
	    if (IN4_IS_ADDR_MULTICAST(dst.s_addr)) {
		TRACE0(NETWORK, "ignoring multicast route");
		break;
	    }
#endif /* notyet */
#endif /* IPV6_DLL */
	    if (prdi->ViewInfo[i].NumRoutes == 0) {
		TRACE0(NETWORK, "route deleted");
 		type = RTM_DELETE;
	    } else if (prdi->ViewInfo[i].NumRoutes == 1) {
		TRACE0(NETWORK, "route added");
 		type = RTM_ADD;
	    } else {
		/*
		 * XXX: The route has multiple next-hops. We do not know
		 * which next-hop we should send to the FEA, so do not
		 * process such changes for now.
		 */
		TRACE1(NETWORK, "route change, dest %d nexthops, no msg",
			prdi->ViewInfo[i].NumRoutes);
		type = 0;
	    }
	    break;  /* stop when unicast route view is dealt with. */
	}
    }
    /*
     * Craft a routing socket message based on the changes.
     * We only allocate memory here if we require it.
     */
    if (type != 0) {
	sockunion_t *sa;
	struct rt_msghdr *rtm;
#ifdef IPV6_DLL
	struct in6_addr nh;
#else
	struct in_addr nh;
#endif
	int maxmsgsize;

	maxmsgsize = sizeof(struct rt_msghdr) + (sizeof(sockunion_t) * 3);
	rtm = malloc(maxmsgsize);
	ZeroMemory(rtm, maxmsgsize);

	sa = (sockunion_t *)(rtm + 1);

 	rtm->rtm_msglen = maxmsgsize - sizeof(*sa);
 	rtm->rtm_version = RTM_VERSION;
 	rtm->rtm_type = type;
 	rtm->rtm_addrs = RTA_DST | RTA_NETMASK;

	/* Destination */
#ifdef IPV6_DLL
	sa->sin6.sin6_family = AF_INET6;
	sa->sin6.sin6_addr = dst;
#else
	sa->sin.sin_family = AF_INET;
	sa->sin.sin_addr = dst;
#endif

	/*
	 * Route additions require that we also report the next-hop.
	 * Perform the necessary RTMv2 incantations to look up the
	 * next-hop from the destination reported as changed.
	 * XXX: Better error checking here considered desirable.
	 */
	if (type == RTM_ADD) {
	    PRTM_ROUTE_INFO prri;
	    RTM_NEXTHOP_INFO nhi;

	    rtm->rtm_msglen += sizeof(*sa);
	    rtm->rtm_addrs |= RTA_GATEWAY;

	    /* XXX weird heap malloc. */
	    MALLOC(&prri,
RTM_SIZE_OF_ROUTE_INFO(g_ce.rrpRtmProfile.MaxNextHopsInRoute), &result);

	    result = RtmGetRouteInfo(reh, prdi->ViewInfo[i].Route, prri, NULL);
	    if (result != NO_ERROR) {
		TRACE1(NETWORK, "RtmGetRouteInfo() returns %d", result);
	    }

	    result = RtmGetNextHopInfo(reh, prri->NextHopsList.NextHops[0],
				       &nhi);
	    if (result != NO_ERROR) {
		TRACE1(ANY, "Error %u getting next hop", result);
	    }

	    /* Gateway */
#ifdef IPV6_DLL
	    RTM_IPV6_GET_ADDR_AND_LEN(nhip.s6_addr, nhprefix,
				      &nhi.NextHopAddress);
	    ++sa;
	    sa->sin6.sin6_family = AF_INET6;
	    sa->sin6.sin6_addr = nhip;
#else
	    RTM_IPV4_GET_ADDR_AND_LEN(nhip.s_addr, nhprefix,
				      &nhi.NextHopAddress);
	    ++sa;
	    sa->sin.sin_family = AF_INET;
	    sa->sin.sin_addr = nhip;
#endif /* IPV6_DLL */

	    /*
	     * Free the next-hop info structures.
	     */
	    (void)RtmReleaseNextHopInfo(reh, &nhi);
	    (void)RtmReleaseRouteInfo(reh, prri);
	    FREE(prri);
	}

	/* Netmask; comes after gateway in the RTM_ADD case. */
	++sa;
#ifdef IPV6_DLL
	/* XXX: may not be right */
	sa->sin6.sin6_family = AF_INET;
	sa->sin6.sin6_addr.s6_addr = RTM_IPV6_MASK_FROM_LEN(dstprefix);
#else
	sa->sin.sin_family = AF_INET;
	sa->sin.sin_addr.s_addr = RTM_IPV4_MASK_FROM_LEN(dstprefix);
#endif

	broadcast_pipe_message(rtm, rtm->rtm_msglen);
	free(rtm);
    }

    return NO_ERROR;    
}

/*
 * Send a message to all connected listeners.
 *
 * XXX: The write blocks the current thread. Because RRAS threads
 * never enter alertable wait state, we can't use WriteFileEx().
 * We must either block or do the additional accounting for overlapped
 * WriteFile().
 * This has a very important consequence: our thread blocks until the
 * client thread reads its data or the pipe is disconnected.
 */
void
broadcast_pipe_message(void *msg, int msgsize)
{
    pipe_instance_t *pp;
    int i;
    int result;
    int nbytes;

    for (i = 0; i < PIPE_INSTANCES; i++) {
	pp = g_ce.pipes[i];
	if (pp != NULL && pp->state == PIPE_STATE_CONNECTED) {
	    result = WriteFile(pp->pipe, msg, msgsize, &nbytes, NULL);
	    if (result == 0) {
		result = GetLastError();
		TRACE1(NETWORK, "broadcast: write error %d", result);
		if (result == ERROR_PIPE_NOT_CONNECTED ||
		    result == ERROR_NO_DATA ||
		    result == ERROR_BROKEN_PIPE) {
		    TRACE1(NETWORK,
"broadcast: pipe %p disconnected; reconnecting.", pp->pipe);
		    /*
		     * We may be called by a reader thread. To avoid
		     * introducing loops, we schedule the listen
		     * operation on another thread.
		     */
		    ResetEvent(pp->revent);
		    QueueUserWorkItem(
(LPTHREAD_START_ROUTINE)pipe_relisten_cb, (PVOID)pp, WT_EXECUTEINIOTHREAD);
		}
	    }
	}
    }
}


DWORD
ProcessRouteChange (VOID)
{
    DWORD           dwErr           = NO_ERROR;
    RTM_DEST_INFO   rdiDestination;             /* 1 view registered for change */
    BOOL            bDone           = FALSE;
    UINT            uiNumDests;
    
    if (!ENTER_XORPRTM_API()) { return ERROR_CAN_NOT_COMPLETE; }

    /* loop dequeueing messages until RTM says there are no more left */
    while (!bDone)
    {
        /* retrieve route changes */
        uiNumDests = 1;
        dwErr = RtmGetChangedDests(
            g_ce.hRtmHandle,                    /* my RTMv2 handle  */
            g_ce.hRtmNotificationHandle,        /* my notification handle  */
            &uiNumDests,                        /*   # dest info's required */
                                                /* g # dest info's supplied */
            &rdiDestination);                   /* g buffer for dest info's */

        switch (dwErr)
        {
            case ERROR_NO_MORE_ITEMS:
                bDone = TRUE;
                dwErr = NO_ERROR;
                if (uiNumDests < 1)
                    break;
                /* else continue below to process the last destination */

/* XXX: Does not specify what the change(s) are, just that they */
/* occurred, on *this destination*. maybe we should figure */
/* this out? */

            case NO_ERROR:
                rtm_send_dest_change(g_ce.hRtmHandle, &rdiDestination);
                
                /* release the destination info */
                if (RtmReleaseChangedDests(
                    g_ce.hRtmHandle,            /* my RTMv2 handle  */
                    g_ce.hRtmNotificationHandle,/* my notif handle  */
                    uiNumDests,                 /* 1 */
                    &rdiDestination             /* released dest info */
                    ) != NO_ERROR)
                    TRACE0(NETWORK, "Error releasing changed dests");

                break;

            default:
                bDone = TRUE;
                TRACE1(NETWORK, "Error %u RtmGetChangedDests", dwErr);
                break;
        }
    } /* while  */
    
    LEAVE_XORPRTM_API();

    return dwErr;
}


/*
 * Where we get called by RTMv2 when things happen to the routing table.
 */
DWORD
APIENTRY
RTM_CallbackEvent (
     RTM_ENTITY_HANDLE   hRtmHandle, /* registration handle */
     RTM_EVENT_TYPE      retEvent,
     PVOID               pvContext1,
     PVOID               pvContext2)
{
    DWORD dwErr = NO_ERROR;

    TRACE1(ENTER, "Entering RTM_CallbackEvent: %u", retEvent);

    do                          /* breakout loop */
    {
        UNREFERENCED_PARAMETER(hRtmHandle);
        UNREFERENCED_PARAMETER(pvContext1);
        UNREFERENCED_PARAMETER(pvContext2);

        /* only route change notifications are processed */
        if (retEvent != RTM_CHANGE_NOTIFICATION)
        {
            dwErr = ERROR_NOT_SUPPORTED;
            break;
        }

        dwErr = ProcessRouteChange();
    } while (FALSE);

    TRACE0(LEAVE, "Leaving  RTM_CallbackEvent");

    return dwErr;
}


/*
 * Create a new instance of a pipe and return a pointer to
 * its instance structure.
 */
pipe_instance_t *
pipe_new(void)
{
    pipe_instance_t *npp;
    int failed;
    DWORD result;

    TRACE0(ENTER, "Entering pipe_new");

    npp = malloc(sizeof(*npp));
    if (npp == NULL)
	return NULL;
    ZeroMemory(npp, sizeof(*npp));

    failed = 1;

    /* XXX buffer management */
    npp->rsize = PIPE_READBUF_SIZE; 
    npp->state = PIPE_STATE_INIT;

    InitializeCriticalSection(&npp->rcs);

    /*
     * Create the event object used to signal connection completion.
     */
    npp->cevent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (npp->cevent == NULL) {
	result = GetLastError();
        TRACE1(CONFIGURATION, "Error %u creating event", result);
	goto fail;
    }
    npp->cov.hEvent = npp->cevent;

    /*
     * Create the event object used to signal read completion.
     */
    npp->revent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (npp->revent == NULL) {
	result = GetLastError();
        TRACE1(CONFIGURATION, "Error %u creating event", result);
	goto fail;
    }
    npp->rov.hEvent = npp->revent;

    /*
     * Create the instance of the named pipe itself.
     */
    npp->pipe = CreateNamedPipeA(XORPRTM_PIPENAME,
				 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
				 PIPE_TYPE_MESSAGE, PIPE_INSTANCES, 0, 0,
				 XORPRTM_PIPETIMEOUT, NULL);
    if (npp->pipe == NULL) {
	result = GetLastError();
	TRACE1(CONFIGURATION, "Error %u creating named pipe", result);
	goto fail;
    }

    failed = 0;
fail:
    if (failed) {
	pipe_destroy(npp);
	npp = NULL;
    }
    TRACE1(ENTER, "Leaving pipe_new %p", npp);
    return (npp);
}

/*
 * XXX: This must be called from primary thread, or lock held if not!
 */
int
pipe_listen(pipe_instance_t *pp)
{
    int retval;
    DWORD result;

    retval = -1;

    TRACE1(ENTER, "Entering pipe_listen %p", pp);

    if (pp == NULL || pp->state != PIPE_STATE_INIT)
	return (retval);

    /*
     * Register a pool thread to wait for pipe connection.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -