本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源: http://yfydz.cublog.cn

9. 连接处理

关于连接的基本处理在programs/pluto/connections.c中定义,该文件是一个很大的文件,差不多
130K,只是拣主要的处理函数进行分析, 而且关于OE的处理就忽略了。

9.1 新增连接

该函数被whack_handle (rcv_whack.c)函数调用,用于系统初始化从配置文件读取的连接信息,建立
连接结构。

void
add_connection(const struct whack_message *wm)
{
    struct alg_info_ike *alg_info_ike;
    const char *ugh;
// IKE算法指针初始化清零
    alg_info_ike = NULL;

// 检查连接名称对应的连接是否已经存在, 是则打印一个警告信息,但仍继续操作
    if (con_by_name(wm->name, FALSE) != NULL)
    {
 loglog(RC_DUPNAME, "attempt to redefine connection \"%s\"", wm->name);
    }
// 检查连接左右双方的协议是否相同, 是必须要求相同的
    else if (wm->right.protocol != wm->left.protocol)
    {
 /* this should haven been diagnosed by whack
  * !!! overloaded use of RC_CLASH
  */
 loglog(RC_CLASH, "the protocol must be the same for leftport and rightport");
    }
// 检查配置文件中"ike="定义的算法信息是否存在,如果定义"ike=",表示协商过程中的算法是
// 固定的,不是协商的
    else if(wm->ike != NULL
     && ((alg_info_ike = alg_info_ike_create_from_str(wm->ike, &ugh))==NULL
  || alg_info_ike->alg_info_cnt==0)) {
 if (alg_info_ike!= NULL && alg_info_ike->alg_info_cnt==0) {
     loglog(RC_NOALGO
     , "got 0 transforms for ike=\"%s\""
     , wm->esp);
     return;
 }
 loglog(RC_NOALGO
        , "esp string error: %s"
        , ugh? ugh : "Unknown");
 return;
    }
// IKE算法存在或需要自动协商算法的情况
    else if ((wm->ike == NULL || alg_info_ike != NULL)
// 检查连接的左右端点的地址信息是否合法,可以允许一边地址是ANY,但不能两边都是ANY
      && check_connection_end(&wm->right, &wm->left, wm)
      && check_connection_end(&wm->left, &wm->right, wm))
    {
 bool same_rightca, same_leftca;
// 分配新连接
 struct connection *c = alloc_thing(struct connection, "struct connection");
 same_rightca = same_leftca = FALSE;
// 连接名称
 c->name = wm->name;
// 连接策略
 c->policy = wm->policy;
 DBG(DBG_CONTROL,
     DBG_log("Added new connection %s with policy %s"
      , c->name
      , prettypolicy(c->policy)));
// 检查是否定义压缩处理而内核却不支持压缩   
 if ((c->policy & POLICY_COMPRESS) && !can_do_IPcomp)
     loglog(RC_COMMENT
  , "ignoring --compress in \"%s\" because KLIPS is not configured to do
IPCOMP"
  , c->name);
 c->alg_info_esp = NULL;
#ifdef KERNEL_ALG
// 解析"esp="定义的ESP算法
 if (wm->esp) 
 {
  DBG(DBG_CONTROL, DBG_log("from whack: got --esp=%s", wm->esp ? wm->esp:
"NULL"));
// 解析算法信息
  c->alg_info_esp = alg_info_esp_create_from_str(wm->esp? wm->esp : "",
&ugh, FALSE);
  DBG(DBG_CRYPT|DBG_CONTROL,
   static char buf[256]="<NULL>";
   if (c->alg_info_esp)
    alg_info_snprint(buf, sizeof(buf),
       (struct alg_info *)c->alg_info_esp,
TRUE);
   DBG_log("esp string values: %s", buf);
  );
  if (c->alg_info_esp) {
   if (c->alg_info_esp->alg_info_cnt==0) {
    loglog(RC_NOALGO
     , "got 0 transforms for esp=\"%s\""
     , wm->esp);
    return;
   }
  } else {
   loglog(RC_NOALGO
    , "esp string error: %s"
    , ugh? ugh : "Unknown");
   return;
  }
  c->alg_esp = clone_str(wm->esp, "esp string");
 }
#endif 
 c->alg_info_ike = NULL;
#ifdef IKE_ALG
// 解析"ike="定义的IKE算法
 if (wm->ike)
 {
// 这是刚才解析的IKE算法结构
     c->alg_info_ike = alg_info_ike;
     DBG(DBG_CONTROL, DBG_log("from whack: got --ike=%s", wm->ike));
     DBG(DBG_CRYPT|DBG_CONTROL,
  char buf[256];
  alg_info_snprint(buf, sizeof(buf),
     (struct alg_info *)c->alg_info_ike, TRUE);
  DBG_log("ike string values: %s", buf);
  );
     if (c->alg_info_ike) {
  if (c->alg_info_ike->alg_info_cnt==0) {
      loglog(RC_NOALGO
      , "got 0 transforms for ike=\"%s\""
      , wm->ike);
      return;
  }
     } else {
  loglog(RC_NOALGO
         , "ike string error: %s"
         , ugh? ugh : "Unknown");
  return;
     }
     c->alg_ike = clone_str(wm->ike, "ike string");
 }
#endif
// SA参数: IKE生命期
 c->sa_ike_life_seconds = wm->sa_ike_life_seconds;
// IPSEC生命期
 c->sa_ipsec_life_seconds = wm->sa_ipsec_life_seconds;
// 重协商间隔
 c->sa_rekey_margin = wm->sa_rekey_margin;
 c->sa_rekey_fuzz = wm->sa_rekey_fuzz;
// 重协商尝试次数
 c->sa_keying_tries = wm->sa_keying_tries;
 /* RFC 3706 DPD */
// DPD参数
        c->dpd_delay = wm->dpd_delay;
// DPD超时
        c->dpd_timeout = wm->dpd_timeout;
// DPD超时后的动作
        c->dpd_action = wm->dpd_action;
// 强制UDP封装
 c->forceencaps = wm->forceencaps;
// 地址协议族
 c->addr_family = wm->addr_family;
// 通道内地址协议族
 c->tunnel_addr_family = wm->tunnel_addr_family;
 c->requested_ca = NULL;
// 解析连接端点地址信息
 same_leftca  = extract_end(&c->spd.this, &wm->left, "left");
 same_rightca = extract_end(&c->spd.that, &wm->right, "right");
// 填充安全策略的路由结构参数
// CA名称
 if (same_rightca)
     c->spd.that.ca = c->spd.this.ca;
 else if (same_leftca)
     c->spd.this.ca = c->spd.that.ca;
// 建立端点地址信息
 default_end(&c->spd.this, &c->spd.that.host_addr);
 default_end(&c->spd.that, &c->spd.this.host_addr);
 /* force any wildcard host IP address, any wildcard subnet
  * or any wildcard ID to that end
  */
 if (isanyaddr(&c->spd.this.host_addr) || c->spd.this.has_client_wildcard
 || c->spd.this.has_port_wildcard || c->spd.this.has_id_wildcards)
 {
     struct end t = c->spd.this;
     c->spd.this = c->spd.that;
     c->spd.that = t;
 }
 c->spd.next = NULL;
// 请求ID,唯一值
 c->spd.reqid = gen_reqid();
 /* set internal fields */
 c->instance_serial = 0;
// 添加到系统连接链表的链表头
 c->ac_next = connections;
 connections = c;
// 初始化参数为无效值
 c->interface = NULL;
 c->spd.routing = RT_UNROUTED;
 c->newest_isakmp_sa = SOS_NOBODY;
 c->newest_ipsec_sa = SOS_NOBODY;
 c->spd.eroute_owner = SOS_NOBODY;
// 确定连接类型
 if (c->policy & POLICY_GROUP)
 {
// 组策略
     c->kind = CK_GROUP;
     add_group(c);
 }
 else if ((isanyaddr(&c->spd.that.host_addr) && !NEVER_NEGOTIATE(c->policy))
  || c->spd.that.has_client_wildcard || c->spd.that.has_port_wildcard 
  || ((c->policy & POLICY_SHUNT_MASK) == 0  && c-
>spd.that.has_id_wildcards ))
 {
// 一端地址不固定的动态连接,此时的连接结构只相对于一个连接模板,等具体有端点连接
// 时才具体实例化
     DBG(DBG_CONTROL, DBG_log("based upon policy, the connection is a
template."));
     /* Opportunistic or Road Warrior or wildcard client subnet
      * or wildcard ID */
     c->kind = CK_TEMPLATE;
 }
 else
 {
// 固定连接, 双方地址固定
     c->kind = CK_PERMANENT;
 }
// 确定策略优先级
 set_policy_prio(c); /* must be after kind is set */
#ifdef DEBUG
 c->extra_debugging = wm->debugging;
#endif
 c->gw_info = NULL;
#ifdef VIRTUAL_IP
 passert(!(wm->left.virt && wm->right.virt));
// 虚拟IP参数设置
 if (wm->left.virt || wm->right.virt) {
     passert(isanyaddr(&c->spd.that.host_addr));
     c->spd.that.virt = create_virtual(c,
  wm->left.virt ? wm->left.virt : wm->right.virt);
     if (c->spd.that.virt)
  c->spd.that.has_client = TRUE;
 }
#endif
// 去共享化,将原来共享的参数重新分配各自的空间保存,不再共享
 unshare_connection_strings(c);
#ifdef KERNEL_ALG
// 内核算法引用数增加
 alg_info_addref((struct alg_info *)c->alg_info_esp);
#endif
#ifdef IKE_ALG
// IKE算法引用数增加
 alg_info_addref((struct alg_info *)c->alg_info_ike);
#endif
// 确定方向, 确定使用的ipsec*网卡
 (void)orient(c);
// 将连接转换为主机对
 connect_to_host_pair(c);
 /* log all about this connection */
// 在日志中记录详细的连接信息
 openswan_log("added connection description \"%s\"", c->name);
 DBG(DBG_CONTROL,
     char topo[CONNECTION_BUF];
     (void) format_connection(topo, sizeof(topo), c, &c->spd);
     DBG_log("%s", topo);
     /* Make sure that address families can be correctly inferred
      * from printed ends.
      */
     passert(c->addr_family == addrtypeof(&c->spd.this.host_addr)
  && c->addr_family == addrtypeof(&c->spd.this.host_nexthop)
  && (c->spd.this.has_client? c->tunnel_addr_family : c->addr_family)
    == subnettypeof(&c->spd.this.client)
  && c->addr_family == addrtypeof(&c->spd.that.host_addr)
  && c->addr_family == addrtypeof(&c->spd.that.host_nexthop)
  && (c->spd.that.has_client? c->tunnel_addr_family : c->addr_family)
    == subnettypeof(&c->spd.that.client));
     DBG_log("ike_life: %lus; ipsec_life: %lus; rekey_margin: %lus;"
  " rekey_fuzz: %lu%%; keyingtries: %lu; policy: %s"
  , (unsigned long) c->sa_ike_life_seconds
  , (unsigned long) c->sa_ipsec_life_seconds
  , (unsigned long) c->sa_rekey_margin
  , (unsigned long) c->sa_rekey_fuzz
  , (unsigned long) c->sa_keying_tries
  , prettypolicy(c->policy));
 );
    } else {
 loglog(RC_FATAL, "attempt to load incomplete connection");
    }
}

 
9.2 启动连接

该函数可能被whack_handle (rcv_whack.c)函数或dpd_timeout (dpd.c)调用, 启动连接, 如果连接
双方地址固定就发起IKE协商, 如果是动态连接的服务器端, 无法启动, 继续监听状态。
void
initiate_connection(const char *name, int whackfd
      , lset_t moredebug
      , enum crypto_importance importance)
{
// 根据名称查找连接结构
    struct connection *c = con_by_name(name, TRUE);
    if (c != NULL)
    {
// 连接找到
// 将该连接作为系统当前连接
 set_cur_connection(c);
 /* turn on any extra debugging asked for */
 c->extra_debugging |= moredebug;
 if (!oriented(*c))
 {
// 没确定方向的连接
     loglog(RC_ORIENT, "We cannot identify ourselves with either end of this
connection.");
 }
 else if (NEVER_NEGOTIATE(c->policy))
 {
// 无协商连接, 不需要进入协商
     loglog(RC_INITSHUNT
  , "cannot initiate an authby=never connection");
 }
 else if (c->kind != CK_PERMANENT)
 {
// 非固定连接, 是动态连接, 不能启动, 继续监听
     if (isanyaddr(&c->spd.that.host_addr))
  loglog(RC_NOPEERIP, "cannot initiate connection without knowing peer IP
address (kind=%s)"
         , enum_show(&connection_kind_names, c->kind));
     else
  loglog(RC_WILDCARD, "cannot initiate connection with ID wildcards
(kind=%s)"
         , enum_show(&connection_kind_names, c->kind));
 }
 else
 {
// 可以启动连接
     /* We will only request an IPsec SA if policy isn't empty
      * (ignoring Main Mode items).
      * This is a fudge, but not yet important.
      * If we are to proceed asynchronously, whackfd will be NULL_FD.
      */
// 策略启动
     c->policy |= POLICY_UP;
     if(c->policy & POLICY_ENCRYPT) {
// 需要加密的话获取算法和SADB
  struct alg_info_esp *alg = c->alg_info_esp;
  struct db_sa *phase2_sa = kernel_alg_makedb(alg, TRUE);
  
  if(alg != NULL && phase2_sa == NULL) {
      whack_log(RC_NOALGO, "can not initiate: no acceptable kernel
algorithms loaded");
      reset_cur_connection();
      close_any(whackfd);
      return;
  }
  free_sa(phase2_sa);
     }
#ifdef SMARTCARD
// 进行smartcard验证, 失败时也不能启动
     /* do we have to prompt for a PIN code? */
     if (c->spd.this.sc != NULL && !c->spd.this.sc->valid && whackfd != NULL_FD)
  scx_get_pin(c->spd.this.sc, whackfd);
     if (c->spd.this.sc != NULL && !c->spd.this.sc->valid)
     {
  loglog(RC_NOVALIDPIN, "cannot initiate connection without valid PIN");
     }
     else
#endif
     {
// 发起IKE协商
  ipsecdoi_initiate(whackfd, c, c->policy, 1
      , SOS_NOBODY, importance);
  whackfd = NULL_FD; /* protect from close */
     }
 }
 reset_cur_connection();
    }
    close_any(whackfd);
}
 

9.3 删除连接

删除连接有几个不同的函数:

/* Delete connections with the specified name */
// 按名称删除连接,函数被whack_handle (rcv_whack.c)函数调用
void
delete_connections_by_name(const char *name, bool strict)
{
// 根据名称查找连接
    struct connection *c = con_by_name(name, strict);
// 删除连接,循环删除所有同名的连接
    for (; c != NULL; c = con_by_name(name, FALSE))
 delete_connection(c, FALSE);
}

// 删除全部连接
void
delete_every_connection(void)
{
// 遍历连接链表,删除连接
    while (connections != NULL)
 delete_connection(connections, TRUE);
}

// 释放连接
void
release_connection(struct connection *c, bool relations)
{
    if (c->kind == CK_INSTANCE)
    {
// 如果连接是实例化所得的连接,删除该连接
 /* This does everything we need.
  * Note that we will be called recursively by delete_connection,
  * but kind will be CK_GOING_AWAY.
  */
 delete_connection(c, relations);
    }
    else
    {
// 否则清除连接相关的状态
 flush_pending_by_connection(c);
 delete_states_by_connection(c, relations);
 unroute_connection(c);
    }
}
 
// 删除连接的核心函数
void
delete_connection(struct connection *c, bool relations)
{
    struct spd_route *sr;
// 备份当前连接作为返回时系统的当前连接, 如果要删除的连接就是当前连接,为空
    struct connection *old_cur_connection
 = cur_connection == c? NULL : cur_connection;
#ifdef DEBUG
    lset_t old_cur_debugging = cur_debugging;
#endif
// 设置连接为当前连接
    set_cur_connection(c);
    /* Must be careful to avoid circularity:
     * we mark c as going away so it won't get deleted recursively.
     */
    passert(c->kind != CK_GOING_AWAY);
    if (c->kind == CK_INSTANCE)
    {
// 如果是实例化后得到的连接, 将连接类型改为CK_GOING_AWAY
 openswan_log("deleting connection \"%s\" instance with peer %s {isakmp=#%
lu/ipsec=#%lu}"
      , c->name
      , ip_str(&c->spd.that.host_addr)
      , c->newest_isakmp_sa, c->newest_ipsec_sa);
 c->kind = CK_GOING_AWAY;
    }
    else
    {
 openswan_log("deleting connection");
    }
// 释放连接的相关状态,注意这时连接的类型已经肯定不是CK_INSTANCE了,
// 所以不会出现递归调用的情况
    release_connection(c, relations); /* won't delete c */
// 释放组
    if (c->kind == CK_GROUP)
 delete_group(c);
    /* free up any logging resources */
// 关闭日志资源
    perpeer_logfree(c);
    /* find and delete c from connections list */
// 将连接从系统连接链表中断开
    list_rm(struct connection, ac_next, c, connections);
// 恢复系统原来的当前连接
    cur_connection = old_cur_connection;
    /* find and delete c from the host pair list */
// 从主机对链表中删除连接
    if (c->host_pair == NULL)
    {
// 从unoriented_connections链表中断开
 list_rm(struct connection, hp_next, c, unoriented_connections);
    }
    else
    {
 struct host_pair *hp = c->host_pair;
// 从主机对链表中断开
 list_rm(struct connection, hp_next, c, hp->connections);
 c->host_pair = NULL; /* redundant, but safe */
 /* if there are no more connections with this host_pair
  * and we haven't even made an initial contact, let's delete
  * this guy in case we were created by an attempted DOS attack.
  */
// 如果该主机对已经没有其他连接, 释放该主机对
 if (hp->connections == NULL
 && !hp->initial_connection_sent)
 {
     passert(hp->pending == NULL); /* ??? must deal with this! */
     list_rm(struct host_pair, next, hp, host_pairs);
     pfree(hp);
 }
    }
#ifdef VIRTUAL_IP
// 释放虚拟IP资源
    if (c->kind != CK_GOING_AWAY) pfreeany(c->spd.that.virt);
#endif
#ifdef DEBUG
    set_debugging(old_cur_debugging);
#endif
// 释放连接名称空间
    pfreeany(c->name);
    sr = &c->spd;
// 释放连接的所有安全策略路由
    while(sr) {
 delete_sr(c, sr);
 sr = sr->next;
    }
// 释放CA信息
    free_generalNames(c->requested_ca, TRUE);
// 减少网关使用计数
    gw_delref(&c->gw_info);
#ifdef KERNEL_ALG
// 减少内核算法使用计数
    alg_info_delref((struct alg_info **)&c->alg_info_esp);
#endif
#ifdef IKE_ALG
// 减少IKE算法使用计数
    alg_info_delref((struct alg_info **)&c->alg_info_ike);
#endif
// 释放连接空间
    pfree(c);
}
 
9.4 停止连接

该函数被whack_handle (rcv_whack.c)函数调用,用于停止连接, 释放连接相关的SA。

void
terminate_connection(const char *nm)
{
    /* Loop because more than one may match (master and instances)
     * But at least one is required (enforced by con_by_name).
     */
    struct connection *c, *n;
// 只要还能找到连接就循环操作
    for (c = con_by_name(nm, TRUE); c != NULL; c = n)
    {
// 该连接的链表下一项, 会在循环时赋值给c
 n = c->ac_next; /* grab this before c might disappear */
// 名称匹配
 if (streq(c->name, nm)
// 类型是永久,实例,或要死的
 && c->kind >= CK_PERMANENT
// 策略需协商
 && !NEVER_NEGOTIATE(c->policy))
 {
// 设置为当前连接
     set_cur_connection(c);
     openswan_log("terminating SAs using this connection");
// 策略停
     c->policy &= ~POLICY_UP;
// 释放连接相关所有状态
     flush_pending_by_connection(c);
     delete_states_by_connection(c, FALSE);
     reset_cur_connection();
 }
    }
}

9.5 查找连接

9.5.1 根据名称查找连接
/* find a connection by name.
 * If strict, don't accept a CK_INSTANCE.
 * Move the winner (if any) to the front.
 * If none is found, and strict, a diagnostic is logged to whack.
 */
struct connection *
con_by_name(const char *nm, bool strict)
{
    struct connection *p, *prev;
// 遍历连接链表
    for (prev = NULL, p = connections; ; prev = p, p = p->ac_next)
    {
 if (p == NULL)
 {
// 到链表末尾,中断
     if (strict)
  whack_log(RC_UNKNOWN_NAME
      , "no connection named \"%s\"", nm);
     break;
 }
// 名称要相同
 if (streq(p->name, nm)
// 如果是严格情况,连接类型不能是实例化的连接
 && (!strict || p->kind != CK_INSTANCE))
 {
     if (prev != NULL)
     {
// 从当前位置断开
  prev->ac_next = p->ac_next; /* remove p from list */
// 设置为链表头
  p->ac_next = connections; /* and stick it on front */
  connections = p;
     }
     break;
 }
    }
    return p;
}

9.5.2 查找子连接

通常是连接是动态连接时使用, 在quick_inI1_outR1_authtail()和info_lookuphostpair()等函数中
调用:
struct connection *
find_client_connection(struct connection *c
         , const ip_subnet *our_net
         , const ip_subnet *peer_net
         , const u_int8_t our_protocol
         , const u_int16_t our_port
         , const u_int8_t peer_protocol
         , const u_int16_t peer_port)
{
    struct connection *d;
    struct spd_route *sr;
#ifdef DEBUG
    if (DBGP(DBG_CONTROLMORE))
    {
 char s1[SUBNETTOT_BUF],d1[SUBNETTOT_BUF];
 subnettot(our_net,  0, s1, sizeof(s1));
 subnettot(peer_net, 0, d1, sizeof(d1));
 DBG_log("find_client_connection starting with %s"
     , (c ? c->name : "(none)"));
 DBG_log("  looking for %s:%d/%d -> %s:%d/%d"
     , s1, our_protocol, our_port
     , d1, peer_protocol, peer_port);
    }
#endif /* DEBUG */
    /* give priority to current connection
     * but even greater priority to a routed concrete connection
     */
    {
 struct connection *unrouted = NULL;
 int srnum = -1;
// 遍历连接的安全策略路由,如果找到未路由的连接(unrouted != NULL),中断循环
 for (sr = &c->spd; unrouted == NULL && sr != NULL; sr = sr->next)
 {
     srnum++;
#ifdef DEBUG
     if (DBGP(DBG_CONTROLMORE))
     {
  char s2[SUBNETTOT_BUF],d2[SUBNETTOT_BUF];
  subnettot(&sr->this.client, 0, s2, sizeof(s2));
  subnettot(&sr->that.client, 0, d2, sizeof(d2));
  DBG_log("  concrete checking against sr#%d %s -> %s"
   , srnum, s2, d2);
     }
#endif /* DEBUG */
// 比较安全策略路由的本地子网,对端子网,本地协议,本地端口,对端协议,对端端口是否匹配
     if (samesubnet(&sr->this.client, our_net)
  && samesubnet(&sr->that.client, peer_net)
  && (sr->this.protocol == our_protocol)
  && (!sr->this.port || (sr->this.port == our_port))
  && (sr->that.protocol == peer_protocol)
  && (!sr->that.port || (sr->that.port == peer_port)))
     {
  passert(oriented(*c));
// 如果已经路由过了,返回该连接
  if (routed(sr->routing))
      return c;
// 没路由过的连接, 可以中断循环了
  unrouted = c;
     }
 }
 /* exact match? */
// 目前最多只有未路由的连接,进行更详细地查找
 d = fc_try(c, c->host_pair, NULL, our_net, peer_net
     , our_protocol, our_port, peer_protocol, peer_port);
 DBG(DBG_CONTROLMORE,
     DBG_log("  fc_try %s gives %s"
      , c->name
      , (d ? d->name : "none"))
 )
// 没找到,还是用未路由的连接
 if (d == NULL)
     d = unrouted;
    }

// 如果d为空, 没有找到合适的连接
    if (d == NULL)
    {
 /* look for an abstract connection to match */
 struct spd_route *sr;
 struct host_pair *hp = NULL;
// 遍历连接的安全策略路由,如果找到主机对(hp!=NULL),中断循环
 for (sr = &c->spd; hp==NULL && sr != NULL; sr = sr->next)
 {
// 根据连接双方的地址查找合适的主机对
     hp = find_host_pair(&sr->this.host_addr
    , sr->this.host_port
    , NULL
    , sr->that.host_port);
#ifdef DEBUG
     if (DBGP(DBG_CONTROLMORE))
     {
  char s2[SUBNETTOT_BUF],d2[SUBNETTOT_BUF];
  subnettot(&sr->this.client, 0, s2, sizeof(s2));
  subnettot(&sr->that.client, 0, d2, sizeof(d2));
  DBG_log("  checking hostpair %s -> %s is %s"
   , s2, d2
   , (hp ? "found" : "not found"));
     }
#endif /* DEBUG */
 }
 if (hp != NULL)
 {
// 找到, 使用该主机对作为参数重新查找
     /* RW match with actual peer_id or abstract peer_id? */
     d = fc_try(c, hp, NULL, our_net, peer_net
  , our_protocol, our_port, peer_protocol, peer_port);

// 如果还是没找到,重新进行OE查找
     if (d == NULL
     && subnetishost(our_net)
     && subnetishost(peer_net))
     {
  /* Opportunistic match?
   * Always use abstract peer_id.
   * Note that later instantiation will result in the same peer_id.
   */
  d = fc_try_oppo(c, hp, our_net, peer_net
      , our_protocol, our_port, peer_protocol, peer_port);
     }
 }
    }
    DBG(DBG_CONTROLMORE,
 DBG_log("  concluding with d = %s"
  , (d ? d->name : "none"))
    )
    return d;
}

/* find_client_connection: given a connection suitable for ISAKMP
 * (i.e. the hosts match), find a one suitable for IPSEC
 * (i.e. with matching clients).
 *
 * If we don't find an exact match (not even our current connection),
 * we try for one that still needs instantiation.  Try Road Warrior
 * abstract connections and the Opportunistic abstract connections.
 * This requires inverse instantiation: abstraction.
 *
 * After failing to find an exact match, we abstract the peer
 * to be NO_IP (the wildcard value).  This enables matches with
 * Road Warrior and Opportunistic abstract connections.
 *
 * After failing that search, we also abstract the Phase 1 peer ID
 * if possible.  If the peer's ID was the peer's IP address, we make
 * it NO_ID; instantiation will make it the peer's IP address again.
 *
 * If searching for a Road Warrior abstract connection fails,
 * and conditions are suitable, we search for the best Opportunistic
 * abstract connection.
 *
 * Note: in the end, both Phase 1 IDs must be preserved, after any
 * instantiation.  They are the IDs that have been authenticated.
 */
#define PATH_WEIGHT 1
#define WILD_WEIGHT (MAX_CA_PATH_LEN+1)
#define PRIO_WEIGHT (MAX_WILDCARDS+1)*WILD_WEIGHT
/* fc_try: a helper function for find_client_connection */
static struct connection *
fc_try(const struct connection *c
, struct host_pair *hp
, const struct id *peer_id UNUSED
, const ip_subnet *our_net
, const ip_subnet *peer_net
, const u_int8_t our_protocol
, const u_int16_t our_port
, const u_int8_t peer_protocol
, const u_int16_t peer_port)
{
    struct connection *d;
    struct connection *best = NULL;
    policy_prio_t best_prio = BOTTOM_PRIO;
    int wildcards, pathlen;
    const bool peer_net_is_host = subnetisaddr(peer_net, &c->spd.that.host_addr);
// 遍历主机对相关的所有连接
    for (d = hp->connections; d != NULL; d = d->hp_next)
    {
 struct spd_route *sr;
// 忽略组连接
 if (d->policy & POLICY_GROUP)
     continue;
// 检查本地ID,对端ID,对端CA是否匹配
 if (!(same_id(&c->spd.this.id, &d->spd.this.id)
       && match_id(&c->spd.that.id, &d->spd.that.id, &wildcards)
       && trusted_ca(c->spd.that.ca, d->spd.that.ca, &pathlen)))
     continue;
     /* compare protocol and ports */
// 检查本地协议,本地端口,对端协议,对端端口是否匹配
 if (d->spd.this.protocol != our_protocol
     ||  (d->spd.this.port && d->spd.this.port != our_port)
     ||  d->spd.that.protocol != peer_protocol
            || (d->spd.that.port != peer_port && !d->spd.that.has_port_wildcard))
     continue;
 /* non-Opportunistic case:
  * our_client must match.
  *
  * So must peer_client, but the testing is complicated
  * by the fact that the peer might be a wildcard
  * and if so, the default value of that.client
  * won't match the default peer_net.  The appropriate test:
  *
  * If d has a peer client, it must match peer_net.
  * If d has no peer client, peer_net must just have peer itself.
  */
// 该连接的以上参数全部匹配
// 遍历该连接的安全策略路由,判断是否是最优连接
 for (sr = &d->spd; best != d && sr != NULL; sr = sr->next)
 {
     policy_prio_t prio;
#ifdef DEBUG
     if (DBGP(DBG_CONTROLMORE))
     {
  char s1[SUBNETTOT_BUF],d1[SUBNETTOT_BUF];
  char s3[SUBNETTOT_BUF],d3[SUBNETTOT_BUF];
  subnettot(our_net,  0, s1, sizeof(s1));
  subnettot(peer_net, 0, d1, sizeof(d1));
  subnettot(&sr->this.client,  0, s3, sizeof(s3));
  subnettot(&sr->that.client,  0, d3, sizeof(d3));
  DBG_log("  fc_try trying "
   "%s:%s:%d/%d -> %s:%d/%d vs %s:%s:%d/%d -> %s:%d/%d"
   , c->name, s1, c->spd.this.protocol, c->spd.this.port
     , d1, c->spd.that.protocol, c->spd.that.port
   , d->name, s3, sr->this.protocol, sr->this.port
     , d3, sr->that.protocol, sr->that.port);
     }
#endif /* DEBUG */
// 检查本地子网是否相同
     if (!samesubnet(&sr->this.client, our_net))
  continue;
     if (sr->that.has_client)
     {
// 检查对端子网是否匹配
  if (sr->that.has_client_wildcard) {
      if (!subnetinsubnet(peer_net, &sr->that.client))
   continue;
  } else {
#ifdef VIRTUAL_IP
      if ((!samesubnet(&sr->that.client, peer_net)) && (!
is_virtual_connection(d)))
#else
      if (!samesubnet(&sr->that.client, peer_net))
#endif
   continue;
#ifdef VIRTUAL_IP
      if ((is_virtual_connection(d)) &&
   ( (!is_virtual_net_allowed(d, peer_net, &c->spd.that.host_addr))
||
   (is_virtual_net_used(peer_net, peer_id?peer_id:&c->spd.that.id))
))
       continue;
#endif
  }
     }
     else
     {
// 如果对端是子网,循环跳过
  if (!peer_net_is_host)
      continue;
     }
     /* We've run the gauntlet -- success:
      * We've got an exact match of subnets.
      * The connection is feasible, but we continue looking for the best.
      * The highest priority wins, implementing eroute-like rule.
      * - a routed connection is preferrred
      * - given that, the smallest number of ID wildcards are preferred
      * - given that, the shortest CA pathlength is preferred
      */
// 计算优先权
     prio = PRIO_WEIGHT * routed(sr->routing)
   + WILD_WEIGHT * (MAX_WILDCARDS - wildcards)
   + PATH_WEIGHT * (MAX_CA_PATH_LEN - pathlen)
   + 1;
// 如果该优先权超过限值,可以算最优的连接了
     if (prio > best_prio)
     {
  best = d;
  best_prio = prio;
     }
 }
    }
    if (best != NULL && NEVER_NEGOTIATE(best->policy))
 best = NULL;
    DBG(DBG_CONTROLMORE,
 DBG_log("  fc_try concluding with %s [%ld]"
  , (best ? best->name : "none"), best_prio)
    )
    return best;
}

9.5.3 根据状态精炼合适的连接

/* given an up-until-now satisfactory connection, find the best connection
 * now that we just got the Phase 1 Id Payload from the peer.
 *
 * Comments in the code describe the (tricky!) matching criteria.
 * Although this routine could handle the initiator case,
 * it isn't currently called in this case.
 * If it were, it could "upgrade" an Opportunistic Connection
 * to a Road Warrior Connection if a suitable Peer ID were found.
 *
 * In RFC 2409 "The Internet Key Exchange (IKE)",
 * in 5.1 "IKE Phase 1 Authenticated With Signatures", describing Main
 * Mode:
 *
 *         Initiator                          Responder
 *        -----------                        -----------
 *         HDR, SA                     -->
 *                                     <--    HDR, SA
 *         HDR, KE, Ni                 -->
 *                                     <--    HDR, KE, Nr
 *         HDR*, IDii, [ CERT, ] SIG_I -->
 *                                     <--    HDR*, IDir, [ CERT, ] SIG_R
 *
 * In 5.4 "Phase 1 Authenticated With a Pre-Shared Key":
 *
 *               HDR, SA             -->
 *                                   <--    HDR, SA
 *               HDR, KE, Ni         -->
 *                                   <--    HDR, KE, Nr
 *               HDR*, IDii, HASH_I  -->
 *                                   <--    HDR*, IDir, HASH_R
 *
 * refine_host_connection could be called in two case:
 *
 * - the Responder receives the IDii payload:
 *   + [PSK] after using PSK to decode this message
 *   + before sending its IDir payload
 *   + before using its ID in HASH_R computation
 *   + [DSig] before using its private key to sign SIG_R
 *   + before using the Initiator's ID in HASH_I calculation
 *   + [DSig] before using the Initiator's public key to check SIG_I
 *
 * - the Initiator receives the IDir payload:
 *   + [PSK] after using PSK to encode previous message and decode this message
 *   + after sending its IDii payload
 *   + after using its ID in HASH_I computation
 *   + [DSig] after using its private key to sign SIG_I
 *   + before using the Responder's ID to compute HASH_R
 *   + [DSig] before using Responder's public key to check SIG_R
 *
 * refine_host_connection can choose a different connection, as long as
 * nothing already used is changed.
 *
 * In the Initiator case, the particular connection might have been
 * specified by whatever provoked Pluto to initiate.  For example:
 * whack --initiate connection-name
 * The advantages of switching connections when we're the Initiator seem
 * less important than the disadvantages, so after FreeS/WAN 1.9, we
 * don't do this.
 */
struct connection *
refine_host_connection(const struct state *st, const struct id *peer_id
, bool initiator, bool aggrmode)
{
// 状态对应的连接
    struct connection *c = st->st_connection;
// 认证方法
    u_int16_t auth = st->st_oakley.auth;
    struct connection *d;
    struct connection *best_found = NULL;
    lset_t auth_policy;
// 策略模式
    lset_t p1mode_policy = aggrmode ? POLICY_AGGRESSIVE : LEMPTY;
    const struct RSA_private_key *my_RSA_pri = NULL;
// 对方IP是否不确定
    bool wcpip; /* wildcard Peer IP? */
    int wildcards, best_wildcards;
    int our_pathlen, best_our_pathlen, peer_pathlen, best_peer_pathlen;
    chunk_t peer_ca;
    const chunk_t *psk;
    psk = NULL;
    our_pathlen = peer_pathlen = 0;
    best_our_pathlen  = 0;
    best_peer_pathlen = 0;
    wildcards = best_wildcards = 0;
    DBG(DBG_CONTROLMORE
  , DBG_log("refine_connection: starting with %s"
     , c->name));
// 根据对方ID获取对方CA
    peer_ca = get_peer_ca(peer_id);
// 检查对端ID是否匹配
    if (same_id(&c->spd.that.id, peer_id)
// 是否是信任的CA
 && trusted_ca(peer_ca, c->spd.that.ca, &peer_pathlen)
 && peer_pathlen == 0
// 是否是请求的CA
 && match_requested_ca(c->requested_ca, c->spd.this.ca, &our_pathlen)
 && our_pathlen == 0
 ) {
 DBG(DBG_CONTROLMORE
     , DBG_log("refine_connection: happy with starting point: %s"
        , c->name));
// OK, 返回状态对应连接
 return c; /* peer ID matches current connection -- look no further */
    }
#if defined(XAUTH)
// 如果是XAUTH, 更新认证方法
    auth = xauth_calcbaseauth(auth);
#endif
    switch (auth)
    {
// 共享密钥认证
    case OAKLEY_PRESHARED_KEY:
 auth_policy = POLICY_PSK;
// 获取共享密钥
 psk = get_preshared_secret(c);
 /* It should be virtually impossible to fail to find PSK:
  * we just used it to decode the current message!
  */
 if (psk == NULL)
     return NULL; /* cannot determine PSK! */
 break;
// RSA签名认证  
    case OAKLEY_RSA_SIG:
 auth_policy = POLICY_RSASIG;
 if (initiator && c->spd.this.sc == NULL)
 {
     /* at this point, we've committed to our RSA private key:
      * we used it in our previous message.
      */
// 获取RSA密钥
     my_RSA_pri = get_RSA_private_key(c);
     if (my_RSA_pri == NULL)
  return NULL; /* cannot determine my RSA private key! */
 }
 break;
    default:
 bad_case(auth);
    }
    /* The current connection won't do: search for one that will.
     * First search for one with the same pair of hosts.
     * If that fails, search for a suitable Road Warrior or Opportunistic
     * connection (i.e. wildcard peer IP).
     * We need to match:
     * - peer_id (slightly complicated by instantiation)
     * - if PSK auth, the key must not change (we used it to decode message)
     * - policy-as-used must be acceptable to new connection
     * - if initiator, also:
     *   + our ID must not change (we sent it in previous message)
     *   + our RSA key must not change (we used in in previous message)
     */
// 连接C对应的主机对相关的连接链表
    d = c->host_pair->connections;
    for (wcpip = FALSE; ; wcpip = TRUE)
    {
// 遍历链表
 for (; d != NULL; d = d->hp_next)
 {
// 对端ID匹配
     bool match1 = match_id(peer_id, &d->spd.that.id, &wildcards);
// 是否信任的CA
     bool match2 = trusted_ca(peer_ca, d->spd.that.ca, &peer_pathlen);
// 是否是请求的CA
     bool match3 = match_requested_ca(c->requested_ca, d->spd.this.ca,
&our_pathlen);
// 三个条件都要满足
     bool match = match1 && match2 && match3;
     DBG(DBG_CONTROLMORE
  , DBG_log("refine_connection: checking %s against %s, best=%s with
match=%d(id=%d/ca=%d/reqca=%d)"
     , c->name, d->name, best_found ? best_found->name : "(none)"
     , match, match1, match2, match3));
     /* ignore group connections */
// 组策略跳过
     if (d->policy & POLICY_GROUP)
  continue;
     /* check if peer_id matches, exactly or after instantiation */
// 不匹配跳过
     if (!match)
  continue;
     /* if initiator, our ID must match exactly */
// 如果是初始方, 检查本地ID是否匹配
     if (initiator && !same_id(&c->spd.this.id, &d->spd.this.id))
  continue;
     /* authentication used must fit policy of this connection */
// 策略检查
     if ((d->policy & auth_policy) == LEMPTY)
  continue; /* our auth isn't OK for this connection */
// 策略模式检查
     if ((d->policy & POLICY_AGGRESSIVE) ^ p1mode_policy)
  continue;   /* disallow phase1 main/aggressive mode mismatch */
#ifdef XAUTH
// 是否是相同的XAUTH角色, 都是服务器还是都是客户端
     if (d->spd.this.xauth_server != c->spd.this.xauth_server)
  continue; /* disallow xauth/no xauth mismatch */
     if (d->spd.this.xauth_client != c->spd.this.xauth_client)
  continue; /* disallow xauth/no xauth mismatch */
#endif
     DBG(DBG_CONTROLMORE
  , DBG_log("refine_connection: checked %s against %s, now for see if
best"
     , c->name, d->name));
     switch (auth)
     {
     case OAKLEY_PRESHARED_KEY:
  /* secret must match the one we already used */
  {
// 获取连接d的共享密钥
      const chunk_t *dpsk = get_preshared_secret(d);
      if (dpsk == NULL)
   continue; /* no secret */
// 如果此共享密钥和连接c的共享密钥不匹配,跳过
      if (psk != dpsk)
   if (psk->len != dpsk->len
   || memcmp(psk->ptr, dpsk->ptr, psk->len) != 0)
       continue; /* different secret */
  }
  break;
     case OAKLEY_RSA_SIG:
  /* We must at least be able to find our private key.
   * If we initiated, it must match the one we
   * used in the SIG_I payload that we sent previously.
   */
   if (d->spd.this.sc == NULL) /* no smartcard */
  {
// 获取连接d的RSA密钥
      const struct RSA_private_key *pri
   = get_RSA_private_key(d);
// 如果和连接c的RSA密钥不匹配,跳过
      if (pri == NULL
      || (initiator && (c->spd.this.sc != NULL
   || !same_RSA_public_key(&my_RSA_pri->pub, &pri->pub))))
       continue;
  }
  else if (initiator && c->spd.this.sc != d->spd.this.sc)
      continue;
  break;
     default:
  bad_case(auth);
     }
     /* d has passed all the tests.
      * We'll go with it if the Peer ID was an exact match.
      */
// 前面都匹配,而且匹配时的各返回参数都是0, 可返回该连接
     if (match && wildcards == 0 && peer_pathlen == 0 && our_pathlen == 0)
  return d;
     /* We'll remember it as best_found in case an exact
      * match doesn't come along.
      */
// 根据这3个返回参数更新最优连接
     if (best_found == NULL || wildcards < best_wildcards
     || ((wildcards == best_wildcards && peer_pathlen < best_peer_pathlen)
  || (peer_pathlen == best_peer_pathlen && our_pathlen <
best_our_pathlen)))
     {
  DBG(DBG_CONTROLMORE
      , DBG_log("refine_connection: picking new best %s (wild=%d,
peer_pathlen=%d/our=%d)"
          , d->name
          , wildcards, peer_pathlen, our_pathlen));
  best_found = d;
  best_wildcards = wildcards;
  best_peer_pathlen = peer_pathlen;
  best_our_pathlen = our_pathlen;
     }
 }
// 如果是动态对端,返回该最合适连接
 if (wcpip)
     return best_found; /* been around twice already */
 /* Starting second time around.
  * We're willing to settle for a connection that needs Peer IP
  * instantiated: Road Warrior or Opportunistic.
  * Look on list of connections for host pair with wildcard Peer IP
  */
// 根据本地地址端口和对方端口查找主机对的连接
 d = find_host_pair_connections(__FUNCTION__, &c->spd.this.host_addr
           , c->spd.this.host_port
           , (ip_address *)NULL
           , c->spd.that.host_port);
    }
}

9.5.4 根据地址端口查找连接

/* find head of list of connections with this pair of hosts */
static struct connection *
find_host_pair_connections(const char *func
      , const ip_address *myaddr, u_int16_t myport   

      , const ip_address *hisaddr, u_int16_t hisport)
{
    char b1[ADDRTOT_BUF],b2[ADDRTOT_BUF];
// 根据本地地址,本地端口,对端地址,对端端口查找主机对
    struct host_pair *hp = find_host_pair(myaddr, myport, hisaddr, hisport);
    DBG(DBG_CONTROLMORE
 , DBG_log("find_host_pair_conn (%s): %s:%d %s:%d -> hp:%s\n"
    , func
    , (addrtot(myaddr,  0, b1, sizeof(b1)), b1)
    , myport
    , hisaddr ? (addrtot(hisaddr, 0, b2, sizeof(b2)), b2) : "%any"
    , hisport
    , (hp && hp->connections) ? hp->connections->name : "none"));
// 如果找到主机对, 返回主机对对应的连接(这是个链表, 返回的是链表头)    
    return hp == NULL? NULL : hp->connections;
}
 
9.5.5 查找合适安全策略路由的连接
/* Find the connection to connection c's peer's client with the
 * largest value of .routing.  All other things being equal,
 * preference is given to c.  If none is routed, return NULL.
 *
 * If erop is non-null, set *erop to a connection sharing both
 * our client subnet and peer's client subnet with the largest value
 * of .routing.  If none is erouted, set *erop to NULL.
 *
 * The return value is used to find other connections sharing a route.
 * *erop is used to find other connections sharing an eroute.
 */
struct connection *
route_owner(struct connection *c
     , struct spd_route **srp
     , struct connection **erop
     , struct spd_route **esrp)
{
    struct connection *d
 , *best_ro = c
 , *best_ero = c;
    struct spd_route *srd, *src;
    struct spd_route *best_sr, *best_esr;
    enum routing_t best_routing, best_erouting;
    passert(oriented(*c));
    best_sr  = NULL;
    best_esr = NULL;
    best_routing = c->spd.routing;
    best_erouting = best_routing;
// 遍历系统连接链表
    for (d = connections; d != NULL; d = d->ac_next)
    {
// 遍历每个连接的安全策略路由连接
 for (srd = &d->spd; srd; srd = srd->next)
 {
// 非路由,跳过
     if (srd->routing == RT_UNROUTED)
  continue;
// 遍历连接c的安全策略路由链表
     for (src = &c->spd; src; src=src->next)
     {
// 比较这两个安全策略路由参数是否匹配
// 对端子网
  if (!samesubnet(&src->that.client, &srd->that.client))
      continue;
// 对端协议
  if (src->that.protocol != srd->that.protocol)
      continue;
// 对端端口
  if (src->that.port != srd->that.port)
      continue;
  passert(oriented(*d));
// best_routing初始化为c的路由
// 如果新路由的路由值更大, 更新best_ro
  if (srd->routing > best_routing)
  {
      best_ro = d;
      best_sr = srd;
      best_routing = srd->routing;
  }
// 比较本地子网
  if (!samesubnet(&src->this.client, &srd->this.client))
      continue;
// 本地协议
  if (src->this.protocol != srd->this.protocol)
      continue;
// 本地端口
  if (src->this.port != srd->this.port)
      continue;
// best_erouting初始化为c的路由
// 如果新路由的路由值更大, 更新best_ero
  if (srd->routing > best_erouting)
  {
      best_ero = d;
      best_esr = srd;
      best_erouting = srd->routing;
  }
     }
 }
    }
// 打印调试信息
    DBG(DBG_CONTROL,
 {
     char cib[CONN_INST_BUF];
     err_t m = builddiag("route owner of \"%s\"%s %s:"
  , c->name
  , (fmt_conn_instance(c, cib), cib)
  , enum_name(&routing_story, c->spd.routing));
     if (!routed(best_ro->spd.routing))
  m = builddiag("%s NULL", m);
     else if (best_ro == c)
  m = builddiag("%s self", m);
     else
  m = builddiag("%s \"%s\"%s %s", m
      , best_ro->name
      , (fmt_conn_instance(best_ro, cib), cib)
      , enum_name(&routing_story, best_ro->spd.routing));
     if (erop != NULL)
     {
  m = builddiag("%s; eroute owner:", m);
  if (!erouted(best_ero->spd.routing))
      m = builddiag("%s NULL", m);
  else if (best_ero == c)
      m = builddiag("%s self", m);
  else
      m = builddiag("%s \"%s\"%s %s", m
   , best_ero->name
   , (fmt_conn_instance(best_ero, cib), cib)
   , enum_name(&routing_story, best_ero->spd.routing));
     }
     DBG_log("%s", m);
 });
// 将best_ero返回
    if (erop != NULL)
 *erop = erouted(best_erouting)? best_ero : NULL;
// 将best_sr和best_esr返回
    if (srp != NULL )
    {
 *srp = best_sr;
 if (esrp != NULL )
     *esrp = best_esr;
    }
// 返回找到的best_ro
    return routed(best_routing)? best_ro : NULL;
}

9.6 输出连接状态

void
show_connections_status(void)
{
    struct connection *c;
    int count, i;
    struct connection **array;

    /* make an array of connections, and sort it */
    count=0;
// 连接链表计数
    for (c = connections; c != NULL; c = c->ac_next)
    {
 count++;
    }
// 分配能容纳所有连接指针的空间, 注意只是指针, 不是整个结构
    array = alloc_bytes(sizeof(struct connection *)*count, "connection array");
    count=0;
// 复制所有连接指针
    for (c = connections; c != NULL; c = c->ac_next)
    {
 array[count++]=c;
    }
    /* sort it! */
// 快速排序,按名称,类型,优先权值排序
    qsort(array, count, sizeof(struct connection *), connection_compare_qsort);
// 遍历所有连接
    for (i=0; i<count; i++)
    {
 const char *ifn;
 char instance[1 + 10 + 1];
 char prio[POLICY_PRIO_BUF];
 c = array[i];
// ipsec网卡名称
 ifn = oriented(*c)? c->interface->ip_dev->id_rname : "";
 instance[0] = '\0';
// 将实例连接的序列号转换为字符串
 if (c->kind == CK_INSTANCE && c->instance_serial != 0)
     snprintf(instance, sizeof(instance), "[%lu]", c->instance_serial);
 /* show topology */
 {
     char topo[CONNECTION_BUF];
     struct spd_route *sr = &c->spd;
     int num=0;
// 遍历安全策略路由
     while (sr != NULL)
     {
  char srcip[ADDRTOT_BUF], dstip[ADDRTOT_BUF];
  char thissemi[3+sizeof("srcup=")];
  char thatsemi[3+sizeof("dstup=")];
  char *thisup, *thatup;
// 输出连接信息到topo数组
  (void) format_connection(topo, sizeof(topo), c, sr);
// 向whack输出
  whack_log(RC_COMMENT, "\"%s\"%s: %s; %s; eroute owner: #%lu"
     , c->name, instance, topo
     , enum_name(&routing_story, sr->routing)
     , sr->eroute_owner);
// 本地地址
  if(addrbytesptr(&c->spd.this.host_srcip, NULL) == 0
     || isanyaddr(&c->spd.this.host_srcip)) {
      strcpy(srcip, "unset");
  } else {
      addrtot(&sr->this.host_srcip, 0, srcip, sizeof(srcip));
  }
// 对端地址
  if(addrbytesptr(&c->spd.that.host_srcip, NULL) == 0
     || isanyaddr(&c->spd.that.host_srcip)) {
      strcpy(dstip, "unset");
  } else {
      addrtot(&sr->that.host_srcip, 0, dstip, sizeof(dstip));
  }
  thissemi[0]='\0';
  thisup=thissemi;
  if(sr->this.updown) {
      thissemi[0]=';';
      thissemi[1]=' ';
      thissemi[2]='\0';
      strcat(thissemi, "srcup=");
      thisup=sr->this.updown;
  }
  
  thatsemi[0]='\0';
  thatup=thatsemi;
  if(sr->that.updown) {
      thatsemi[0]=';';
      thatsemi[1]=' ';
      thatsemi[2]='\0';
      strcat(thatsemi, "dstup=");
      thatup=sr->that.updown;
  }
// 向whack输出  
  whack_log(RC_COMMENT, "\"%s\"%s:     srcip=%s; dstip=%s%s%s%s%s;"
     , c->name, instance, srcip, dstip
     , thissemi, thisup
     , thatsemi, thatup);
  sr = sr->next;
  num++;
     }
 }
// 输出CA信息
 /* show CAs */
 if (c->spd.this.ca.ptr != NULL || c->spd.that.ca.ptr != NULL)
 {
     char this_ca[IDTOA_BUF], that_ca[IDTOA_BUF];
     dntoa_or_null(this_ca, IDTOA_BUF, c->spd.this.ca, "%any");
     dntoa_or_null(that_ca, IDTOA_BUF, c->spd.that.ca, "%any");
     whack_log(RC_COMMENT
  , "\"%s\"%s:   CAs: '%s'...'%s'"
  , c->name
  , instance
  , this_ca
  , that_ca);
 }
// 输出生命期
 whack_log(RC_COMMENT
     , "\"%s\"%s:   ike_life: %lus; ipsec_life: %lus;"
     " rekey_margin: %lus; rekey_fuzz: %lu%%; keyingtries: %lu"
     , c->name
     , instance
     , (unsigned long) c->sa_ike_life_seconds
     , (unsigned long) c->sa_ipsec_life_seconds
     , (unsigned long) c->sa_rekey_margin
     , (unsigned long) c->sa_rekey_fuzz
     , (unsigned long) c->sa_keying_tries);
 if (c->policy_next)
 {
     whack_log(RC_COMMENT
        , "\"%s\"%s:   policy_next: %s"
        , c->name, instance, c->policy_next->name);
 }
 /* Note: we display key_from_DNS_on_demand as if policy [lr]KOD */
// 输出策略优先级信息
 fmt_policy_prio(c->prio, prio);
 whack_log(RC_COMMENT
     , "\"%s\"%s:   policy: %s%s%s; prio: %s; interface: %s; encap: %s;"
     , c->name
     , instance
     , prettypolicy(c->policy)
     , c->spd.this.key_from_DNS_on_demand? "+lKOD" : ""
     , c->spd.that.key_from_DNS_on_demand? "+rKOD" : ""
     , prio
     , ifn
    ,c->forceencaps ? "udp" : "esp");
// 输出DPD信息
 /* slightly complicated stuff to avoid extra crap */
 if(c->dpd_timeout > 0 || DBGP(DBG_DPD)) {
     whack_log(RC_COMMENT
        , "\"%s\"%s:   dpd: %s; delay:%lu; timeout:%lu; "
        , c->name
        , instance
        , enum_name(&dpd_action_names, c->dpd_action)
        , c->dpd_delay, c->dpd_timeout);
 }
 if(c->extra_debugging) {
     whack_log(RC_COMMENT, "\"%s\"%s:   debug: %s"
        , c->name
        , instance
        , bitnamesof(debug_bit_names
       , c->extra_debugging));
 }
// 输出SA信息
 whack_log(RC_COMMENT
     , "\"%s\"%s:   newest ISAKMP SA: #%ld; newest IPsec SA: #%ld; "
     , c->name
     , instance
     , c->newest_isakmp_sa
     , c->newest_ipsec_sa);
#ifdef IKE_ALG
// 输出IKE算法信息
 ike_alg_show_connection(c, instance);
#endif
#ifdef KERNEL_ALG
// 输出内核算法信息
 kernel_alg_show_connection(c, instance);
#endif
    }
// 释放数组
    pfree(array);
}

...... 待续 ......