RFC 7871 – EDNS Client Subnet (ECS)

这篇RFC主要介绍了如何使用EDNS0机制来传递DNS用户网络信息的方式。

当前一些DNS权威服务器可以根据用户的查询IP获得其大概的地理位置,从而提供用户更精准的解析信息,实现有效的基于地理位置的负载均衡处理。但是我们知道查询的大部分来源可能是用户配置的递归服务器地址,如果是运营商本地的网络,则还好一些,但是如果是网络上的开放递归比如1.1.1.1或8.8.8.8则差距较大,实际返回给用户的可能是距离较远的服务器地址,甚至有一些运营商也会构建一些中心化的DNS递归解析服务器,导致用户查询的内容也不是距离最近的结果。

在RFC6891中定义了EDNS0扩展机制,使得DNS消息体可以传递更多自定义的数据类型,ClientSubnet就是通过EDNS0中定义的来实现了其传递用户网络信息的方式。作为EDNS option中的数据,即便是服务器无法处理,也不会影响正常的DNS数据解析(只是无法使用这些扩展功能)。

1. 消息体格式

实施ECS的数据定义如下,使用EDNS扩展的Option字段定义整个的数据结构。IANA 分配Option-Code为8,代表该消息体为ECS消息体,后续读取数据包的时候,根据以下的结构体进行拆分数据。

                +0 (MSB)                            +1 (LSB)
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  0: |                          OPTION-CODE                          |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  2: |                         OPTION-LENGTH                         |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  4: |                            FAMILY                             |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  6: |     SOURCE PREFIX-LENGTH      |     SCOPE PREFIX-LENGTH       |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  8: |                           ADDRESS...                          /
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

Option-Length代表了消息体的大小,从Length之后所有的数据的长度(按字节计算)。

Family代表了地址所属的类别,一般为IPv4或者IPv6,

0 Reserved    
1 IP (IP version 4)
2 IP6 (IP version 6)
3 NSAP
4 HDLC (8-bit multidrop)
5 BBN 1822
6 802 (includes all 802 media plus Ethernet "canonical format")
7 E.163
8 E.164 (SMDS, Frame Relay, ATM)
9 F.69 (Telex)
10 X.121 (X.25, Frame Relay)
...

详细的可参考如下的IANA的定义规范。

https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml

因此根据不同的Famliy,来解码不同的消息体,这里以IPv4和IPv6来解释如何使用,

  • SOURCE PREFIX-LENGTH : 代表了查询数据中,定义的Address最左端的起始位置,也就是地址的块大小,比如24位的100.100.100.0/24,或者16位的10.10.0.0/16,用于提供数据的读取
  • SCOPE PREFIX-LENGTH : 代表响应消息中代表最左端的Address起始位置, 因此查询消息中直接设置为0
  • Address, 可变的一组数据,包含IPv4或者IPv6地址信息、并按照SOURCE PREFIX-LENGTH的长度进行切分,补0操作。

2. 协议实施

2.1 DNS查询

ECS消息一般通过递归服务器设置,增加到消息体,用于查询权威的域名服务器, 同时客户端和Forward服务器也可以直接增加对应的ECS信息到数据包中。比如下面是使用dig来执行ECS查询的方式(比较老的dig版本可能不支持该参数)

root@localhost:~# dig @ns1.google.com google.com +subnet=218.241.108.0/24 +short
172.217.160.78
root@localhost:~# dig @ns1.google.com google.com +subnet=208.67.222.0/24 +short
172.217.17.142

对于支持ECS的递归服务器,如果用户查询的时候本身不带ECS信息,则递归服务器通过获取用户的查询IP,并设置对应的Famliy类别,同时使用本身服务器设置的最大缓存IP地址块大小来决定其SOURCE PREFIX-LENGTH字段。因此发送的可能是24位的地址块,或者30位等等。

如果客户端查询本身自带了ECS,则服务器检查其SOURCE PREFIX-LENGTH, 递归服务器在其外发的时候,必须小于该客户端自定义的地址块长度,或满足服务器本身的最大的缓存长度限制。也就是说如果服务器设置最大的为24的地址块,如果客户端发送的30的地址块,则按照服务器的设置来发送。

对于Address字段,可以仅仅满足其SOURCE PREFIX-LENGTH长度即可,无需设置全部的长度,比如本身IPv4为32位,如果设置了SOURCE PREFIX-LENGTH为24,可以仅仅给出24位的Address即可。

用户的DNS客户端,在发送DNS查询的时候,可以自己定义其ECS数据,设置对应的子网信息,并发送给递归服务器。就像之前使用dig的方式。

如果客户端在设置的时候,设置SOURCE PREFIX-LENGTH为0,代表了递归服务器不需要增加客户端相关的地址信息到查询中。后续的递归请求,要么不使用ECS,要么使用递归服务器自己的地址信息作为查询。该信息缓存后,可以被任何后续的查询SOURCE PREFIX-LENGTH=0的DNS查询(同域名和类型下)使用。

查询信息的ECS中SCOPE PREFIX-LENGTH 必须设置为0

对于Forward服务器,递归服务器将其作为客户端对待即可。如果需要设置ECS信息,也需要遵循所有之前定义的数据规范。

2.2 DNS响应

对于支持ECS的权威服务器接收到查询后,根据ECS选项确定适当的DNS响应信息,客户端查询中包含ECS数据,响应中也需要包含ECS数据,如果查询中不包含ECS数据,即便是权威可以提供定制化的解析数据,也无需添加对应的ECS消息。

如果当前权威服务器不支持ECS,可以简单的忽略该信息。不影响正常原有的的DNS解析处理。而对于处于中间层的递归或者Forward服务器,如果遇到没有实施这些的权威应答,可以简单的设置 SCOPE PREFIX-LENGTH为0代表该消息为全网适用的数据。

SCOPE PREFIX-LENGTH 代表了响应数据中:

  • 当响应中的SCOPE PREFIX-LENGTH值相对查询的SCOPE PREFIX-LENGTH更长的时候,意味者查询提供的长度过于宽泛,后续的查询中建议使用更长的网络,比如原来查询的为16的,响应中却设置为24,代表将来查询的时候可以用24的作为地址块进行查询。具体的递归服务器是否遵循建议,依赖于不同的实施(有些可能会考虑其资源消耗或隐私等原因)
  • 当响应中的SCOPE PREFIX-LENGTH值相对查询的SCOPE PREFIX-LENGTH更短的时候,意味者查询提供的长度过于精细, 服务器只能提供粗粒度的范围响应。当为0的时候,代表数据为全网适用的。

如果查询的类型,比如Any类型,返回多组RRSet,每组都有自己的SCOPE PREFIX-LENGTH,但是协议仅仅支持设置一个,因此按照最长的块来确定。

权威服务器在确定响应的时候,需要关注地址段重叠的问题,比如下面的例子,如果服务器针对1.2.0/20的响应为A, 而针对1.2.3/24的响应为B, 那么实际查询的时候,所有1.2.0/23, 1.2.2/24, 1.2.4/22, 和 1.2.8/21都会返回A的信息,而1.2.3/24返回 B的地址,对于大范围的地址段进行拆分成多个小的段.

中间的DNS传递环节,如果接收到更细粒度的响应的时候,可以以更细的响应的

2.3 如何处理缓存

递归服务器如果获取到的数据中包含ECS数据,且TC字段未设置,应该缓存该数据。对于设置了TC字段的则使用TCP进行重试。

如果接收到的响应中SOURCE PREFIX-LENGTH不为0,则应该丢弃该数据。

当接收到的消息不包含ECS信息,则SCOPE PREFIX-LENGTH设置为0, 代表其适用于全网的解析信息。

如果收到REFUSED的消息,则尝试不使用ECS信息后重发,但是无法确定是否由于ECS的原因导致

缓存的数据除了与查询的域名和类型有关系,还将绑定上对应的网络范围。该值与服务器配置的最大缓存块长度,以及下面字段有关系。

SOURCE PREFIX-LENGTH
SCOPE PREFIX-LENGTH
  • 当SCOPE PREFIX-LENGTH < SOURCE PREFIX-LENGTH , 存储宽范围的网络(SCOPE由服务器设置,代表服务器也无法处理较细粒度的切分)标记此响应对于所有该宽范围网络内有效。
  • 如果SOURCE PREFIX-LENGTH是最大配置的缓存长度,存储SOURCE PREFIX-LENGTH 作为有效的网络地址范围。
  • 如果SOURCE PREFIX-LENGTH 比配置的最大缓存长度小,并且SOURCE PREFIX-LENGTH 比 SCOPE PREFIX-LENGTH夜宵。存储 SCOPE PREFIX-LENGTH作为仅仅针对该段范围内的查询有效(代表客户端无需更细力度的切分)

对于Additinal和Authority中的信息,无需与指定的网络进行绑定缓存

有两类缓存需要进行区分对待:

  • 权威服务器设置的SCOPE PREFIX-LENGTH=0 , 缓存的数据为/0代表适用于所有网络的解析结果
  • 由于查询设置的SOURCE PREFIX-LENGTH =0 ,导致的缓存的数据为/0,仅仅适用于其他查询为/0的结果

实施ECS将导致显著的缓存大小的增加,增加服务器的负担,因此对于缓存的数据结构设计好坏变得尤为重要。

缓存查询的时候,先按照<name, type, class>的信息,进行匹配查询,查询后依赖于网络范围进行查找,具体的流程可以按照如下的方式实施:

  • 如果ECS信息没有提供,则根据客户端地址进行查询
  • 如果存在ECS信息,则根据SOURCE PREFIX-LENGTH和Address来确定用户的范围,当用户的地址处于SOURCE PREFIX-LENGTH和Address范围内的时候,直接使用客户端地址同上面的条件一流程相同。
  • 当用户设置的SOURCE PREFIX-LENGTH和Address范围与用户的地址不一致的时候,且SOURCE PREFIX-LENGTH相对最大缓存网络块长度短的时候,使用该值作为查询条件。
  • 可以根据策略针对不同的地址设置访问权限。
  • 当缓存数据不存在的时候,或没有适合用户地址范围的结果被找到的时候,需要启动权威查询。

对于否定应答,权威服务器应当默认,所有的递归服务器默认会按照/0的网络段设置否定应答,因此,应当设置有效的SCOPE PREFIX-LENGTH来响应消息。

对于Additinal中的地址,应当忽略其ECS数据,权威服务器在响应该消息的时候应当设置一个值为0的SCOPE PREFIX-Length.

对于中间服务器,传递DNS消息的时候,根据其具体的实施而略有差异,但是如果新增ECS信息,则可以按照客户端地址设置,覆盖客户端的地址范围。如果接收到任何SOURCE-PREFIX-LENGTH为0的,不应该包含任何客户端的地址信息。

如果任何中间服务器不想处理带有ECS消息的请求,应该丢弃该请求,并返回REFUSED消息。

3. 其他相关

关于DNSSEC, OPT资源记录本身不会被签名,使用ECS也不会对于原有的DNSSEC机制产生任何影响,但是使用该选项,的确会导致更多的DNS查询流量的产生。

大部分的DNSSEC记录其范围应该设置为/0, 除了RRSIG记录信息,应该与具体的RR绑定, 同时对NSEC和NSEC3的应答也应该是与网络无关的响应信息。

同时NAT后面的服务器在使用ECS的时候也需要设置合理的网络范围,或者直接留给外部的服务器去处理该信息。

隐私问题

使用ECS信息,可以导致客户端信息暴露给所有解析流程中的服务器,同时中间网路中的数据分析也会获悉该数据,为了保证用户隐私,递归服务器应该设置用户至少为24的块大小范围,以及针对IPv6的56的大小范围。

对于运营商,可能会设置更大的块范围,用于提升缓存利用率以及降低隐私问题。

客户端可以设置SOURCE PREFIX-LENGTH为0代表了隐藏自己的Client地址。

当然对于任何不实施ECS的,网络流量分析也会捕获用户的相关查询,并且包含全部的用户IP信息。同时权威也可以直接根据Ip来响应适当的信息。

4. 实例

整个的查询流程实例如下面所示:

  • 客户端地址2001:0db8:fd13:4231:2112:8a2e:c37b:7334尝试解析地址www.example.com
  • 递归服务器支持ECS, 检查缓存中是否已经存在该记录
  • 递归服务器构建一个查询到根服务器和com服务器查询结果,同时递归本身可以配置不去顶级域中查询的时候携带任何的ECS信息。
  • 递归服务器获取到example.com的NS地址。
  • 递归服务器查询www.example.com, 并携带必要的opt信息
    • 设置option-code=8
    • 设置option-length长度为 0x00 0x0b(4字节+7个字节的Address)
    • Famliy设置为0x00 0x02作为IPv6地址信息。
    • 递归服务器设置SOURCE PREFIX-LENGTH为0x38,也就是针对IPv6的块大小可以用56位块查询, 本身设置的最小为72(依赖于不同的实现而不同)。
    • 设置SCOPE PREFIX LENGTH为0
    • 地址设置0x20 0x01 0x0d 0xb8 0xfd 0x13 0x42, 也就是56位长度。
  • 权威接收到消息,并返回具体的地址信息, 增加必要的option信息到DNS消息体中:
    • 设置option-code=8
    • 设置option-length长度为 0x00 0x07(4字节+7个字节的Address)
    • Famliy设置为0x00 0x02作为IPv6地址信息。
    • SOURCE PREFIX-LENGTH为0x38, SCOPE PREFIX-LENGTH设置为0x30对应一个48位的地址(依赖于不同的实现而不同)
    • 地址设置为0x20 0x01 0x0d 0xb8 0xfd 0x13 0x42,也和查询的一致
  • 递归服务器接收到响应,验证所有字段是否与查询一致。
  • 响应中的ECS信息会被缓存下来作为后面查询使用
  • 递归响应给服务器DNS消息,无需设置option信息
  • 当递归服务器获得新的查询的时候,
    • 如果地址范围一致则直接响应,如果存在多个匹配结果,则最长的SCOPE PREFIX-LENGTH数据被选择
    • 否则启动新的查询,获取信息。

发表评论

邮箱地址不会被公开。 必填项已用*标注