Windows上的OpenVPN如何封装真实IP作为源地址

来源:互联网 发布:网络发送短信 编辑:程序博客网 时间:2024/06/01 10:48
OpenVPN引入了一个虚拟的网段,进而每个节点都有一个虚拟的IP地址。为了在加入OpenVPN之后保证原有的网络拓扑不更改,必要的做法就是不让虚拟IP跑出VPN节点之外,也就是说虚拟IP地址不能在任何非VPN网段出现,因为在加入OpenVPN之前,根本就没有这些IP地址,现在加入了,也不应该有。或许很多的防火墙会把这种奇怪的地址直接屏蔽掉。
        然而当OpenVPN用于Windows平台的时候,问题就来了。由于Windows在发出数据包的时候,其源地址完全是根据路由选择而来的,人们无法干预源地址的选择,一旦数据包需要经由虚拟网卡发出,那么其源地址自然也就成了虚拟IP地址,这样为了保证网络拓扑的无改动,需要在服务端数据包从真实网卡路由出去的口子上作一个SNAT将源地址改回Windows的真实IP地址。这一次的SNAT导致了通信变成了单向的,也就是说只允许Windows客户端通过VPN发起访问,而其它网段无法通过VPN对Windows进行访问。要想将通信变为双向的,就需要再做一次DNAT,一个Windows客户端接入导致了服务端两条NAT规则的添加,这明显是一种修补策略,太撇脚了。
        如果能让通信从Windows客户端发起的时候就使用真实IP地址作为源地址,任何问题就解决了。也就是说,如果Windows的路由命令可以支持Linux的iproute2的src参数就好了:
ip route add 1.2.3.4/32 via $VPN服务端虚拟地址 dev tap0 src $客户端真实地址
遗憾的是,Windows不支持这个,即便是Windows 2008也不支持用户手工指定源IP地址。搞技术的碰到这个问题,最显然的想法就是解决它而不是避开它,目标只有一个,那就是不让虚拟IP地址流到VPN网络之外,并且不通过地址转换来做,因为我知道Linux的地址转换是基于流的,要配置就要配两条,很不爽,非Linux的也没有实验环境,最关键的,我就是Linux程序员,术业有专攻。解决这个问题的方法最直接的就是编写NDIS驱动,然而那样动作太大了,并且它只和OpenVPN(目前)有关,因此就想到使用OpenVPN的plugin,注册up事件来做,这是合理的。然而问题是怎么做,由于不熟悉Windows开发环境,也不能恋战,不知怎么突然就想到了LSP,那就顺着这个路子走下去吧,幸亏能走通,否则就又和去年折腾虚拟文件系统时那样一场悲剧了。我只知道LSP可以在标准的Winsock上添加自己的逻辑,就是个钩子链,具体的代码还是需要请教公司的Windows高手以及互联网上查找现有的代码,虽然有时我也浏览MSDN,还是没有百度来的快,对于这种代码,百度比google快。
        大概花了5个小时时间,代码调通了,并且很好的和OpenVPN联动,也就是实现了OpenVPN的plugin,如果一个去往1.2.3.4的包需要走虚拟网卡,本机的物理网卡IP地址11.22.33.44,那么可以完美实现数据包从虚拟网卡发出时,封装11.22.33.44作为源地址,而不是封装虚拟地址128.129.0.3。代码如下:
#include <ws2spi.h>#include <errno.h>#include <fstream>#include <list>#include <Sporder.h>        // 定义了WSCWriteProviderOrder函数#include <windows.h>#include <stdio.h>#pragma comment(lib, "Rpcrt4.lib")#pragma   comment(lib,"Ws2_32.lib")GUID filterguid = {0xd3c21122, 0x85e1, 0x48f3,{0x9a,0xb6,0x23,0xd9,0x0c,0x73,0x07,0xef}};LPWSAPROTOCOL_INFOW  ProtoInfo=NULL;WSPPROC_TABLE        NextProcTable;DWORD                ProtoInfoSize=0;int                  TotalProtos=0;using namespace std;//将socket与“是否bind源IP地址”进行关联typedef struct  {    SOCKET s;    BOOL bd;}sockets;list<sockets*> li;/*************************************************************************//*                            以下安装LSP                                *//* 代码来自:http://hi.baidu.com/hiliqirun/item/194805be5695c6462bebe3f0 *//*************************************************************************/GUID ProviderGuid = {0xd3c21122, 0x85e1, 0x48f3,{0x9a,0xb6,0x23,0xd9,0x0c,0x73,0x07,0xef}};LPWSAPROTOCOL_INFOW GetProvider(LPINT lpnTotalProtocols){    DWORD dwSize = 0;    int nError;    LPWSAPROTOCOL_INFOW pProtoInfo = NULL;    // 取得需要的长度    if(::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError) == SOCKET_ERROR){        if(nError != WSAENOBUFS)            return NULL;    }    pProtoInfo = (LPWSAPROTOCOL_INFOW)::GlobalAlloc(GPTR, dwSize);    *lpnTotalProtocols = ::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError);    return pProtoInfo;}void FreeProvider(LPWSAPROTOCOL_INFOW pProtoInfo){    ::GlobalFree(pProtoInfo);}BOOL InstallProvider(WCHAR *pwszPathName){    WCHAR wszLSPName[] = L"ZetsinLSP";    LPWSAPROTOCOL_INFOW pProtoInfo;    int nProtocols;    WSAPROTOCOL_INFOW OriginalProtocolInfo[3];    DWORD             dwOrigCatalogId[3];    int nArrayCount = 0;    DWORD dwLayeredCatalogId;        // 我们分层协议的目录ID号    int nError;    // 找到我们的下层协议,将信息放入数组中    // 枚举所有服务程序提供者    pProtoInfo = GetProvider(&nProtocols);    BOOL bFindUdp = FALSE;    BOOL bFindTcp = FALSE;    BOOL bFindRaw = FALSE;    for(int i=0; i<nProtocols; i++){        if(pProtoInfo[i].iAddressFamily == AF_INET){            if(!bFindUdp && pProtoInfo[i].iProtocol == IPPROTO_UDP){                memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));                OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =                    OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);                dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;                bFindUdp = TRUE;            }            if(!bFindTcp && pProtoInfo[i].iProtocol == IPPROTO_TCP){                memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));                OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =                    OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);                dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;                bFindTcp = TRUE;            }            if(!bFindRaw && pProtoInfo[i].iProtocol == IPPROTO_IP){                memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));                OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =                    OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);                dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;                bFindRaw = TRUE;            }        }    }    // 安装我们的分层协议,获取一个dwLayeredCatalogId    // 随便找一个下层协议的结构复制过来即可    WSAPROTOCOL_INFOW LayeredProtocolInfo;    memcpy(&LayeredProtocolInfo, &OriginalProtocolInfo[0], sizeof(WSAPROTOCOL_INFOW));    // 修改协议名称,类型,设置PFL_HIDDEN标志    wcscpy_s(LayeredProtocolInfo.szProtocol, wszLSPName);    LayeredProtocolInfo.ProtocolChain.ChainLen = LAYERED_PROTOCOL; // 0;    LayeredProtocolInfo.dwProviderFlags |= PFL_HIDDEN;    // 安装    if(::WSCInstallProvider(&ProviderGuid,        pwszPathName, &LayeredProtocolInfo, 1, &nError) == SOCKET_ERROR){        printf("%d", nError);        return FALSE;    }    // 重新枚举协议,获取分层协议的目录ID号    FreeProvider(pProtoInfo);    pProtoInfo = GetProvider(&nProtocols);    for(int i=0; i<nProtocols; i++){        if(memcmp(&pProtoInfo[i].ProviderId, &ProviderGuid, sizeof(ProviderGuid)) == 0){            dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;            break;        }    }    // 安装协议链    // 修改协议名称,类型    WCHAR wszChainName[WSAPROTOCOL_LEN + 1];    for(int i=0; i<nArrayCount; i++){        swprintf(wszChainName, L"%ws over %ws", wszLSPName, OriginalProtocolInfo[i].szProtocol);        wcscpy_s(OriginalProtocolInfo[i].szProtocol, wszChainName);        if(OriginalProtocolInfo[i].ProtocolChain.ChainLen == 1){            OriginalProtocolInfo[i].ProtocolChain.ChainEntries[1] = dwOrigCatalogId[i];        }        else{            for(int j = OriginalProtocolInfo[i].ProtocolChain.ChainLen; j>0; j--){                OriginalProtocolInfo[i].ProtocolChain.ChainEntries[j]                = OriginalProtocolInfo[i].ProtocolChain.ChainEntries[j-1];            }        }        OriginalProtocolInfo[i].ProtocolChain.ChainLen ++;        OriginalProtocolInfo[i].ProtocolChain.ChainEntries[0] = dwLayeredCatalogId;        }    // 获取一个Guid,安装之    GUID ProviderChainGuid;    if(::UuidCreate(&ProviderChainGuid) == RPC_S_OK){        if(::WSCInstallProvider(&ProviderChainGuid,            pwszPathName, OriginalProtocolInfo, nArrayCount, &nError) == SOCKET_ERROR){            return FALSE;            }    }    else        return FALSE;    // 重新排序Winsock目录,将我们的协议链提前    // 重新枚举安装的协议    FreeProvider(pProtoInfo);    pProtoInfo = GetProvider(&nProtocols);    PDWORD dwIds = (PDWORD)malloc(sizeof(DWORD) * nProtocols);    int nIndex = 0;    // 添加我们的协议链    for(int i=0; i<nProtocols; i++){        if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&            (pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId))            dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;    }    // 添加其它协议    for(int i=0; i<nProtocols; i++){        if((pProtoInfo[i].ProtocolChain.ChainLen <= 1) ||            (pProtoInfo[i].ProtocolChain.ChainEntries[0] != dwLayeredCatalogId))            dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;    }    // 重新排序Winsock目录    if((nError = ::WSCWriteProviderOrder(dwIds, nIndex)) != ERROR_SUCCESS){        return FALSE;    }    FreeProvider(pProtoInfo);    return TRUE;}BOOL RemoveProvider(){    LPWSAPROTOCOL_INFOW pProtoInfo;    int nProtocols;    DWORD dwLayeredCatalogId;    // 根据Guid取得分层协议的目录ID号    pProtoInfo = GetProvider(&nProtocols);    int nError;    int i;    for(i=0; i<nProtocols; i++){        if(memcmp(&ProviderGuid, &pProtoInfo[i].ProviderId, sizeof(ProviderGuid)) == 0){            dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;            break;        }    }    if(i < nProtocols){        // 移除协议链        for(i=0; i<nProtocols; i++){            if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&                (pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId)){                ::WSCDeinstallProvider(&pProtoInfo[i].ProviderId, &nError);            }        }        // 移除分层协议        ::WSCDeinstallProvider(&ProviderGuid, &nError);    }    else return FALSE;    return TRUE;}/**********************************************    以下支持此DLL成为一个OpenVPN的plugin***********************************************/#define MAX_IP_LEN    16struct plugin_context {    char real_addr[MAX_IP_LEN];    char vitrual_addr[MAX_IP_LEN];    char vitrual_gw[MAX_IP_LEN];    char vitrual_mask[MAX_IP_LEN];};//首先要定义一个共享节,因为该DLL作为OpenVPN的plugin的同时也要作为LSP存在//需要在两个DLL中共享这个数据#pragma data_seg (".shared")//数据一定要初始化struct plugin_context g_context = {0};TCHAR self[MAX_PATH] = {0};#pragma   data_seg()       #pragma comment(linker,  " /SECTION:.shared,RWS " )//这里只是一些个stub,具体以openvpn的header为准# define OPENVPN_EXPORT        //需要在.def文件中导出open,func,close函数typedef void *openvpn_plugin_handle_t;#define OPENVPN_PLUGIN_MASK(x) (1<<(x))#define OPENVPN_PLUGIN_UP                    0#define OPENVPN_PLUGIN_ROUTE_UP              2#define OPENVPN_PLUGIN_FUNC_SUCCESS  0#define OPENVPN_PLUGIN_FUNC_ERROR    1#define OPENVPN_PLUGIN_DOWN                  1//以下函数摘自openvpn的plugin的simple实例static const char *get_env (const char *name, const char *envp[]){    if (envp){        int i;        size_t namelen = strlen (name);        for (i = 0; envp[i]; ++i){            OutputDebugStringA(envp[i]);            if (!strncmp (envp[i], name, namelen)){                const char *cp = envp[i] + namelen;                if (*cp == '=')                    return cp + 1;            }        }    }    return NULL;}//openvpn的plugin的关键函数,以下的openvpn_plugin_open_v1,//openvpn_plugin_func_v1,openvpn_plugin_close_v1都是要导出的OPENVPN_EXPORT openvpn_plugin_handle_topenvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]){    struct plugin_context *context;    context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));    //up事件发生时获取环境变量    *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP);    //route_up事件发生时注册LSP。不能在open的时候加载lsp,因此此时虚拟IP地址还不确定呢    //所以必须在up事件或者route-up事件时加载,再此我选择了route-up而不是up,没有特殊原因    *type_mask |= OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP);    *type_mask |= OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN);    return (openvpn_plugin_handle_t) context;}OPENVPN_EXPORT intopenvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]){    struct plugin_context *context = (struct plugin_context *) handle;    int ret = OPENVPN_PLUGIN_FUNC_SUCCESS;    char buf[512] = {0};    switch (type) {        case OPENVPN_PLUGIN_UP:{            //获取实际IP地址,用于封装源地址            const char *real_addr = get_env ("OPENVPN_clientip", envp);            //获取虚拟IP地址,用于判断路由            const char *vitrual_addr = get_env ("ifconfig_local", envp);            const char *vitrual_gw = get_env ("route_vpn_gateway", envp);            const char *vitrual_mask = get_env ("ifconfig_netmask", envp);                        real_addr !=NULL?strncpy(context->real_addr, real_addr, strlen(real_addr)):NULL;            vitrual_addr != NULL?strncpy(context->vitrual_addr, vitrual_addr, strlen(vitrual_addr)):NULL;            vitrual_gw != NULL?strncpy(context->vitrual_gw, vitrual_gw, strlen(vitrual_gw)):NULL;            vitrual_mask != NULL?strncpy(context->vitrual_mask, vitrual_mask, strlen(vitrual_mask)):NULL;            memcpy(&g_context, context, sizeof(g_context));            ret = OPENVPN_PLUGIN_FUNC_SUCCESS;            break;        }        case OPENVPN_PLUGIN_ROUTE_UP:{            //服务端推送的路由起来的时候,加载LSP,这个LSP的dll和plugin的dll是同一个            TCHAR szPathName[MAX_PATH];            TCHAR* p;            if(::GetFullPathName(self, MAX_PATH, szPathName, &p) != 0) {                if(!InstallProvider(szPathName)) {                    ret = OPENVPN_PLUGIN_FUNC_ERROR;                }            } else {                ret = OPENVPN_PLUGIN_FUNC_ERROR;            }            break;        }        case OPENVPN_PLUGIN_DOWN:            //虚拟网卡down时,注销LSP            if(!RemoveProvider())                ret = OPENVPN_PLUGIN_FUNC_ERROR;            break;        default:            ret = OPENVPN_PLUGIN_FUNC_ERROR;            break;    }    return ret;}OPENVPN_EXPORT voidopenvpn_plugin_close_v1 (openvpn_plugin_handle_t handle){    struct plugin_context *context = (struct plugin_context *) handle;    free (context);}/****************************************************************************//*                            以下实现LSP                                        *//*    代码修改自:http://hi.baidu.com/hiliqirun/item/194805be5695c6462bebe3f0    *//****************************************************************************/BOOL GetLSP(){    int    errorcode;    ProtoInfo=NULL;    ProtoInfoSize=0;    TotalProtos=0;    if(WSCEnumProtocols(NULL,ProtoInfo,&ProtoInfoSize,&errorcode)==SOCKET_ERROR)        if(errorcode!=WSAENOBUFS)            return FALSE;    if((ProtoInfo=(LPWSAPROTOCOL_INFOW)GlobalAlloc(GPTR,ProtoInfoSize))==NULL)                     return FALSE;    if((TotalProtos=WSCEnumProtocols(NULL,ProtoInfo,&ProtoInfoSize,&errorcode))==SOCKET_ERROR)        return FALSE;    return TRUE;}void FreeLSP(){    GlobalFree(ProtoInfo);}unsigned long GetRealAddress(){    //从OpenVPN的up脚本中获取的用于连接OpenVPN服务器的本地网卡的真实IP    //更加简便的方式就是将这个DLL既作为LSP,又作为OpenVPN的plugin,注册up事件    //在up事件发生时,获取真实IP地址,通过全局变量传给GetRealAddress。    //up脚本/plugin中的获取方式:获取环境变量。Linux下的printenv在Windows下的对应物    //这里的地址是在plugin中取到的    char *addr = g_context.real_addr;    //......    return inet_addr(addr);}BOOL NeedBind(unsigned long addr, sockets *entry){    //根据目标地址来判断是否需要bind一个本地物理网卡地址    //当且仅当数据包要从Tap-win32发出,也就是路由到这个网卡    //时且本身没有bind任何IP地址时,数据包的源地址才会被被    //设置成虚拟IP,因此需要调用Windows的路由API来查找一下    //目标地址的路由。为了测试方便,一律认为除了广播以及虚拟    //网段本身的所有数据包都从虚拟网卡出去        unsigned long net = inet_addr(g_context.vitrual_mask) & inet_addr(g_context.vitrual_addr);    //GetIpForwardTable ......本来要根据目标地址addr查找系统路由表来决定是不是要    //强制bind一个本地物理网卡的IP的,只因对Windows路由API不熟悉以及人为WinAPI参数    //太繁琐,...留在以后实现    BOOL nd = ((addr & inet_addr(g_context.vitrual_mask)) == net);    BOOL route_res = (!nd) && (addr != 0xffffffff);    BOOL bd = entry->bd;    return route_res & !bd;}BOOL WINAPI DllMain(HINSTANCE hmodule,                    DWORD     reason,                    LPVOID    lpreserved){    TCHAR   processname[MAX_PATH];    if(reason==DLL_PROCESS_ATTACH) {        //得到DLL自己的路径        GetModuleFileName(hmodule,processname,MAX_PATH);        wcscpy(self, processname);        OutputDebugString(processname);    }    return TRUE;}//在原始调用的基础上添加一个链表插入SOCKET WINAPI WSPSocket(                        __in   int af,                        __in   int type,                        __in   int protocol,                        __in   LPWSAPROTOCOL_INFO lpProtocolInfo,                        __in   GROUP g,                        DWORD dwFlags,                        __out  LPINT lpErrno                        ){    SOCKET s;    sockets *entry = NULL;    s = NextProcTable.lpWSPSocket(af, type, protocol, lpProtocolInfo, g, dwFlags, lpErrno);    if (s > 0) {        //只要成功创建一个socket,即将其插入list,一起插入的还有它是否已经bind源IP地址        entry = (sockets*)calloc(1, sizeof(sockets));        entry->s = s;        entry->bd = FALSE;        li.push_front(entry);    }    return s;}//改写原始的connect逻辑,强行绑定int WSPAPI WSPConnect(                      SOCKET s,                      const struct sockaddr *name,                      int namelen,                      LPWSABUF lpCallerData,                      LPWSABUF lpCalleeData,                      LPQOS lpSQOS,                      LPQOS lpGQOS,                      LPINT lpErrno){    struct sockaddr_in new_name, *remote_name;    int ret = 0;    sockets *entry = NULL;    list<sockets*>::iterator it;    remote_name = (struct sockaddr_in *)name;    //找到相关的entry,目的是看它是否已经关联了源IP地址    for (it = li.begin(); it != li.end(); it++) {        entry = *it;        if (entry->s == s) {            break;        }    }    //如果还没有bind源IP地址,则为其bind一个本地物理网卡的地址    if (NeedBind(remote_name->sin_addr.s_addr, entry)) {        memset(&new_name, 0, sizeof(struct sockaddr_in));        new_name.sin_family=AF_INET;           new_name.sin_addr.s_addr= GetRealAddress();        new_name.sin_port=htons(0);        ret = NextProcTable.lpWSPBind(s, (struct sockaddr *)&new_name, sizeof(new_name), lpErrno);        if (ret == 0) {            //如果成功bind,则更新该entry的bd字段            entry->bd = TRUE;        }    }    return NextProcTable.lpWSPConnect(s, name, namelen,                        lpCallerData, lpCalleeData, lpSQOS,                        lpGQOS, lpErrno);}//改写原始的bind逻辑,将绑定0地址的bind行为改为延迟bind,将不帮定源地址的改为严格绑定物理网卡地址int WINAPI WSPBind(                   __in   SOCKET s,                   __in   const struct sockaddr *name,                   __in   int namelen,                   __out  LPINT lpErrno                   ){    int ret = 0;    sockets *entry = NULL;    list<sockets*>::iterator it;    //找到对应entry,目的是对其bd字段进行更新    for (it = li.begin(); it != li.end(); it++) {        entry = *it;        if (entry->s == s)            break;    }    //如果一个socket bind了一个非0地址,则直接放过    if (((sockaddr_in*)name)->sin_addr.s_addr == 0) {        //如果一个socket bind了一个0地址,则为其延迟bind一个本地物理网卡的地址        //由于此时还不知道目标地址是什么,无法确定是否需要bind本地物理网卡地址        //故而,更新bd字段后,直接返回0,以下的两行代码不需要        //sockaddr_in *new_name = (sockaddr_in*)name;        //new_name->sin_addr.s_addr = GetRealAddress();        entry->bd = FALSE;        return 0;    }    ret = NextProcTable.lpWSPBind(s, name, namelen, lpErrno);    //如果bind成果,则更新相应entry的bd字段    if (ret == 0)        entry->bd = TRUE;    return ret;}int WINAPI WSPSendTo(                     __in   SOCKET s,                     __in   LPWSABUF lpBuffers,                     __in   DWORD dwBufferCount,                     __out  LPDWORD lpNumberOfBytesSent,                     __in   DWORD dwFlags,                     __in   const struct sockaddr *lpTo,                     __in   int iTolen,                     __in   LPWSAOVERLAPPED lpOverlapped,                     __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,                     __in   LPWSATHREADID lpThreadId,                     __out  LPINT lpErrno                     ){    struct sockaddr_in new_name, *remote_name;    int ret = 0;    sockets *entry = NULL;    list<sockets*>::iterator it;    remote_name = (struct sockaddr_in *)lpTo;    //找到对应的entry,目的是处理诸如UDP之类没有bind直接发送的情形    //此类情形下,为了关联一个源IP地址,需要bind一个本地物理网卡地址    for (it = li.begin(); it != li.end(); it++) {        entry = *it;        if (entry->s == s)            break;    }    if (NeedBind(remote_name->sin_addr.s_addr, entry)) {        memset(&new_name, 0, sizeof(struct sockaddr_in));        new_name.sin_family=AF_INET;           new_name.sin_addr.s_addr= GetRealAddress();        new_name.sin_port=htons(0);        ret = NextProcTable.lpWSPBind(s, (struct sockaddr *)&new_name, sizeof(new_name), lpErrno);        if (ret == 0)            entry->bd = TRUE;    }    return NextProcTable.lpWSPSendTo(s, lpBuffers, dwBufferCount,                        lpNumberOfBytesSent, dwFlags, lpTo,                        iTolen, lpOverlapped, lpCompletionRoutine,                        lpThreadId, lpErrno);}int WINAPI WSPCloseSocket(                          SOCKET s,                          LPINT lpErrno                          ){    int ret = 0;    sockets *entry = NULL;    list<sockets*>::iterator it;    //找到对应的entry,目的是将其从list删除,并释放数据结构    for (it = li.begin(); it != li.end(); it++) {        entry = *it;        if (entry->s == s)            break;    }    li.remove(entry);    free(entry);    return NextProcTable.lpWSPCloseSocket(s, lpErrno);}int WSPAPI WSPStartup(                      WORD wversionrequested,                      LPWSPDATA         lpwspdata,                      LPWSAPROTOCOL_INFOW lpProtoInfo,                      WSPUPCALLTABLE upcalltable,                      LPWSPPROC_TABLE lpproctable                      ){    int           i;    int           errorcode;    int           filterpathlen;    DWORD         layerid=0;    DWORD         nextlayerid=0;    TCHAR         *filterpath;    HINSTANCE     hfilter;    LPWSPSTARTUP  wspstartupfunc=NULL;    if(lpProtoInfo->ProtocolChain.ChainLen<=1)        return FALSE;    GetLSP();    for(i=0;i<TotalProtos;i++){        if(memcmp(&ProtoInfo[i].ProviderId,&filterguid,sizeof(GUID))==0){            layerid=ProtoInfo[i].dwCatalogEntryId;            break;        }    }    for(i=0;i<lpProtoInfo->ProtocolChain.ChainLen;i++){        if(lpProtoInfo->ProtocolChain.ChainEntries[i]==layerid){            nextlayerid=lpProtoInfo->ProtocolChain.ChainEntries[i+1];            break;        }    }    filterpathlen=MAX_PATH;    filterpath=(TCHAR*)GlobalAlloc(GPTR,filterpathlen);      for(i=0;i<TotalProtos;i++) {        if(nextlayerid==ProtoInfo[i].dwCatalogEntryId) {            if(WSCGetProviderPath(&ProtoInfo[i].ProviderId,filterpath,&filterpathlen,&errorcode)==SOCKET_ERROR) {                return WSAEPROVIDERFAILEDINIT;            }            break;        }    }    if(!ExpandEnvironmentStrings(filterpath,filterpath,MAX_PATH))          return WSAEPROVIDERFAILEDINIT;    if((hfilter=LoadLibrary(filterpath))==NULL)        return WSAEPROVIDERFAILEDINIT;    if((wspstartupfunc=(LPWSPSTARTUP)GetProcAddress(hfilter,"WSPStartup"))==NULL)        return WSAEPROVIDERFAILEDINIT;    if((errorcode=wspstartupfunc(wversionrequested,lpwspdata,lpProtoInfo,upcalltable,lpproctable))!=ERROR_SUCCESS)          return errorcode;    NextProcTable=*lpproctable;// 保存原来的入口函数表        //仅下列几个socket函数需要被HOOK,因为这些都涉及到了源地址和目标地址的关系    lpproctable->lpWSPSendTo = WSPSendTo;    lpproctable->lpWSPBind = WSPBind;    lpproctable->lpWSPConnect = WSPConnect;    lpproctable->lpWSPSocket = WSPSocket;    lpproctable->lpWSPCloseSocket = WSPCloseSocket;    FreeLSP();    return 0;}/**********************************************            以下是本代码的def文件                 EXPORTS        WSPStartup        openvpn_plugin_open_v1        openvpn_plugin_func_v1        openvpn_plugin_close_v1    ***********************************************//**********************************************VS2005的编译要点:1.使用unicode2.不使用预编译头3.定义def文件并编译输入***********************************************//**************************************************服务端的client-connect脚本中要有以下的代码:#!/bin/bash......#获取连接而来的客户端的真实IP地址real_addr=$(printenv trusted_ip)#将这个真实IP地址推回客户端以供其获取echo "push setenv-safe clientip $real_addr" >$1***************************************************/