Teredo 所需的防火墙例外

要使应用程序接收 Teredo 流量,必须允许应用程序接收主机防火墙中的 IPv6 流量,并且应用程序需要将套接字选项 IPV6_PROTECTION_LEVEL设置为 “PROTECTION_LEVEL_UNRESTRICTED”。 若要启用此类方案,必须实现本文档中详述的防火墙例外。

需要以下防火墙配置来确保防火墙与 Teredo 之间的顺利互操作:

  • 客户端防火墙必须允许解析 teredo.ipv6.microsoft.com。

  • UDP 端口 3544 必须打开,以确保 Teredo 客户端能够成功与 Teredo 服务器通信。

  • 防火墙必须通过调用 FwpmSystemPortsGet0 函数在本地计算机上检索 Teredo 服务使用的动态 UDP 端口;相关端口的类型为FWPM_SYSTEM_PORT_TEREDO。 应实现 FwpmSystemPortsGet0 函数,以取代现已弃用的 GetTeredoPortNotifyTeredoPortChange 函数。

  • 防火墙允许系统将 UDP/IPv4 数据包发送到本地子网上的 UDP 端口 1900,因为这允许 UPnP 发现流量流动,并有可能提高连接率。

    注意

    如果未满足此条件,则会引入涉及某些 NAT 类型之间通信的兼容性问题的方案:具体而言,在对称 NAT 和受限的 NAT 之间。 虽然对称 NAT 在热点中很受欢迎,但受限 NAT 在家庭中很受欢迎,但两者之间的通信有可能在受限 NAT 的一侧出错。

     

  • 必须启用传入和传出 ICMPv6“Echo 请求”和“回显回复”异常。 这些异常是必需的,以确保 Teredo 客户端可以充当 Teredo 主机特定的中继。 可通过附加本机 IPv6 地址或 Teredo 地址提供的 6to4 地址来标识 Teredo 主机特定的中继。

客户端防火墙必须支持每个 RFC 4443 的以下 ICMPv6 错误消息和发现功能:

代码 说明
135/136 ICMPV6 邻居请求和广告
133/134 路由器请求和播发
128/129 ICMPV6 回显请求和回复
1 目标不可访问
2 数据包太大
3 超出时间
4 参数无效

 

如果无法明确允许这些消息,则应在防火墙上启用所有 ICMPv6 消息的豁免。 此外,主机防火墙可能会注意到,由代码 135/136 或 133/134 分类的数据包源自用户模式服务 iphlpsvc ,而不是来自堆栈。 主机防火墙不得删除这些数据包。 Teredo 服务主要在“用户模式”IP 帮助程序服务中实现。

使用 INetFwPolicy2 Windows防火墙 API 枚举所有规则并设置了 Edge 遍历标志,所有想要侦听未请求流量的应用程序都会枚举防火墙例外。 有关使用边缘遍历选项的具体信息,请参阅 在 Teredo 上接收未经请求的流量

回调与以下示例枚举代码不关联;强烈建议第三方防火墙定期执行枚举,或每当防火墙检测到尝试通过防火墙的新应用程序时。

#include <windows.h>
#include <objbase.h>
#include <stdio.h>
#include <atlcomcli.h>
#include <strsafe.h>
#include <netfw.h>

#define NET_FW_IP_PROTOCOL_TCP_NAME L"TCP"
#define NET_FW_IP_PROTOCOL_UDP_NAME L"UDP"

#define NET_FW_RULE_DIR_IN_NAME L"In"
#define NET_FW_RULE_DIR_OUT_NAME L"Out"

#define NET_FW_RULE_ACTION_BLOCK_NAME L"Block"
#define NET_FW_RULE_ACTION_ALLOW_NAME L"Allow"

#define NET_FW_RULE_ENABLE_IN_NAME L"TRUE"
#define NET_FW_RULE_DISABLE_IN_NAME L"FALSE"

#import "netfw.tlb"

void DumpFWRulesInCollection(long Allprofiletypes, NetFwPublicTypeLib::INetFwRulePtr FwRule)
{
    variant_t InterfaceArray;
    variant_t InterfaceString;    

    if(FwRule->Profiles == Allprofiletypes)
    {
        wprintf(L"---------------------------------------------\n");
        wprintf(L"Name:             %s\n", (BSTR)FwRule->Name);        
        wprintf(L"Description:      %s\n", (BSTR)FwRule->Description);
        wprintf(L"Application Name: %s\n", (BSTR)FwRule->ApplicationName);
        wprintf(L"Service Name:     %s\n", (BSTR)FwRule->serviceName);

        switch(FwRule->Protocol)
        {
        case NET_FW_IP_PROTOCOL_TCP: wprintf(L"IP Protocol:      %s\n", NET_FW_IP_PROTOCOL_TCP_NAME);
            break;
        case NET_FW_IP_PROTOCOL_UDP: wprintf(L"IP Protocol:      %s\n", NET_FW_IP_PROTOCOL_UDP_NAME);
            break;
        default:
            break;
        }

        if(FwRule->Protocol != NET_FW_IP_VERSION_V4 && FwRule->Protocol != NET_FW_IP_VERSION_V6)
        {
            wprintf(L"Local Ports:      %s\n", (BSTR)FwRule->LocalPorts);
            wprintf(L"Remote Ports:     %s\n", (BSTR)FwRule->RemotePorts);
        }
        
        wprintf(L"LocalAddresses:   %s\n", (BSTR)FwRule->LocalAddresses);
        wprintf(L"RemoteAddresses:  %s\n", (BSTR)FwRule->RemoteAddresses);
        wprintf(L"Profile:          %d\n", Allprofiletypes);
        

        if(FwRule->Protocol == NET_FW_IP_VERSION_V4 || FwRule->Protocol == NET_FW_IP_VERSION_V6)
        {
            wprintf(L"ICMP TypeCode:    %s\n", (BSTR)FwRule->IcmpTypesAndCodes);
        }

        switch(FwRule->Direction)
        {
        case NET_FW_RULE_DIR_IN:
            wprintf(L"Direction:        %s\n", NET_FW_RULE_DIR_IN_NAME);
            break;
        case NET_FW_RULE_DIR_OUT:
            wprintf(L"Direction:        %s\n", NET_FW_RULE_DIR_OUT_NAME);
            break;
        default:
            break;
        }

        switch(FwRule->Action)
        {
        case NET_FW_ACTION_BLOCK:
            wprintf(L"Action:           %s\n", NET_FW_RULE_ACTION_BLOCK_NAME);
            break;
        case NET_FW_ACTION_ALLOW:
            wprintf(L"Action:           %s\n", NET_FW_RULE_ACTION_ALLOW_NAME);
            break;
        default:
            break;
        }
        
        InterfaceArray = FwRule->Interfaces;

        if(InterfaceArray.vt != VT_EMPTY)
        {
            SAFEARRAY    *pSa = NULL;
            long index = 0;

            pSa = InterfaceArray.parray;

            for(long index= pSa->rgsabound->lLbound; index < (long)pSa->rgsabound->cElements; index++)
            {
                SafeArrayGetElement(pSa, &index, &InterfaceString);
                wprintf(L"Interfaces:       %s\n", (BSTR)InterfaceString.bstrVal);
            }
        }
        wprintf(L"Interface Types:  %s\n", (BSTR)FwRule->InterfaceTypes);
        if(FwRule->Enabled)
        {
            wprintf(L"Enabled:          %s\n", NET_FW_RULE_ENABLE_IN_NAME);
        }
        else
        {
            wprintf(L"Enabled:          %s\n", NET_FW_RULE_DISABLE_IN_NAME);
        }
        wprintf(L"Grouping:         %s\n", (BSTR)FwRule->Grouping);
        wprintf(L"Edge:             %s\n", (BSTR)FwRule->EdgeTraversal);
    }
}

int __cdecl main()
{
    HRESULT hr;
    BOOL fComInitialized = FALSE;
    ULONG cFetched = 0; 
    CComVariant var;
    long Allprofiletypes = 0;

    try
    {
        IUnknownPtr pEnumerator = NULL;
        IEnumVARIANT* pVariant = NULL;
        NetFwPublicTypeLib::INetFwPolicy2Ptr sipFwPolicy2;

        //
        // Initialize the COM library on the current thread.
        //
        hr = CoInitialize(NULL);
        if (FAILED(hr))
        {
            _com_issue_error(hr);
        }
        fComInitialized = TRUE;
        
        hr = sipFwPolicy2.CreateInstance("HNetCfg.FwPolicy2");
        if (FAILED(hr))
        {
            _com_issue_error(hr);
        }
        Allprofiletypes = NET_FW_PROFILE2_ALL; // 0x7FFFFFFF
        
        printf("The number of rules in the Windows Firewall are %d\n", sipFwPolicy2->Rules->Count);

        pEnumerator = sipFwPolicy2->Rules->Get_NewEnum();

        if(pEnumerator)
        {
            hr = pEnumerator->QueryInterface(__uuidof(IEnumVARIANT), (void **) &pVariant);
        }

        while(SUCCEEDED(hr) && hr != S_FALSE)
        {        
            NetFwPublicTypeLib::INetFwRulePtr sipFwRule;

            var.Clear();
            hr = pVariant->Next(1, &var, &cFetched);
            if (S_FALSE != hr)
            {
                if (SUCCEEDED(hr))
                {
                    hr = var.ChangeType(VT_DISPATCH);
                }
                if (SUCCEEDED(hr))
                {
                    hr = (V_DISPATCH(&var))->QueryInterface(__uuidof(INetFwRule), reinterpret_cast<void**>(&sipFwRule));
                }

                if (SUCCEEDED(hr))
                {
                    DumpFWRulesInCollection(Allprofiletypes, sipFwRule);
                }
            }
        }
    }
    catch(_com_error& e)
    {
        printf ("Error. HRESULT message is: %s (0x%08lx)\n", e.ErrorMessage(), e.Error());
        if (e.ErrorInfo())
        {
            printf ("Description: %s\n", (char *)e.Description());
        }
    }
    if (fComInitialized)
    {
        CoUninitialize();
    }
    return 0;
}