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

7. 内核接口

pluto可支持内核使用KLIPS或NETKEY实现IPSEC,前者的通信接口是PF_KEY, 后者的通信接口是netlink, 另外也支持内核无IPSEC的情况(NO_KERNEL), 不过则基本就没什么意义。

KLIPS的IPSEC实现是通过构造ipsec*虚拟网卡来实现的, 将数据从该网卡发送, 就意味着对数据进行加密; 从该网卡获取数据, 就是对数据包进行解密。因此安全策略实际是根据路由来进行的,因此配置加密路由就是配置安全策略,因此专门引入了eroute概念来描述这类路由。

7.1 初始化

/* programs/pluto/kernel.c */

void
init_kernel(void)
{
    struct utsname un;
    /* get kernel version */
// 获取系统版本信息, 等同"uname -a"
    uname(&un);
    strncpy(kversion, un.release, sizeof(kversion));
    if (kern_interface == NO_KERNEL)
    {
        kernel_ops = &noklips_kernel_ops;
        return;
    }
// 初始化pfkey
    init_pfkey();
#if defined(KLIPS) && defined(KERNEL26_SUPPORT)
// 如果是自动选择
    if(kern_interface == AUTO_PICK)
    {
        bool linux_ipsec = 0;
        struct stat buf;
// 检查是否有"/proc/net/pfkey"文件,该文件是2.6的native linux下才有的
// KLIPS可以支持2.6, 但和2.6自带的NETKEY不能同时使用,只能2选1
        linux_ipsec = (stat("/proc/net/pfkey", &buf) == 0);
        if (linux_ipsec)
            {
  kern_interface = USE_NETKEY;
            }
        else
            {
                kern_interface = USE_KLIPS;
            }
    }
#endif

// 根据内核接口类型将内核操作结构初始化相应的KLIPS或NETKEY内核操作
    switch(kern_interface) {
#if defined(KERNEL26_SUPPORT)
    case USE_NETKEY:
 openswan_log("Using NETKEY IPsec interface code on %s"
       , kversion);
 kernel_ops = &linux_kernel_ops;
 break;
#endif
#if defined(KLIPS)
    case USE_KLIPS:
 openswan_log("Using KLIPS IPsec interface code on %s"
       , kversion);
 kernel_ops = &klips_kernel_ops;
 break;
#endif
    default:
 openswan_log("kernel interface '%s' not available"
       , enum_name(&kern_interface_names, kern_interface));
 exit(5);
    }
// 初始化操作
    if (kernel_ops->init)
    {
        kernel_ops->init();
    }
    /* register SA types that we can negotiate */
    can_do_IPcomp = FALSE;  /* until we get a response from KLIPS */
// 登记PFKEY
    kernel_ops->pfkey_register();

// 如果没有定义策略生存期
    if (!kernel_ops->policy_lifetime)
    {
// 进行事件调度
        event_schedule(EVENT_SHUNT_SCAN, SHUNT_SCAN_INTERVAL, NULL);
    }
}

内核操作结构:
/* programs/pluto/kernel.h */
struct kernel_ops {
// 类型
 enum {
         KERNEL_TYPE_NONE,
  KERNEL_TYPE_KLIPS,
  KERNEL_TYPE_LINUX,
 } type;
// 名称
   const char *opname;
// 是否有向内的加密路由
 bool inbound_eroute;
// 是否有策略生存期
 bool policy_lifetime;
// 回放窗口大小
        int  replay_window;
// 通信描述符
 int *async_fdp;
// 初始化
 void (*init)(void);
// 登记
 void (*pfkey_register)(void);
// 登记回应
 void (*pfkey_register_response)(const struct sadb_msg *msg);
// 队列处理
 void (*process_queue)(void);
// 消息处理
 void (*process_msg)(void);
// 原始路由
 bool (*raw_eroute)(const ip_address *this_host,
      const ip_subnet *this_client,
      const ip_address *that_host,
      const ip_subnet *that_client,
      ipsec_spi_t spi,
      unsigned int proto,
      unsigned int transport_proto,
      unsigned int satype,
      const struct pfkey_proto_info *proto_info,
      time_t use_lifetime,
      unsigned int op,
      const char *text_said);
// 添加SA
 bool (*add_sa)(const struct kernel_sa *sa, bool replace);
// SA归组
 bool (*grp_sa)(const struct kernel_sa *sa_outer,
         const struct kernel_sa *sa_inner);
// 删除SA
 bool (*del_sa)(const struct kernel_sa *sa);
// 获取SPI
 ipsec_spi_t (*get_spi)(const ip_address *src,
          const ip_address *dst,
          int proto,
          bool tunnel_mode,
          unsigned reqid,
          ipsec_spi_t min,
          ipsec_spi_t max,
          const char *text_said);
// 执行命令操作
 bool (*docommand)(struct connection *c
        , struct spd_route *sr
        , const char *verb
        , struct state *st);
};

7.2 KLIPS内核操作结构

pluto和KLIPS的通信是通过PF_KEY类型的套接口来实现的, 关于内核中PF_KEY的实现可参考前面的文章(虽然KLIPS有自带的PF_KEY实现而不是linux内核自己的PF_KEY, 但基本是类似的), 用户层PF_KEY的编程处理也可见UNPv1e3。
 
7.2.1 结构定义

/* programs/pluto/kernel_pfkey.c */

const struct kernel_ops klips_kernel_ops = {
 type: KERNEL_TYPE_KLIPS,
 async_fdp: &pfkeyfd,
// 回放窗口为64
 replay_window: 64,
 pfkey_register: klips_pfkey_register,
 pfkey_register_response: klips_pfkey_register_response,
 process_queue: pfkey_dequeue,
 process_msg: pfkey_event,
 raw_eroute: pfkey_raw_eroute,
 add_sa: pfkey_add_sa,
 grp_sa: pfkey_grp_sa,
 del_sa: pfkey_del_sa,
 get_spi: NULL,
        inbound_eroute: FALSE,
 policy_lifetime: FALSE,
// 无初始化函数
 init: NULL,
 docommand: do_command_linux,
 opname: "pfkey"
};

7.2.2 登记协议

// 登记IPSEC相关处理协议
static void
klips_pfkey_register(void)
{
// 登记AH,ESP,IPCOMP,IPIP这4种协议
    pfkey_register_proto(SADB_SATYPE_AH, "AH");
    pfkey_register_proto(SADB_SATYPE_ESP, "ESP");
    can_do_IPcomp = FALSE;  /* until we get a response from KLIPS */
    pfkey_register_proto(SADB_X_SATYPE_COMP, "IPCOMP");
    pfkey_register_proto(SADB_X_SATYPE_IPIP, "IPIP");
}

7.2.3 登记回应

该函数处理内核返回的SADB_REGISTER消息, 可能是REGISTER操作后的回应, 也可能是内核主动发出的. 向内核发出REGISTER信息告诉内核pluto支持哪些协议和算法, 内核会返回结果告诉pluto内核又支持哪些协议和算法, 相当于一个协议支持类型的协商过程,一般来说内核的IPSEC实现是肯定要实现AH、ESP和IPIP的,IPCOMP可能会不支持;对于算法,认证算法要求支持MD5和SHA1,加密算法要支持DES,3DES。
/* Process a SADB_REGISTER message from the kernel.
 * This will be a response to one of ours, but it may be asynchronous
 * (if kernel modules are loaded and unloaded).
 * Some sanity checking has already been performed.
 */
static void
klips_pfkey_register_response(const struct sadb_msg *msg)
{
    /* Find out what the kernel can support.
     * In fact, the only question at the moment
     * is whether it can support IPcomp.
     * So we ignore the rest.
     * ??? we really should pay attention to what transforms are supported.
     */
// 检查返回的消息类型
    switch (msg->sadb_msg_satype)
    {
    case SADB_SATYPE_AH:
// AH, 正常, 忽略
 break;
    case SADB_SATYPE_ESP:
#ifdef KERNEL_ALG
// ESP, 登记内核能支持的加密认证算法
 kernel_alg_register_pfkey(msg, sizeof (pfkey_buf));
#endif
 break;
    case SADB_X_SATYPE_COMP:
 /* ??? There ought to be an extension to list the
  * supported algorithms, but RFC 2367 doesn't
  * list one for IPcomp.  KLIPS uses SADB_X_CALG_DEFLATE.
  * Since we only implement deflate, we'll assume this.
  */
// 支持IP压缩协议
 can_do_IPcomp = TRUE;
 break;
    case SADB_X_SATYPE_IPIP:
// IPIP,支持,忽略
 break;
    default:
 break;
    }
}

7.2.4 队列处理

处理PF_KEY数据队列中的数据包
/* asynchronous messages from our queue */
static void
pfkey_dequeue(void)
{
// 遍历当前的数据链表
    while (pfkey_iq_head != NULL)
    {
// 获取链表头元素
 pfkey_item *it = pfkey_iq_head;
// 进行相关异步处理
 pfkey_async(&it->buf);
// 从链表中断开, 出队释放
 pfkey_iq_head = it->next;
 pfree(it);
    }
    /* Handle any orphaned holds, but only if no pfkey input is pending.
     * For each, we initiate Opportunistic.
     * note: we don't need to advance the pointer because
     * record_and_initiate_opportunistic will remove the current
     * record each time we call it.
     */
// 如果有不匹配所有连接的PF_KEY包, 就要启动OE过程了
    while (orphaned_holds != NULL && !pfkey_input_ready())
      record_and_initiate_opportunistic(&orphaned_holds->ours
     , &orphaned_holds->his
     , orphaned_holds->transport_proto
     , "%hold found-pfkey");
}
 
/* Handle PF_KEY messages from the kernel that are not dealt with
 * synchronously.  In other words, all but responses to PF_KEY messages
 * that we sent.
 */
// 处理异步PF_KEY数据包
static void
pfkey_async(pfkey_buf *buf)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];
// 解析数据包, 失败的话返回
    if (pfkey_msg_parse(&buf->msg, NULL, extensions, EXT_BITS_OUT))
    {
 plog("pfkey_async:"
     " unparseable PF_KEY message:"
     " %s len=%d, errno=%d, seq=%d, pid=%d; message ignored"
     , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type)
     , buf->msg.sadb_msg_len
     , buf->msg.sadb_msg_errno
     , buf->msg.sadb_msg_seq
     , buf->msg.sadb_msg_pid);
    }
    else
    {
// 解析成功
 DBG(DBG_CONTROL | DBG_KLIPS, DBG_log("pfkey_async:"
     " %s len=%u, errno=%u, satype=%u, seq=%u, pid=%u"
     , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type)
     , buf->msg.sadb_msg_len
     , buf->msg.sadb_msg_errno
     , buf->msg.sadb_msg_satype
     , buf->msg.sadb_msg_seq
     , buf->msg.sadb_msg_pid));
// 根据数据包类型进行相关处理
 switch (buf->msg.sadb_msg_type)
 {
 case SADB_REGISTER:
// 登记, 调用相关的登记回应处理函数
     kernel_ops->pfkey_register_response(&buf->msg);
     break;
 case SADB_ACQUIRE:
// 请求, 内核需要pluto协商一个新的SA出来
     /* to simulate loss of ACQUIRE, delete this call */
     process_pfkey_acquire(buf, extensions);
     break;
#ifdef NAT_TRAVERSAL
 case SADB_X_NAT_T_NEW_MAPPING:
// NAT穿越的映射信息
     process_pfkey_nat_t_new_mapping(&(buf->msg), extensions);
     break;
#endif
 default:
     /* ignored */
     break;
 }
    }
}
 
/* Processs a SADB_ACQUIRE message from KLIPS.
 * Try to build an opportunistic connection!
 * See RFC 2367 "PF_KEY Key Management API, Version 2" 3.1.6
 * <base, address(SD), (address(P)), (identity(SD),) (sensitivity,) proposal>
 * - extensions for source and data IP addresses
 * - optional extensions for identity [not useful for us?]
 * - optional extension for sensitivity [not useful for us?]
 * - expension for proposal [not useful for us?]
 *
 * ??? We must use the sequence number in creating an SA.
 * We actually need to create up to 4 SAs each way.  Which one?
 * I guess it depends on the protocol present in the sadb_msg_satype.
 * For now, we'll ignore this requirement.
 *
 * ??? We need some mechanism to make sure that multiple ACQUIRE messages
 * don't cause a whole bunch of redundant negotiations.
 */
// 应内核请求建立新SA
static void
process_pfkey_acquire(pfkey_buf *buf, struct sadb_ext *extensions[SADB_EXT_MAX + 1])
{
// 源地址结构
    struct sadb_address *srcx = (void *) extensions[SADB_EXT_ADDRESS_SRC];
// 目的地址结构
    struct sadb_address *dstx = (void *) extensions[SADB_EXT_ADDRESS_DST];
// 源端协议
    int src_proto = srcx->sadb_address_proto;
// 目的段协议
    int dst_proto = dstx->sadb_address_proto;
// 具体的源和目的地址
    ip_address *src = (ip_address*)&srcx[1];
    ip_address *dst = (ip_address*)&dstx[1];
    ip_subnet ours, his;
    err_t ugh = NULL;
    /* assumption: we're only catching our own outgoing packets
     * so source is our end and destination is the other end.
     * Verifying this is not actually convenient.
     *
     * This stylized control structure yields a complaint or
     * desired results.  For compactness, a pointer value is
     * treated as a boolean.  Logically, the structure is:
     * keep going as long as things are OK.
     */
// 条件检查
// 必须是内核请求
    if (buf->msg.sadb_msg_pid == 0 /* we only wish to hear from kernel */
// 源目的协议相同
 && !(ugh = src_proto == dst_proto? NULL : "src and dst protocols differ")
// 地址类型相同, 都是V4或V6
 && !(ugh = addrtypeof(src) == addrtypeof(dst)? NULL : "conflicting address types")
// 获取源地址子网
 && !(ugh = addrtosubnet(src, &ours))
// 获取目的地址子网
 && !(ugh = addrtosubnet(dst, &his)))
// 启动OE过程协商新SA, 因为这时两端可能是没有任何预设置共享密钥的
// 该函数的分析将在后续文章中
      record_and_initiate_opportunistic(&ours, &his, 0, "%acquire-pfkey");

// 获取子网失败返回
    if (ugh != NULL)
 plog("SADB_ACQUIRE message from KLIPS malformed: %s", ugh);
}
/* programs/pluto/nat_traversal.c */
// NAT穿越映射关系处理
void process_pfkey_nat_t_new_mapping(
 struct sadb_msg *msg __attribute__ ((unused)),
 struct sadb_ext *extensions[SADB_EXT_MAX + 1])
{
// NAT映射信息
 struct _new_klips_mapp_nfo nfo;
// 源地址参数
 struct sadb_address *srcx = (void *) extensions[SADB_EXT_ADDRESS_SRC];
// 目的地址参数
 struct sadb_address *dstx = (void *) extensions[SADB_EXT_ADDRESS_DST];
 struct sockaddr *srca, *dsta;
 err_t ugh = NULL;
// SA信息结构
 nfo.sa = (void *) extensions[SADB_EXT_SA];
// 如果地址信息不全, 错误, 返回
 if ((!nfo.sa) || (!srcx) || (!dstx)) {
  openswan_log("SADB_X_NAT_T_NEW_MAPPING message from KLIPS malformed: "
   "got NULL params");
  return;
 }
// 具体的源和目的地址
 srca = ((struct sockaddr *)(void *)&srcx[1]);
 dsta = ((struct sockaddr *)(void *)&dstx[1]);
// 只支持IPV4, 在V6下是没必要使用NAT的,因为V6地址肯定是足够用的
 if ((srca->sa_family != AF_INET) || (dsta->sa_family != AF_INET)) {
  ugh = "only AF_INET supported";
 }
 else {
  char text_said[SATOT_BUF];
  char _srca[ADDRTOT_BUF], _dsta[ADDRTOT_BUF];
  ip_said said;
// 将源和目的地址端口信息填充到SA结构中
  initaddr((const void *) &((const struct sockaddr_in *)srca)->sin_addr,
   sizeof(((const struct sockaddr_in *)srca)->sin_addr),
   srca->sa_family, &(nfo.src));
  nfo.sport = ntohs(((const struct sockaddr_in *)srca)->sin_port);
  initaddr((const void *) &((const struct sockaddr_in *)dsta)->sin_addr,
   sizeof(((const struct sockaddr_in *)dsta)->sin_addr),
   dsta->sa_family, &(nfo.dst));
  nfo.dport = ntohs(((const struct sockaddr_in *)dsta)->sin_port);
  DBG(DBG_NATT,
   initsaid(&nfo.src, nfo.sa->sadb_sa_spi, SA_ESP, &said);
   satot(&said, 0, text_said, SATOT_BUF);
   addrtot(&nfo.src, 0, _srca, ADDRTOT_BUF);
   addrtot(&nfo.dst, 0, _dsta, ADDRTOT_BUF);
   DBG_log("new klips mapping %s %s:%d %s:%d",
    text_said, _srca, nfo.sport, _dsta, nfo.dport);
  );
// 对所有状态进行NAT映射调整
// 有关NAT穿越详细处理在后续文档中分析
  for_each_state((void *)nat_t_new_klips_mapp, &nfo);
 }
 if (ugh != NULL)
  openswan_log("SADB_X_NAT_T_NEW_MAPPING message from KLIPS malformed: %s", ugh);
}
 
7.2.5 消息处理

实际上本质也是调用pfkey_async()函数.
/* asynchronous messages directly from PF_KEY socket */
static void
pfkey_event(void)
{
    pfkey_buf buf;
    if (pfkey_get(&buf))
 pfkey_async(&buf);
}
 
7.2.6 原始路由

// 因为KLIPS的eroute就对应安全策略, 所以则实际是实现安全策略的操作
// 但RFC2367中没有定义标准的安全策略的处理命令, 所以各种实现都是定
// 义自己的, 不同实现可能不同
// KLIPS将策略描述为流(FLOW), 即从某IP到某IP的数据进行加密处理
static bool
pfkey_raw_eroute(const ip_address *this_host
   , const ip_subnet *this_client
   , const ip_address *that_host
   , const ip_subnet *that_client
   , ipsec_spi_t spi
   , unsigned int proto UNUSED
   , unsigned int transport_proto
   , unsigned int satype
   , const struct pfkey_proto_info *proto_info UNUSED
   , time_t use_lifetime UNUSED
   , unsigned int op
   , const char *text_said)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];
    ip_address
 sflow_ska,
 dflow_ska,
 smask_ska,
 dmask_ska;
// 端口
    int sport = ntohs(portof(&this_client->addr));
    int dport = ntohs(portof(&that_client->addr));
// 本地子网信息
    networkof(this_client, &sflow_ska);
    maskof(this_client, &smask_ska);
    setportof(sport ? ~0:0, &smask_ska);
// 对方子网信息
    networkof(that_client, &dflow_ska);
    maskof(that_client, &dmask_ska);
    setportof(dport ? ~0:0, &dmask_ska);
// 构造SADB数据包头信息, 操作类型由op参数的低8位确定
    if (!pfkey_msg_start(op & ERO_MASK, satype
         , "pfkey_msg_hdr flow", text_said, extensions))
    {
 return FALSE;
    }
// 非删除操作
    if (op != ERO_DELETE)
    {
// 构造SPI
 if (!(pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA]
      , SADB_EXT_SA
      , spi /* in network order */
      , 0, 0, 0, 0, op >> ERO_FLAG_SHIFT)
     , "pfkey_sa add flow", text_said, extensions)
// 构造本地主机地址
     && pfkeyext_address(SADB_EXT_ADDRESS_SRC, this_host
  , "pfkey_addr_s add flow", text_said, extensions)
// 构造对方主机地址
     && pfkeyext_address(SADB_EXT_ADDRESS_DST, that_host
    , "pfkey_addr_d add flow", text_said
    , extensions)))
 {
     return FALSE;
 }
    }
// 构造本地子网参数
    if (!pfkeyext_address(SADB_X_EXT_ADDRESS_SRC_FLOW, &sflow_ska
     , "pfkey_addr_sflow", text_said, extensions))
    {
 return FALSE;
    }
// 构造对方子网参数
    if (!pfkeyext_address(SADB_X_EXT_ADDRESS_DST_FLOW, &dflow_ska
   , "pfkey_addr_dflow", text_said, extensions))
    {
 return FALSE;
    }
// 构造本地子网掩码参数
    if (!pfkeyext_address(SADB_X_EXT_ADDRESS_SRC_MASK, &smask_ska
   , "pfkey_addr_smask", text_said, extensions))
    {
 return FALSE;
    }
// 构造对方子网掩码参数
    if (!pfkeyext_address(SADB_X_EXT_ADDRESS_DST_MASK, &dmask_ska
   , "pfkey_addr_dmask", text_said, extensions))
    {
 return FALSE;
    }
// 构造协议参数
    if (!pfkeyext_protocol(transport_proto
   , "pfkey_x_protocol", text_said, extensions))
    {
 return FALSE;
    }
// 添加SADB结束信息, 发送内核, 处理回应
    return finish_pfkey_msg(extensions, "flow", text_said, NULL);
}

7.2.7 增加SA

// 可进行增加和替换SA操作
// 各种SA操作就是构造SADB数据包, 发送给内核, 然后接收内核回应, SADB数据包构造比较麻烦,
// 但都是标准例程, 可见UNPv1e3
static bool
pfkey_add_sa(const struct kernel_sa *sa, bool replace)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];
    bool success = FALSE;
// 构造消息头, 根据replace参数确定是增加还是替换操作
    success = pfkey_msg_start(replace ? SADB_UPDATE : SADB_ADD, sa->satype
         , "pfkey_msg_hdr Add SA"
         , sa->text_said, extensions);
// 构造失败返回
    if(!success) return FALSE;
// 构造SADB结构参数:SPI, 回放窗口, 认证算法, 加密算法等
    success = pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA]
      , SADB_EXT_SA
      , sa->spi /* in network order */
      , sa->replay_window, SADB_SASTATE_MATURE
      , sa->authalg, sa->encalg, 0)
     , "pfkey_sa Add SA", sa->text_said, extensions);
    if(!success) return FALSE;
// 构造SADB结构参数: 源地址
    success = pfkeyext_address(SADB_EXT_ADDRESS_SRC, sa->src
          , "pfkey_addr_s Add SA"
          , sa->text_said, extensions);
    if(!success) return FALSE;
// 构造SADB结构参数: 目的地址
    success = pfkeyext_address(SADB_EXT_ADDRESS_DST, sa->dst
          , "pfkey_addr_d Add SA", sa->text_said
          , extensions);
    if(!success) return FALSE;
    if(sa->authkeylen != 0) {
// 构造SADB结构参数: 认证密钥
 success = pfkey_build(pfkey_key_build(&extensions[SADB_EXT_KEY_AUTH]
           , SADB_EXT_KEY_AUTH
           , sa->authkeylen * BITS_PER_BYTE
           , sa->authkey)
         , "pfkey_key_a Add SA"
         , sa->text_said, extensions);
 if(!success) return FALSE;
    }
 
    if(sa->enckeylen != 0) {
// 构造SADB结构参数: 加密密钥
 success = pfkey_build(pfkey_key_build(&extensions[SADB_EXT_KEY_ENCRYPT]
           , SADB_EXT_KEY_ENCRYPT
           , sa->enckeylen * BITS_PER_BYTE
           , sa->enckey)
         , "pfkey_key_e Add SA"
         , sa->text_said, extensions);
 if(!success) return FALSE;
    }
 
#ifdef NAT_TRAVERSAL
    if(sa->natt_type != 0) {
// 构造SADB结构参数: NAT穿越类型
 success = pfkey_build(pfkey_x_nat_t_type_build(
      &extensions[SADB_X_EXT_NAT_T_TYPE]
      , sa->natt_type),
         "pfkey_nat_t_type Add ESP SA"
         ,  sa->text_said, extensions);
 DBG(DBG_KLIPS
     , DBG_log("setting natt_type to %d\n", sa->natt_type));
 if(!success) return FALSE;
 if(sa->natt_sport != 0) {
// 构造SADB结构参数: NAT穿越源端口
   success = pfkey_build(pfkey_x_nat_t_port_build(
      &extensions[SADB_X_EXT_NAT_T_SPORT]
      , SADB_X_EXT_NAT_T_SPORT,
      sa->natt_sport)
         , "pfkey_nat_t_sport Add ESP SA"
         , sa->text_said, extensions);
   DBG(DBG_KLIPS
       , DBG_log("setting natt_sport to %d\n", sa->natt_sport));
   if(!success) return FALSE;
 }
 
 if(sa->natt_dport != 0) {
// 构造SADB结构参数: NAT穿越目的端口
   success = pfkey_build(pfkey_x_nat_t_port_build(
      &extensions[SADB_X_EXT_NAT_T_DPORT]
      , SADB_X_EXT_NAT_T_DPORT
      , sa->natt_dport)
         , "pfkey_nat_t_dport Add ESP SA"
         , sa->text_said, extensions);
   DBG(DBG_KLIPS
       , DBG_log("setting natt_dport to %d\n", sa->natt_dport));
   if(!success) return FALSE;
 }
 
 if(sa->natt_type!=0 && !isanyaddr(sa->natt_oa)) {
// 构造SADB结构参数: NAT穿越OA参数
   success = pfkeyext_address(SADB_X_EXT_NAT_T_OA, sa->natt_oa
         , "pfkey_nat_t_oa Add ESP SA"
         , sa->text_said, extensions);
   DBG(DBG_KLIPS
       , DBG_log("setting nat_oa to %s\n", ip_str(sa->natt_oa)));
   if(!success) return FALSE;
 }
    }
#endif
// 添加SADB结束信息, 发送内核, 处理回应
    return finish_pfkey_msg(extensions, "Add SA", sa->text_said, NULL);
}
 
7.2.8 SA归组

static bool
pfkey_grp_sa(const struct kernel_sa *sa0, const struct kernel_sa *sa1)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];
// 和前面类似, 依次构造SADB数据头(GRPSA命令类型), SPI, 目的地址, SATYPE2,
// EXT_SA2, 目的地址2和结束符, 然后发送内核, 这是直接将所有过程串一起写了
    return pfkey_msg_start(SADB_X_GRPSA, sa1->satype
 , "pfkey_msg_hdr group", sa1->text_said, extensions)
    && pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA]
     , SADB_EXT_SA
     , sa1->spi /* in network order */
     , 0, 0, 0, 0, 0)
 , "pfkey_sa group", sa1->text_said, extensions)
    && pfkeyext_address(SADB_EXT_ADDRESS_DST, sa1->dst
 , "pfkey_addr_d group", sa1->text_said, extensions)
    && pfkey_build(pfkey_x_satype_build(&extensions[SADB_X_EXT_SATYPE2]
     , sa0->satype)
 , "pfkey_satype group", sa0->text_said, extensions)
    && pfkey_build(pfkey_sa_build(&extensions[SADB_X_EXT_SA2]
     , SADB_X_EXT_SA2
     , sa0->spi /* in network order */
     , 0, 0, 0, 0, 0)
 , "pfkey_sa2 group", sa0->text_said, extensions)
    && pfkeyext_address(SADB_X_EXT_ADDRESS_DST2, sa0->dst
 , "pfkey_addr_d2 group", sa0->text_said, extensions)
    && finish_pfkey_msg(extensions, "group", sa1->text_said, NULL);
}

7.2.9 删除SA

static bool
pfkey_del_sa(const struct kernel_sa *sa)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];
// 和前面类似, 依次构造SADB数据头(DELETE命令类型), SPI, 源地址, 目的地址,
// 和结束符, 然后发送内核, 这是直接将所有过程串一起写了
    return pfkey_msg_start(SADB_DELETE, proto2satype(sa->proto)
 , "pfkey_msg_hdr delete SA", sa->text_said, extensions)
    && pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA]
     , SADB_EXT_SA
     , sa->spi /* in host order */
     , 0, SADB_SASTATE_MATURE, 0, 0, 0)
 , "pfkey_sa delete SA", sa->text_said, extensions)
    && pfkeyext_address(SADB_EXT_ADDRESS_SRC, sa->src
 , "pfkey_addr_s delete SA", sa->text_said, extensions)
    && pfkeyext_address(SADB_EXT_ADDRESS_DST, sa->dst
 , "pfkey_addr_d delete SA", sa->text_said, extensions)
    && finish_pfkey_msg(extensions, "Delete SA", sa->text_said, NULL);
}

7.2.10 执行命令

/* programs/pluto/sysdep_linux.c */
// 这是系统相关的, 就只对linux有效, 这是执行一个shell命令脚本来启动或停止连接
bool
do_command_linux(struct connection *c, struct spd_route *sr
   , const char *verb, struct state *st)
{
// shell命令空间
    char cmd[1536];     /* arbitrary limit on shell command length */
    const char *verb_suffix;
    /* figure out which verb suffix applies */
    {
        const char *hs, *cs;
// 主机类型
        switch (addrtypeof(&sr->this.host_addr))
        {
            case AF_INET:
                hs = "-host";
                cs = "-client";
                break;
            case AF_INET6:
                hs = "-host-v6";
                cs = "-client-v6";
                break;
            default:
                loglog(RC_LOG_SERIOUS, "unknown address family");
                return FALSE;
        }
// 地址参数
        verb_suffix = subnetisaddr(&sr->this.client, &sr->this.host_addr)
            ? hs : cs;
    }
    /* form the command string */
    {
// 各种命令参数空间
        char
            nexthop_str[sizeof("PLUTO_NEXT_HOP='' ")+ADDRTOT_BUF],
            me_str[ADDRTOT_BUF],
            myid_str[IDTOA_BUF],
            srcip_str[ADDRTOT_BUF+sizeof("PLUTO_MY_SOURCEIP=")+4],
            myclient_str[SUBNETTOT_BUF],
            myclientnet_str[ADDRTOT_BUF],
            myclientmask_str[ADDRTOT_BUF],
            peer_str[ADDRTOT_BUF],
            peerid_str[IDTOA_BUF],
            peerclient_str[SUBNETTOT_BUF],
            peerclientnet_str[ADDRTOT_BUF],
            peerclientmask_str[ADDRTOT_BUF],
            secure_myid_str[IDTOA_BUF] = "",
            secure_peerid_str[IDTOA_BUF] = "",
            secure_peerca_str[IDTOA_BUF] = "",
            secure_xauth_username_str[IDTOA_BUF] = "";
    
        ip_address ta;
 nexthop_str[0]='\0';
 if(addrbytesptr(&sr->this.host_nexthop, NULL)
    && !isanyaddr(&sr->this.host_nexthop))
 {
     char *n;
     strcpy(nexthop_str, "PLUTO_NEXT_HOP='");
     n = nexthop_str + strlen(nexthop_str);
     addrtot(&sr->this.host_nexthop, 0,
      n, sizeof(nexthop_str)-strlen(nexthop_str));
     strncat(nexthop_str, "' ", sizeof(nexthop_str));
 }
        addrtot(&sr->this.host_addr, 0, me_str, sizeof(me_str));
        idtoa(&sr->this.id, myid_str, sizeof(myid_str));
        escape_metachar(myid_str, secure_myid_str, sizeof(secure_myid_str));
        subnettot(&sr->this.client, 0, myclient_str, sizeof(myclientnet_str));
        networkof(&sr->this.client, &ta);
        addrtot(&ta, 0, myclientnet_str, sizeof(myclientnet_str));
        maskof(&sr->this.client, &ta);
        addrtot(&ta, 0, myclientmask_str, sizeof(myclientmask_str));
        addrtot(&sr->that.host_addr, 0, peer_str, sizeof(peer_str));
        idtoa(&sr->that.id, peerid_str, sizeof(peerid_str));
        escape_metachar(peerid_str, secure_peerid_str, sizeof(secure_peerid_str));
        subnettot(&sr->that.client, 0, peerclient_str, sizeof(peerclientnet_str));
        networkof(&sr->that.client, &ta);
        addrtot(&ta, 0, peerclientnet_str, sizeof(peerclientnet_str));
        maskof(&sr->that.client, &ta);
        addrtot(&ta, 0, peerclientmask_str, sizeof(peerclientmask_str));
 
 secure_xauth_username_str[0]='\0';
 if (st != NULL && st->st_xauth_username) {
  size_t len;
   strcpy(secure_xauth_username_str, "PLUTO_XAUTH_USERNAME='");
  len = strlen(secure_xauth_username_str);
  remove_metachar(st->st_xauth_username
    ,secure_xauth_username_str+len
    ,sizeof(secure_xauth_username_str)-(len+2));
  strncat(secure_xauth_username_str, "'", sizeof(secure_xauth_username_str)-1);
 }
        srcip_str[0]='\0';
        if(addrbytesptr(&sr->this.host_srcip, NULL) != 0
           && !isanyaddr(&sr->this.host_srcip))
        {
            char *p;
            int   l;
            strncat(srcip_str, "PLUTO_MY_SOURCEIP=", sizeof(srcip_str));
            strncat(srcip_str, "'", sizeof(srcip_str));
            l = strlen(srcip_str);
            p = srcip_str + l;
           
            addrtot(&sr->this.host_srcip, 0, p, sizeof(srcip_str));
            strncat(srcip_str, "'", sizeof(srcip_str));
        }
        {
            struct pubkey_list *p;
            char peerca_str[IDTOA_BUF];
            for (p = pubkeys; p != NULL; p = p->next)
                {
                    struct pubkey *key = p->key;
                    int pathlen;
                   
                    if (key->alg == PUBKEY_ALG_RSA && same_id(&sr->that.id, &key->id)
                        && trusted_ca(key->issuer, sr->that.ca, &pathlen))
                        {
                            dntoa_or_null(peerca_str, IDTOA_BUF, key->issuer, "");
                            escape_metachar(peerca_str, secure_peerca_str, sizeof(secure_peerca_str));
                            break;
                        }
                }
        }
// 构造shell命令, 具体执行脚本是_updown
        if (-1 == snprintf(cmd, sizeof(cmd)
      , "2>&1 "   /* capture stderr along with stdout */
      "PLUTO_VERSION='1.1' "    /* change VERSION when interface spec changes */
      "PLUTO_VERB='%s%s' "
      "PLUTO_CONNECTION='%s' "
      "%s"      /* possible PLUTO_NEXT_HOP */
      "PLUTO_INTERFACE='%s' "
      "PLUTO_ME='%s' "
      "PLUTO_MY_ID='%s' "
      "PLUTO_MY_CLIENT='%s' "
      "PLUTO_MY_CLIENT_NET='%s' "
      "PLUTO_MY_CLIENT_MASK='%s' "
      "PLUTO_MY_PORT='%u' "
      "PLUTO_MY_PROTOCOL='%u' "
      "PLUTO_PEER='%s' "
      "PLUTO_PEER_ID='%s' "
      "PLUTO_PEER_CLIENT='%s' "
      "PLUTO_PEER_CLIENT_NET='%s' "
      "PLUTO_PEER_CLIENT_MASK='%s' "
      "PLUTO_PEER_PORT='%u' "
      "PLUTO_PEER_PROTOCOL='%u' "
      "PLUTO_PEER_CA='%s' "
      "PLUTO_CONN_POLICY='%s' "
      "%s "
      "%s "       /* PLUTO_MY_SRCIP */                   
      "%s"        /* actual script */
      , verb, verb_suffix
      , c->name
      , nexthop_str
      , c->interface->ip_dev->id_vname
      , me_str
      , secure_myid_str
      , myclient_str
      , myclientnet_str
      , myclientmask_str
      , sr->this.port
      , sr->this.protocol
      , peer_str
      , secure_peerid_str
      , peerclient_str
      , peerclientnet_str
      , peerclientmask_str
      , sr->that.port
      , sr->that.protocol
      , secure_peerca_str
      , prettypolicy(c->policy)
      , secure_xauth_username_str
      , srcip_str
      , sr->this.updown == NULL? DEFAULT_UPDOWN : sr->this.updown))
        {
            loglog(RC_LOG_SERIOUS, "%s%s command too long!", verb, verb_suffix);
            return FALSE;
        }
    }
    DBG(DBG_CONTROL, DBG_log("executing %s%s: %s"
        , verb, verb_suffix, cmd));
    {
        /* invoke the script, catching stderr and stdout
         * It may be of concern that some file descriptors will
         * be inherited.  For the ones under our control, we
         * have done fcntl(fd, F_SETFD, FD_CLOEXEC) to prevent this.
         * Any used by library routines (perhaps the resolver or syslog)
         * will remain.
         */
 __sighandler_t savesig;
        FILE *f;
 savesig = signal(SIGCHLD, SIG_DFL);
// 读取shell命令执行结果
        f = popen(cmd, "r");
        if (f == NULL)
        {
            loglog(RC_LOG_SERIOUS, "unable to popen %s%s command", verb, verb_suffix);
     signal(SIGCHLD, savesig);
            return FALSE;
        }
        /* log any output */
        for (;;)
        {
            /* if response doesn't fit in this buffer, it will be folded */
            char resp[256];
// 将执行结果记录到日志
            if (fgets(resp, sizeof(resp), f) == NULL)
            {
                if (ferror(f))
                {
                    log_errno((e, "fgets failed on output of %s%s command"
                        , verb, verb_suffix));
      signal(SIGCHLD, savesig);
                    return FALSE;
                }
                else
                {
                    passert(feof(f));
                    break;
                }
            }
            else
            {
                char *e = resp + strlen(resp);
                if (e > resp && e[-1] == '\n')
                    e[-1] = '\0';       /* trim trailing '\n' */
                openswan_log("%s%s output: %s", verb, verb_suffix, resp);
            }
        }
        /* report on and react to return code */
        {
            int r = pclose(f);
     signal(SIGCHLD, savesig);
// 善后处理
            if (r == -1)
            {
                log_errno((e, "pclose failed for %s%s command"
                    , verb, verb_suffix));
                return FALSE;
            }
            else if (WIFEXITED(r))
            {
                if (WEXITSTATUS(r) != 0)
                {
                    loglog(RC_LOG_SERIOUS, "%s%s command exited with status %d"
                        , verb, verb_suffix, WEXITSTATUS(r));
                    return FALSE;
                }
            }
            else if (WIFSIGNALED(r))
            {
                loglog(RC_LOG_SERIOUS, "%s%s command exited with signal %d"
                    , verb, verb_suffix, WTERMSIG(r));
                return FALSE;
            }
            else
            {
                loglog(RC_LOG_SERIOUS, "%s%s command exited with unknown status %d"
                    , verb, verb_suffix, r);
                return FALSE;
            }
        }
    }
    return TRUE;
}
 
 
...... 待续 ......