📄 xorprtm.c
字号:
/* -*- 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 + -