Skip to content

--subnet sends unmasked address bits beyond the prefix length #88

Description

@toshi0806

現象

EDNS Client Subnet のアドレスが prefix でマスクされていない。dig はホスト部を 0 にして送るが、tdig は元のアドレスをそのまま送る。

# Tdig.CLI.parse_subnet_option("192.0.2.1/24")
{:edns_client_subnet,
 %{family: 1, client_subnet: {192, 0, 2, 1}, source_prefix: 24, scope_prefix: 0}}
#                             ^^^^^^^^^^^^^ 第 4 オクテットが残っている

dig 9.20.26 の実測(+qr で送信パケットを表示)。

$ dig +qr +subnet=192.0.2.1/24 ... example.com
; CLIENT-SUBNET: 192.0.2.0/24/0
                        ^ 0 にマスクされている

問題

RFC 7871 §6 は ADDRESS について、SOURCE PREFIX-LENGTH を超えるビットを 0 にすることを求めている。

Bits beyond the SOURCE PREFIX-LENGTH ... MUST be set to 0.

ECS は「上流の権威サーバに、応答の最適化に必要な最小限の位置情報だけを渡す」ための仕組みで、prefix を短く指定することが精度を落とす手段になっている。ホスト部を残して送ると、利用者が /24 と書いて隠したつもりの下位ビットがそのまま上流に渡る。プロトコル違反であると同時に、機能の目的そのものを損なう。

対応案

parse_subnet_option/1 で、クランプ後の source_prefix を使ってアドレスをマスクする。

  • IPv4: 4 オクテット、上位 source_prefix ビットを残して以降を 0
  • IPv6: 8 グループ(16 bit 単位)、同様

source_prefix が 0 の場合はアドレス全体が 0 になる(RFC 7871 が「位置情報を渡さない」意思表示として定めている形)。

検証観点

  • 192.0.2.1/24{192, 0, 2, 0}
  • 192.0.2.1/25{192, 0, 2, 0}(境界がオクテットに揃わない場合)
  • 192.0.2.129/25{192, 0, 2, 128}
  • 192.0.2.1/0{0, 0, 0, 0}
  • 2001:db8::1/32{0x2001, 0x0db8, 0, 0, 0, 0, 0, 0}
  • クランプとの組み合わせ(/999 は 32 に丸めた後にマスク=実質マスク無し)

dig +qr の出力と突き合わせて確認するのが確実。

経緯

#86(prefix 長の検証)の作業中に dig の送信パケットを実測して判明した。prefix の検証とは独立した別の不具合なので、PR #87 のスコープからは外した。

https://claude.ai/code/session_01YAjSJR67tWTvYDJLcQThbQ

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions