# RPKI RTR Server

默认运行平台：Ubuntu/Linux。

## 目录

- [协议与规范](#协议与规范)
  - [RTR](#rtr)
  - [SLURM](#slurm)
  - [CCR](#ccr)
- [RTR Server](#rtr-server)
  - [环境变量](#环境变量)
  - [说明](#说明)
- [快速启动](#快速启动)
  - [Docker 启动](#docker-启动)
  - [本地运行（推荐先用脚本）](#本地运行推荐先用脚本)
  - [本地手动运行（最小示例）](#本地手动运行最小示例)
- [CCR 输入说明](#ccr-输入说明)
- [Client 使用](#client-使用)
  - [作用与选择](#作用与选择)
  - [rtr_debug_client（本地）](#rtr_debug_client本地)
  - [rtr_debug_client（Docker）](#rtr_debug_clientdocker)
  - [rpki-rs-test-client（Docker）](#rpki-rs-test-clientdocker)
  - [FRR（Docker，黑盒客户端）](#frrdocker黑盒客户端)
- [RTR Debug Client](#rtr-debug-client)
  - [Client 启动示例](#client-启动示例)
- [存储模型与边界约束（多视图）](#存储模型与边界约束多视图)
  - [唯一写入口](#唯一写入口)
  - [恢复约束](#恢复约束)
  - [边界防回归测试](#边界防回归测试)
- [Deploy Layout](#deploy-layout)
  - [Quick Start](#quick-start)
  - [Compose 入口](#compose-入口)
  - [角色附加命令](#角色附加命令)

## 协议与规范

### RTR

- [RFC 6810: The Resource Public Key Infrastructure (RPKI) to Router Protocol](https://www.rfc-editor.org/rfc/rfc6810.html)
- [RFC 8210: The Resource Public Key Infrastructure (RPKI) to Router Protocol, Version 1](https://www.rfc-editor.org/rfc/rfc8210.html)
- [Draft: The Resource Public Key Infrastructure (RPKI) to Router Protocol, Version 2](https://www.ietf.org/archive/id/draft-ietf-sidrops-8210bis-25.html)

### SLURM

- [RFC 8416: Simplified Local Internet Number Resource Management with the RPKI (SLURM)](https://www.rfc-editor.org/rfc/rfc8416.html)
- [Draft: ASPA extensions for SLURM](https://www.ietf.org/archive/id/draft-ietf-sidrops-aspa-slurm-04.html)

### CCR

- [Draft: A Concise Cryptographic Representation of RPKI Signed Objects (CCR)](https://www.ietf.org/archive/id/draft-ietf-sidrops-rpki-ccr-02.html)

## RTR Server

RTR Server 运行时从 `CCR` 目录中扫描最新的 `.ccr` 文件作为输入源：

- `VRP`
- `VAP (Validated ASPA Payload) / ASPA`

相关实现位置：

- [`src/main.rs`](src/main.rs)
- [`src/source/ccr.rs`](src/source/ccr.rs)

### 环境变量

| 变量名 | 说明 | 默认值 | 示例 |
| --- | --- | --- | --- |
| `RPKI_RTR_ENABLE_TLS` | 是否额外启用 TLS 监听。支持 `true/false`、`1/0`、`yes/no`、`on/off`。 | `false` | `true` |
| `RPKI_RTR_TCP_ADDR` | TCP 监听地址。 | `0.0.0.0:323` | `0.0.0.0:323` |
| `RPKI_RTR_TLS_ADDR` | TLS 监听地址。 | `0.0.0.0:324` | `0.0.0.0:324` |
| `RPKI_RTR_DB_PATH` | RocksDB 路径。 | `./rtr-db` | `./rtr-db` |
| `RPKI_RTR_CCR_DIR` | CCR 目录路径；程序会扫描其中最新的 `.ccr` 文件。 | `./data` | `./data` |
| `RPKI_RTR_SLURM_DIR` | SLURM 目录路径；为空或未设置表示禁用 SLURM。 | `未设置（禁用）` | `./slurm` |
| `RPKI_RTR_TLS_CERT_PATH` | TLS 服务端证书路径。 | `./certs/server.crt` | `./certs/server-dns.crt` |
| `RPKI_RTR_TLS_KEY_PATH` | TLS 服务端私钥路径。 | `./certs/server.key` | `./certs/server-dns.key` |
| `RPKI_RTR_TLS_CLIENT_CA_PATH` | 用于校验 router 客户端证书的 CA 证书路径。 | `./certs/client-ca.crt` | `./certs/client-ca.crt` |
| `RPKI_RTR_ENABLE_SSH` | 是否额外启用进程内原生 SSH 监听。支持 `true/false`、`1/0`、`yes/no`、`on/off`。 | `false` | `true` |
| `RPKI_RTR_SSH_ADDR` | SSH 监听地址。 | `0.0.0.0:22` | `0.0.0.0:22` |
| `RPKI_RTR_SSH_PORT` | SSH 监听端口（仅覆盖 `RPKI_RTR_SSH_ADDR` 中的端口）。 | `22` | `2022` |
| `RPKI_RTR_SSH_HOST_KEY_PATH` | OpenSSH host 私钥路径。 | `./certs/ssh_host_ed25519_key` | `./certs/ssh_host_ed25519_key` |
| `RPKI_RTR_SSH_AUTHORIZED_KEYS_PATH` | 允许接入的 router 公钥列表（authorized_keys）。 | `./certs/rtr-authorized_keys` | `./certs/rtr-authorized_keys` |
| `RPKI_RTR_SSH_USERNAME` | SSH 用户名白名单。 | `rpki-rtr` | `rpki-rtr` |
| `RPKI_RTR_SSH_SUBSYSTEM_NAME` | SSH 子系统名称。 | `rpki-rtr` | `rpki-rtr` |
| `RPKI_RTR_SSH_PASSWORD` | 可选的 SSH password 认证口令。未设置时仅允许 publickey；设置后同时允许 publickey 与 password。 | `未设置` | `test-password` |
| `RPKI_RTR_MAX_DELTA` | 最多保留多少条 delta。 | `100` | `100` |
| `RPKI_RTR_PRUNE_DELTA_BY_SNAPSHOT_SIZE` | 是否启用“累计 delta 估算 wire size 不小于 snapshot 时，继续裁剪最老 delta”的策略。 | `false` | `false` |
| `RPKI_RTR_STRICT_CCR_VALIDATION` | 是否对 CCR 中的非法 VRP / VAP 采用严格模式；`true` 表示整份 CCR 拒绝，`false` 表示跳过非法项并告警。 | `false` | `false` |
| `RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS` | 数据源刷新周期：扫描 CCR/SLURM 并尝试更新缓存的间隔，单位秒，必须 `>= 1`。 | `300` | `30` |
| `RPKI_RTR_REFRESH_INTERVAL_SECS` | 旧变量名（已兼容，建议迁移到 `RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS`）。 | `300` | `300` |
| `RPKI_RTR_TIMING_REFRESH_SECS` | RTR EndOfData timing 的 `refresh` 字段，单位秒。 | `3600` | `300` |
| `RPKI_RTR_TIMING_RETRY_SECS` | RTR EndOfData timing 的 `retry` 字段，单位秒。 | `600` | `60` |
| `RPKI_RTR_TIMING_EXPIRE_SECS` | RTR EndOfData timing 的 `expire` 字段，单位秒（需大于 refresh/retry）。 | `7200` | `1800` |
| `RPKI_RTR_MAX_CONNECTIONS` | 最大并发 RTR 客户端连接数。 | `512` | `512` |
| `RPKI_RTR_NOTIFY_QUEUE_SIZE` | Serial Notify 广播队列大小。 | `1024` | `1024` |
| `RPKI_RTR_TCP_KEEPALIVE_SECS` | TCP keepalive 时间，单位秒；设为 `0` 表示禁用。 | `60` | `60` |
| `RPKI_RTR_WARN_INSECURE_TCP` | 纯 TCP 模式下是否输出不安全警告。 | `true` | `true` |
| `RPKI_RTR_REQUIRE_TLS_SERVER_DNS_NAME_SAN` | 严格模式：TLS 服务端证书不包含 `subjectAltName dNSName` 时拒绝启动。 | `false` | `false` |

### 说明

- 纯 TCP 模式只应部署在受信任、可控网络中。
- TLS 模式要求客户端证书认证。
- 当前输入源是 `CCR` 目录，不再是单独的文本 VRP / ASPA / Router Key 文件。
- 刷新时会重新扫描 `RPKI_RTR_CCR_DIR`，并选取文件名排序最新的 `.ccr` 文件。
- `RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS`（旧名 `RPKI_RTR_REFRESH_INTERVAL_SECS`）控制的是“服务端多长时间扫描并更新数据源”，不是 RTR EndOfData 的 timing。
- RTR 客户端在 EndOfData 中看到的 `refresh/retry/expire` 由 `RPKI_RTR_TIMING_REFRESH_SECS`、`RPKI_RTR_TIMING_RETRY_SECS`、`RPKI_RTR_TIMING_EXPIRE_SECS` 控制。
- `RPKI_RTR_STRICT_CCR_VALIDATION=false` 时，CCR 中的非法 VRP / VAP 会被跳过并输出 warning；`true` 时整份 CCR 更新失败。
- `RPKI_RTR_TCP_KEEPALIVE_SECS=0` 表示关闭 keepalive；非零值表示在整个连接生命周期内启用 keepalive。
- `RPKI_RTR_PRUNE_DELTA_BY_SNAPSHOT_SIZE=true` 时，除了 `RPKI_RTR_MAX_DELTA` 的固定条数裁剪外，还会在累计 delta 估算 wire size 不小于 snapshot 时继续删除最老 delta。

## 快速启动

### Docker 启动

```bash
docker compose -f deploy/server/docker-compose.yml up -d --build
docker compose -f deploy/server/docker-compose.yml logs -f rpki-rtr
docker compose -f deploy/server/docker-compose.yml down
```

### 本地运行（推荐先用脚本）

```bash
docker compose -f deploy/server/docker-compose.yml up -d --build
docker compose -f deploy/server/docker-compose.yml logs -f rpki-rtr
docker compose -f deploy/server/docker-compose.yml down
```

### 本地手动运行（最小示例）

纯 TCP：

```bash
docker compose -f deploy/server/docker-compose.yml up -d --build
```

TLS / mutual TLS：

```bash
docker compose -f deploy/server/docker-compose.yml -f deploy/server/docker-compose.tls.yml up -d --build
```

## CCR 输入说明

当前会从 `RPKI_RTR_CCR_DIR` 指向的目录中扫描最新 `.ccr` 文件，并从中提取：

- `VRP`
- `VAP (Validated ASPA Payload) / ASPA`

测试样例可参考：

- [`data/20260324T000037Z-sng1.ccr`](data/20260324T000037Z-sng1.ccr)

## Client 使用

客户端的作用是从不同角度验证 RTR 服务端行为，覆盖“协议调试、自动化测试、黑盒互通”三类需求。

### 作用与选择

| Client | 主要作用 | 适用场景 |
| --- | --- | --- |
| `rtr_debug_client` | 面向协议交互调试，便于手动发起 `reset/serial` 并观察响应 | 开发联调、定位会话/报文问题、快速复现 |
| `rpki-rs-test-client` | 基于 `rpki-rs` 客户端 API 做自动化验证 | CI/CD、回归测试、批量步骤校验 |
| `FRR` | 真实路由软件黑盒接入，验证与生产侧客户端互通 | 互操作验证、运维演练、端到端验证 |

建议使用顺序：

1. 先用 `rtr_debug_client` 快速确认服务可连通、协议版本和基础响应正常。
2. 再用 `rpki-rs-test-client` 做可重复的自动化步骤校验。
3. 最后用 `FRR` 做黑盒互通验证，确认真实客户端接入行为。

### rtr_debug_client（Docker）

```bash
docker compose -f deploy/client/docker-compose.yml up --build
docker compose -f deploy/client/docker-compose.yml logs -f rtr-debug-client
docker compose -f deploy/client/docker-compose.yml down
```

多实例压测/联调：

```bash
docker compose -f deploy/client/docker-compose.clients.yml up -d
docker compose -f deploy/client/docker-compose.clients.yml logs -f
docker compose -f deploy/client/docker-compose.clients.yml down
```

### rpki-rs-test-client（Docker）

```bash
docker compose -f deploy/rpki-rs-client/docker-compose.yml up --build
docker compose -f deploy/rpki-rs-client/docker-compose.yml run --rm \
  rpki-rs-test-client 127.0.0.1:323 2 reset --steps 1 --assert-min-records 1
docker compose -f deploy/rpki-rs-client/docker-compose.yml down
```

说明：
- 适合自动化场景：可作为流水线中的协议检查器。
- 适合回归校验：通过参数化步骤（`--steps`）和断言（如 `--assert-min-records`）稳定验证行为。

### FRR（Docker，黑盒客户端）

```bash
docker compose -f deploy/frr/docker-compose.yml up -d
docker exec -it frr-rpki-client vtysh -c "show rpki cache-connection"
docker exec -it frr-rpki-client vtysh -c "show rpki prefix-table"
docker compose -f deploy/frr/docker-compose.yml down
```

说明：
- 适合真实互通验证：FRR 更接近生产客户端行为。
- 适合观察业务结果：可直接查看缓存连接状态和前缀表结果。 

## RTR Debug Client

调试用 RTR client 位于：

- [`src/bin/rtr_debug_client/main.rs`](src/bin/rtr_debug_client/main.rs)

说明文档位于：

- [`src/bin/rtr_debug_client/README.md`](src/bin/rtr_debug_client/README.md)

### Client 启动示例

连接纯 TCP RTR server：

```sh
cargo run --bin rtr_debug_client -- 127.0.0.1:323 1 reset
```

连接 TLS RTR server：

```sh
cargo run --bin rtr_debug_client -- \
  127.0.0.1:324 1 reset \
  --tls \
  --ca-cert ./certs/client-ca.crt \
  --server-name localhost \
  --client-cert ./certs/client-good.crt \
  --client-key ./certs/client-good.key
```

如果要在收到错误后继续自动轮询，可以加：

```sh
--keep-after-error
```

## 存储模型与边界约束（多视图）

当前 RTR cache 持久化采用三视图模型（按协议版本拆分）：

- v0：仅 `RouteOrigin`
- v1：`RouteOrigin + RouterKey`
- v2：`RouteOrigin + RouterKey + Aspa`

RocksDB 中的核心状态按版本独立保存：

- `session_id_v0/v1/v2`
- `serial_v0/v1/v2`
- `current_v0/v1/v2`（snapshot）
- `delta_min_v0/v1/v2`、`delta_max_v0/v1/v2`
- delta key 采用 `D + version + serial_be`

### 唯一写入口

为保证 `session_id/serial/snapshot/delta_window` 的一致性，生产代码只允许通过：

- `RtrStore::save_cache_state_versioned(...)`

且该入口应仅由：

- `src/rtr/cache/store.rs`

发起调用（即由 cache 层统一批量原子写入）。

### 恢复约束

重启恢复时，按版本读取并校验：

- `availability`
- `snapshot_for_version(version)`
- `session_id_for_version(version)`
- `serial_for_version(version)`
- `delta_window_for_version(version)` 与 `load_delta_window_for_version(...)`

只有三视图状态完整且自洽时才走 store 恢复；否则回退到输入源加载流程。

### 边界防回归测试

新增边界测试用于限制写调用点，防止后续出现绕写路径：

- `tests/test_store_boundary.rs`

可单独执行：

```sh
cargo test --test test_store_boundary -- --nocapture
```

## Deploy Layout

`deploy/` 目录按角色拆分为四套部署与测试入口：

- `server/`: 本仓库 RTR Server（`src/main.rs`）容器化部署
- `client/`: 本仓库 `rtr_debug_client` 容器化部署
- `rpki-rs-client/`: 基于外部 `rpki-rs` client API 的测试客户端容器化部署
- `frr/`: FRR 作为黑盒 RTR Client 的配置与 compose

### Quick Start

最短路径（本地容器环境）：

```bash
docker compose -f deploy/server/docker-compose.yml up -d --build
docker compose -f deploy/server/docker-compose.yml logs -f rpki-rtr
docker compose -f deploy/server/docker-compose.yml down
```

### Compose 入口

| 角色 | Compose 文件 |
| --- | --- |
| Server | `deploy/server/docker-compose.yml` |
| Debug Client（单实例） | `deploy/client/docker-compose.yml` |
| Debug Client（多实例） | `deploy/client/docker-compose.clients.yml` |
| rpki-rs Client | `deploy/rpki-rs-client/docker-compose.yml` |
| FRR Client | `deploy/frr/docker-compose.yml` |

通用操作模板：

```bash
docker compose -f <compose-file> up -d --build
docker compose -f <compose-file> logs -f
docker compose -f <compose-file> down
```

### 角色附加命令

rpki-rs Client（覆盖默认参数运行）：

```bash
docker compose -f deploy/rpki-rs-client/docker-compose.yml run --rm \
  rpki-rs-test-client 127.0.0.1:323 2 reset --steps 1 --assert-min-records 1
```

FRR Client（检查连接与前缀）：

```bash
docker exec -it frr-rpki-client vtysh -c "show rpki cache-connection"
docker exec -it frr-rpki-client vtysh -c "show rpki prefix-table"
```

更多部署细节：

- `deploy/server/DEPLOYMENT.md`
- `deploy/frr/README.md`
- `deploy/frr/README.zh.md`

## 传输模式说明（TCP / TLS / 原生 SSH）

当前 `rpki` 进程内支持三种传输：

- TCP（默认开启）
- TLS（可选开启，mTLS）
- 原生 SSH（可选开启，进程内实现，不再使用外部 `sshd Subsystem` 桥接）

### TCP 模式

默认监听：
- `RPKI_RTR_TCP_ADDR=0.0.0.0:323`

最小启动（仅 TCP）：

```sh
export RPKI_RTR_ENABLE_TLS=false
export RPKI_RTR_ENABLE_SSH=false
cargo run --bin rpki
```

说明：
- 仅建议部署在受信任、可控网络中。

### TLS 模式（mTLS）

相关环境变量：
- `RPKI_RTR_ENABLE_TLS=true`
- `RPKI_RTR_TLS_ADDR`（默认 `0.0.0.0:324`）
- `RPKI_RTR_TLS_CERT_PATH`
- `RPKI_RTR_TLS_KEY_PATH`
- `RPKI_RTR_TLS_CLIENT_CA_PATH`

最小启动（TCP + TLS）：

```sh
export RPKI_RTR_ENABLE_TLS=true
export RPKI_RTR_ENABLE_SSH=false
export RPKI_RTR_TLS_CERT_PATH=./certs/server-dns.crt
export RPKI_RTR_TLS_KEY_PATH=./certs/server-dns.key
export RPKI_RTR_TLS_CLIENT_CA_PATH=./certs/client-ca.crt
cargo run --bin rpki
```

### 原生 SSH 模式（进程内）

与 `draft-ietf-sidrops-8210bis-25` 对齐要点：
- 使用 SSHv2
- 使用 subsystem（默认 `rpki-rtr`）
- 使用 public key 认证
- 服务端拒绝 `none`
- 服务端默认不启用 `password`，配置 `RPKI_RTR_SSH_PASSWORD` 后可选启用（draft 中为 MAY）

相关环境变量：
- `RPKI_RTR_ENABLE_SSH=true`
- `RPKI_RTR_SSH_ADDR`（默认 `0.0.0.0:22`）
- `RPKI_RTR_SSH_PORT`（默认 `22`，设置后会覆盖 `RPKI_RTR_SSH_ADDR` 的端口）
- `RPKI_RTR_SSH_HOST_KEY_PATH`（OpenSSH host 私钥）
- `RPKI_RTR_SSH_AUTHORIZED_KEYS_PATH`（允许接入的 router 公钥列表）
- `RPKI_RTR_SSH_USERNAME`（默认 `rpki-rtr`）
- `RPKI_RTR_SSH_SUBSYSTEM_NAME`（默认 `rpki-rtr`）
- `RPKI_RTR_SSH_PASSWORD`（可选；设置后启用 password 认证）

密钥准备示例：

```sh
ssh-keygen -t ed25519 -N '' -f ./certs/ssh_host_ed25519_key
ssh-keygen -t ed25519 -N '' -f ./certs/rtr_client_ed25519_key
cp ./certs/rtr_client_ed25519_key.pub ./certs/rtr-authorized_keys
```

最小启动（TCP + SSH）：

```sh
export RPKI_RTR_ENABLE_TLS=false
export RPKI_RTR_ENABLE_SSH=true
export RPKI_RTR_SSH_ADDR=0.0.0.0:22
# or only override the port:
# export RPKI_RTR_SSH_PORT=2022
export RPKI_RTR_SSH_HOST_KEY_PATH=./certs/ssh_host_ed25519_key
export RPKI_RTR_SSH_AUTHORIZED_KEYS_PATH=./certs/rtr-authorized_keys
export RPKI_RTR_SSH_USERNAME=rpki-rtr
export RPKI_RTR_SSH_SUBSYSTEM_NAME=rpki-rtr
# 可选：启用 password 认证（同时仍支持 publickey）
# export RPKI_RTR_SSH_PASSWORD=test-password
cargo run --bin rpki
```

连通性检查（OpenSSH）：

```sh
ssh -i ./certs/rtr_client_ed25519_key -p 22 -s rpki-rtr@127.0.0.1 rpki-rtr
```

说明：
- 该命令主要用于验证 SSH 子系统通道可建立，不等价于完整 RTR 协议回归测试。
