UDPプロキシはFullcone NATを実現する

NATの種類

Mappingの種類

マッピングはNAT内部から外部にパケットを送信時にアドレス変換する行為です。

  • Endpoint-Independent Mapping

NAT内部のネットワークの同じIPアドレスとポートで外部のそれぞれのUDPサーバ(IPアドレスとポートが違う)にパケットを送信すると、各個UDPサーバも同じIPアドレスとポートのパケットを受信する。
例えば:

送信元アドレス宛先アドレスNATマッピングアドレス
192.168.1.1:30001.1.1.1:300043.124.222.234:4500
192.168.1.1:30001.1.1.1:300143.124.222.234:4500
192.168.1.1:30001.1.1.3:300243.124.222.234:4500
192.168.1.1:30001.1.1.4:300443.124.222.234:4500
  • Address-Dependent Mapping

UDPサーバのアドレスが同じ(ポートが違う)なら受信したパケットのIPアドレスとポートを変換しない、そうしないと、IPアドレスやポートを変換する。
例えば:

送信元アドレス宛先アドレスNATマッピングアドレス
192.168.1.1:30001.1.1.1:300043.124.222.234:4500
192.168.1.1:30001.1.1.1:300143.124.222.234:4500
192.168.1.1:30001.1.1.2:300043.124.222.234:4501
192.168.1.1:30001.1.1.3:300343.124.222.234:4502
  • Address and Port-Dependent Mapping

UDPサーバのアドレスとポートが同じなら受信したパケットのIPアドレスとポートを変換しない、そうしないと、IPアドレスやポートを変換する。

例えば:

送信元アドレス宛先アドレスNATマッピングアドレス
192.168.1.1:30001.1.1.1:300043.124.222.234:4500
192.168.1.1:30001.1.1.1:300043.124.222.234:4500
192.168.1.1:30001.1.1.1:300143.124.222.234:4501
192.168.1.1:30001.1.1.2:300043.124.222.234:4502
192.168.1.1:30001.1.1.3:300343.124.222.234:4503

Filteringの種類

フィルタリングはNAT外部から内部に送信するパケットをフィルタリングする行為です。

  • Endpoint-Independent Filtering

一旦内部アドレスを外部にマッピングすると、全ての外部サーバから内部に送信することが可能です。
例えば:

内部アドレスマッピング
192.168.1.1:300043.124.222.234:4500
送信元アドレス宛先アドレス受信する?
1.1.1.1:300043.124.222.234:4500はい 🟢
1.1.1.1:300143.124.222.234:4500はい 🟢
1.1.1.2:300043.124.222.234:4500はい 🟢
1.1.1.3:300243.124.222.234:4500はい 🟢
  • Address-Dependent Filtering

内部から外部に送信したサーバ、且つアドレスも同じなら、サーバから内部に送信することが可能です。
例えば:

内部アドレスマッピング送信歴史 宛先アドレス
192.168.1.1:300043.124.222.234:45001.1.1.1:3000
送信元アドレス宛先アドレス受信する?
1.1.1.1:300043.124.222.234:4500はい 🟢
1.1.1.1:300143.124.222.234:4500はい 🟢
1.1.1.2:300043.124.222.234:4500ダメ ❌
1.1.1.3:300243.124.222.234:4500ダメ ❌
  • Address and Port-Dependent Filtering

内部から外部に送信したサーバ、且つアドレスとポートも同じなら、サーバから内部に送信することが可能です。

例えば:

内部アドレスマッピング送信歴史 宛先アドレス
192.168.1.1:300043.124.222.234:45001.1.1.1:3000
送信元アドレス宛先アドレス受信する?
1.1.1.1:300043.124.222.234:4500はい 🟢
1.1.1.1:300143.124.222.234:4500ダメ ❌
1.1.1.2:300043.124.222.234:4500ダメ ❌
1.1.1.3:300243.124.222.234:4500ダメ ❌

Fullcone NAT

Fullcone NATはEndpoint-Independent FilteringとEndpoint-Independent Mappingです。

goで実現する

最も重要なのはNATテーブルの実現です。

// NATテーブル
// キーはプロキシクライアントの送信元アドレス
// 値はマッピングしたUDPサーバ
var natTable = map[string]net.PacketConn{}

var getTargetAddress = func([]byte) (*net.UDPAddr, []byte) {
  // TODO implement proxy protocol
  return nil, nil
 }

var packetResponse = func(net.Addr, []byte) []byte {
  // TODO implement proxy protocol
  return nil
}

// mockのUDPプロキシサーバ
pc, err := net.ListenPacket("udp", "")
if err != nil {
 panic(err)
}
defer pc.Close()

for {
  // IPプロトコルによってパケット最大は65535です。
  buf := make([]byte, 65535)
  n, addr, err := pc.ReadFrom(buf)
  if err != nil {
   panic(err)
  }

  // クライアントのアドレスでマッピングサーバを保存して
  conn, ok := natTable[addr.String()]
  if !ok {
   // マッピングサーバを作ります
   conn, err = net.ListenPacket("udp", "")
   if err != nil {
    panic(err)
   }

   go func() {
    defer conn.Close()
    for {
     buf := make([]byte, 65535)
     // リモートサーバ(target)の返信を受信する
     n, src, err := conn.ReadFrom(buf)
     if err != nil {
      panic(err)
     }
     // レスポンスデータをクライアントに返信して
     _, err = pc.WriteTo(packetResponse(src, buf[:n]), addr)
     if err != nil {
      panic(err)
     }
    }
   }()

   // 初めてマッピングサーバをNATテーブルに保存して
   natTable[addr.String()] = conn
  }

  target, remain := getTargetAddress(buf[:n])

  // リモートサーバにデータを送信して
  _, err = conn.WriteTo(remain, target)
  if err != nil {
   panic(err)
  }
}