Windows下的OpenVPN怎么封装真实IP作为源地址

Windows上的OpenVPN如何封装真实IP作为源地址
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    16
struct 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_t
openvpn_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 int
openvpn_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 void
openvpn_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.使用unicode
2.不使用预编译头
3.定义def文件并编译输入
***********************************************/

/**************************************************
服务端的client-connect脚本中要有以下的代码:
#!/bin/bash
......
#获取连接而来的客户端的真实IP地址
real_addr=$(printenv trusted_ip)
#将这个真实IP地址推回客户端以供其获取
echo "push setenv-safe clientip $real_addr" >$1
***************************************************/