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

10. 主机对(host_pair)

主机对(host_pair)是连接结构的组成部分, 也都是在program/pluto/connections.c中定义, 只用来定义连接双方的地址对信息, 但又是相对独立的, 不是一一对应的, 也就是每个连接有一个主机对, 但一个主机对可能对应多个连接, 系统里的主机对单独形成了一个链表。

10.1 数据结构
struct host_pair {
// 本地和对端的地址和端口信息
    struct {
 ip_address addr;
 u_int16_t  host_port;         /* IKE port */
 bool       host_port_specific; /* if above is interesting */
    } me, him;
    bool initial_connection_sent;
// 相关的连接结构
    struct connection *connections; /* connections with this pair */
// 等待加密通道建立的等待结构
    struct pending *pending; /* awaiting Keying Channel */
// 主机对链表的下一项
    struct host_pair *next;
};

10.2 查找主机对

根据本地和对端的地址端口信息查找主机对
static struct host_pair *
find_host_pair(const ip_address *myaddr
        , u_int16_t myport
        , const ip_address *hisaddr
        , u_int16_t hisport)
{
    struct host_pair *p, *prev;
    char b1[ADDRTOT_BUF],b2[ADDRTOT_BUF];
    /* default hisaddr to an appropriate any */
    if (hisaddr == NULL)
 hisaddr = aftoinfo(addrtypeof(myaddr))->any;
    /*
     * look for a host-pair that has the right set of ports/address.
     *
     */
   
    /*
     * for the purposes of comparison, port 500 and 4500 are identical,
     * but other ports are not.
     * So if any port==4500, then set it to 500.
     */
// 500和4500端口是等价的
    if(myport == 4500) myport=500;
    if(hisport== 4500) hisport=500;

// 遍历主机对链表, prev参数准备用于节点的断开
    for (prev = NULL, p = host_pairs; p != NULL; prev = p, p = p->next)
    {
 DBG(DBG_CONTROLMORE
     , DBG_log("find_host_pair: comparing to %s:%d %s:%d\n"
        , (addrtot(&p->me.addr, 0, b1, sizeof(b1)), b1)
        , p->me.host_port
        , (addrtot(&p->him.addr, 0, b2, sizeof(b2)), b2)
        , p->him.host_port));
// 比较地址端口是否匹配    
 if (sameaddr(&p->me.addr, myaddr)
     && (!p->me.host_port_specific || p->me.host_port == myport)
     && sameaddr(&p->him.addr, hisaddr)
     && (!p->him.host_port_specific || p->him.host_port == hisport)
     )
 {
// 匹配
     if (prev != NULL)
     {
// 将该节点从当前位置断开, 插到链表头, 而如果prev为空,则说明该节点本来就是链表头
// 不需要调整了
  prev->next = p->next; /* remove p from list */
  p->next = host_pairs; /* and stick it on front */
  host_pairs = p;
     }
     break;
 }
    }
    return p;
}

10.3 从连接生成主机对

static void
connect_to_host_pair(struct connection *c)
{
// 连接是否已经确定方向,即本地和对端谁是left, 谁是right
    if (oriented(*c))
    {
// 根据连接安全策略连接的本地和对端地址端口查找主机对结构
 struct host_pair *hp = find_host_pair(&c->spd.this.host_addr
           , c->spd.this.host_port
           , &c->spd.that.host_addr
           , c->spd.that.host_port);
 char b1[ADDRTOT_BUF],b2[ADDRTOT_BUF];
 DBG(DBG_CONTROLMORE
     , DBG_log("connect_to_host_pair: %s:%d %s:%d -> hp:%s\n"
        , (addrtot(&c->spd.this.host_addr, 0, b1,sizeof(b1)), b1)
        , c->spd.this.host_port
        , (addrtot(&c->spd.that.host_addr, 0, b2,sizeof(b2)), b2)
        , c->spd.that.host_port
        , (hp && hp->connections) ? hp->connections->name : "none"));
// 没找到, 新建主机对结构    
 if (hp == NULL)
 {
     /* no suitable host_pair -- build one */
// 分配结构
     hp = alloc_thing(struct host_pair, "host_pair");
// 本地地址
     hp->me.addr = c->spd.this.host_addr;
// 对端地址
     hp->him.addr = c->spd.that.host_addr;
#ifdef NAT_TRAVERSAL
// 本地端口
     hp->me.host_port = nat_traversal_enabled ? pluto_port : c->spd.this.host_port;
// 对端端口
     hp->him.host_port = nat_traversal_enabled ? pluto_port : c->spd.that.host_port;
#else
     hp->me.host_port = c->spd.this.host_port;
      hp->him.host_port = c->spd.that.host_port;
#endif
     hp->initial_connection_sent = FALSE;
     hp->connections = NULL;
     hp->pending = NULL;
// 添加到系统的主机对链表头
     hp->next = host_pairs;
     host_pairs = hp;
 }
// 连接和主机对结构互指
 c->host_pair = hp;
// 将该连接作为主机对的连接链表的链表头
 c->hp_next = hp->connections;
 hp->connections = c;
    }
    else
    {
 /* since this connection isn't oriented, we place it
  * in the unoriented_connections list instead.
  */
// 主机对为空
 c->host_pair = NULL;
// 连接挂接到未定向连接链表的链表头
 c->hp_next = unoriented_connections;
 unoriented_connections = c;
    }
}

10.4 连接方向调整

根据连接所定义的双方地址和自己的ipsec*网卡地址确定连接的方向

/* adjust orientations of connections to reflect newly added interfaces */
void
check_orientations(void)
{
    /* try to orient all the unoriented connections */
    {
// 未定向的连接链表头
 struct connection *c = unoriented_connections;
// 经过该函数处理后将没有未定向的连接
 unoriented_connections = NULL;
// 遍历链表
 while (c != NULL)
 {
     struct connection *nxt = c->hp_next;
// 连接定向操作
     (void)orient(c);
// 连接转主机对处理
     connect_to_host_pair(c);
     c = nxt;
 }
    }
    /* Check that no oriented connection has become double-oriented.
     * In other words, the far side must not match one of our new interfaces.
     */
    {
 struct iface_port *i;
// 遍历ipsec*网卡链表
 for (i = interfaces; i != NULL; i = i->next)
 {
// 表示是新加的网卡
     if (i->change == IFN_ADD)
     {
  struct host_pair *hp;
// 遍历主机对链表
  for (hp = host_pairs; hp != NULL; hp = hp->next)
  {
// 对端地址和网卡地址相同? 表示是定向错了, 需要重新调整
      if (sameaddr(&hp->him.addr, &i->ip_addr)
   && (kern_interface!=NO_KERNEL || hp->him.host_port == pluto_port))
      {
   /* bad news: the whole chain of connections
    * hanging off this host pair has both sides
    * matching an interface.
    * We'll get rid of them, using orient and
    * connect_to_host_pair.  But we'll be lazy
    * and not ditch the host_pair itself (the
    * cost of leaving it is slight and cannot
    * be induced by a foe).
    */
// 该主机对相关的连接链表, 这些连接方向都错了
   struct connection *c = hp->connections;
// 清空该主机对相关链表
   hp->connections = NULL;
   while (c != NULL)
   {
       struct connection *nxt = c->hp_next;
// 重新调整原来各个连接的方向, 重新建立主机对
// 设置连接的网卡为空, 表示尾定向
       c->interface = NULL;
       (void)orient(c);
       connect_to_host_pair(c);
       c = nxt;
   }
      }
  }
     }
 }
    }
}

// 连接定向
bool
orient(struct connection *c)
{
    struct spd_route *sr;
// 如果连接没定向, 即连接的网卡为空
    if (!oriented(*c))
    {
 struct iface_port *p;
// 遍历连接的安全策略路由链表
 for (sr = &c->spd; sr; sr = sr->next)
 {
     /* Note: this loop does not stop when it finds a match:
      * it continues checking to catch any ambiguity.
      */
// 遍历系统网卡链表
     for (p = interfaces; p != NULL; p = p->next)
     {
#ifdef NAT_TRAVERSAL
// NAT穿越时,端口不固定, 跳过
  if (p->ike_float) continue;
#endif
// 又加一个死循环,为什么要死循环呢?虽然正常应该是最多循环两次就成,出现异常呢?
  for (;;)
  {
      /* check if this interface matches this end */
// 检查安全策略路由的本地地址和网卡地址是否匹配
      if (sameaddr(&sr->this.host_addr, &p->ip_addr)
   && (kern_interface != NO_KERNEL
       || sr->this.host_port == pluto_port))
      {
   if (oriented(*c))
   {
// 如果该连接已经定向, 即连接的ipsec*网卡非空
// 说明有地址重复的ipsec*网卡
       if (c->interface->ip_dev == p->ip_dev)
    loglog(RC_LOG_SERIOUS
           , "both sides of \"%s\" are our interface %s!"
           , c->name, p->ip_dev->id_rname);
       else
    loglog(RC_LOG_SERIOUS, "two interfaces match \"%s\" (%s, %s)"
           , c->name, c->interface->ip_dev->id_rname, p->ip_dev->id_rname);
// 取消连接的定向, 返回失败
       c->interface = NULL; /* withdraw orientation */
       return FALSE;
   }
// 连接的网卡指定为该地址匹配的网卡
   c->interface = p;
      }
      /* done with this interface if it doesn't match that end */
// 如果该网卡地址不匹配对端地址, 说明查找成功
      if (!(sameaddr(&sr->that.host_addr, &p->ip_addr)
     && (kern_interface!=NO_KERNEL
         || sr->that.host_port == pluto_port)))
   break;
      /* swap ends and try again.
       * It is a little tricky to see that this loop will stop.
       * Only continue if the far side matches.
       * If both sides match, there is an error-out.
       */
// 否则对调安全策略路由的本地和对端的地址信息
      {
   struct end t = sr->this;
   sr->this = sr->that;
   sr->that = t;
      }
  }
     }
 }
    }
    return oriented(*c);
}

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