昨天我們講解了 Connection Migration 的概念,今天就來帶著讀者簡單看一下對應的 Source code 實現在哪個 function,讓有興趣的讀者可以繼續往下 trace 細節,大概講一下 ngtcp2 中哪幾個 function 負責做 Connection migration,就可以往下研究了(如果研究出心得記得交流一下)
在 ngtcp2 中 Client 要執行 Connection migration 可以呼叫 ngtcp2_conn_initiate_immediate_migration 或者 ngtcp2_conn_initiate_migration,ngtcp2_conn_initiate_immediate_migration 遷移前不會執行 path validation, 無法確認 reachability,ngtcp2_conn_initiate_migration 在執行前會執行 path validation 的動作確保 reachability。
int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
                                   ngtcp2_tstamp ts) {
  int rv;
  ngtcp2_dcid *dcid;
  ngtcp2_duration pto, initial_pto, timeout;
  ngtcp2_pv *pv;
  assert(!conn->server);
  conn->log.last_ts = ts;
  conn->qlog.last_ts = ts;
  rv = conn_initiate_migration_precheck(conn, &path->local);
  if (rv != 0) {
    return rv;
  }
  if (conn->pv) {
    rv = conn_abort_pv(conn, ts);
    if (rv != 0) {
      return rv;
    }
  }
  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
  ngtcp2_dcid_set_path(dcid, path);
  pto = conn_compute_pto(conn, &conn->pktns);
  initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
  timeout = 3 * ngtcp2_max(pto, initial_pto);
  rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
                     conn->mem);
  if (rv != 0) {
    return rv;
  }
  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
  conn->pv = pv;
  return conn_call_activate_dcid(conn, &pv->dcid);
}
int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn,
                                             const ngtcp2_path *path,
                                             ngtcp2_tstamp ts) {
  int rv;
  ngtcp2_dcid *dcid;
  ngtcp2_duration pto, initial_pto, timeout;
  ngtcp2_pv *pv;
  assert(!conn->server);
  conn->log.last_ts = ts;
  conn->qlog.last_ts = ts;
  rv = conn_initiate_migration_precheck(conn, &path->local);
  if (rv != 0) {
    return rv;
  }
  ngtcp2_conn_stop_pmtud(conn);
  if (conn->pv) {
    rv = conn_abort_pv(conn, ts);
    if (rv != 0) {
      return rv;
    }
  }
  rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
  if (rv != 0) {
    return rv;
  }
  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
  ngtcp2_dcid_set_path(dcid, path);
  ngtcp2_dcid_copy(&conn->dcid.current, dcid);
  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
  conn_reset_congestion_state(conn, ts);
  conn_reset_ecn_validation_state(conn);
  pto = conn_compute_pto(conn, &conn->pktns);
  initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
  timeout = 3 * ngtcp2_max(pto, initial_pto);
  /* TODO It might be better to add a new flag which indicates that a
     connection should be closed if this path validation failed.  The
     current design allows an application to continue, by migrating
     into yet another path. */
  rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
                     conn->mem);
  if (rv != 0) {
    return rv;
  }
  conn->pv = pv;
  return conn_call_activate_dcid(conn, &conn->dcid.current);
}
在程式碼片段中常看到關於 ngtcp2_pv 這個 struct 是專門負責存放 path validation 相關資料的,詳細請看 ngtcp2_pv.h, ngtcp2_pv.c 這兩個檔案。