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

7.3 linux内核操作结构

该操作结构是针对linux内核的IPSEC实现是NETKEY(XFRM)而不是KLIPS的, 因为XFRM支持使用pf_key和netlink两种进行内核与用户层间的通信, pluto就使用了netlink, 把pf_key专门留给了KLIPS

7.3.1 结构定义

/* programs/pluto/kernel_netlink.c */

const struct kernel_ops linux_kernel_ops = {
 type: KERNEL_TYPE_LINUX,
 inbound_eroute: 1,
 policy_lifetime: 1,
 async_fdp: &netlink_bcast_fd,
 replay_window: 32,
 init: init_netlink,
 pfkey_register: linux_pfkey_register,
 pfkey_register_response: linux_pfkey_register_response,
 process_msg: netlink_process_msg,
 raw_eroute: netlink_raw_eroute,
 add_sa: netlink_add_sa,
 del_sa: netlink_del_sa,
// 无队列处理函数
 process_queue: NULL,
 grp_sa: NULL,
 get_spi: netlink_get_spi,
 docommand: do_command_linux,
 opname: "netkey",
};

7.3.2 初始化
 
/** init_netlink - Initialize the netlink inferface.  Opens the sockets and
 * then binds to the broadcast socket.
 */
static void init_netlink(void)
{
    struct sockaddr_nl addr;
// 打开netlink套接口, NETLINK_XFRM类型, 内核实现是XFRM
// 用于向内核发送信息
    netlinkfd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM);
    if (netlinkfd < 0)
 exit_log_errno((e, "socket() in init_netlink()"));
// 设置CLOEXEC属性
    if (fcntl(netlinkfd, F_SETFD, FD_CLOEXEC) != 0)
 exit_log_errno((e, "fcntl(FD_CLOEXEC) in init_netlink()"));
// 打开广播netlink套接口, 用于接收内核主动发出的netlink广播信息
    netlink_bcast_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM);
    if (netlink_bcast_fd < 0)
 exit_log_errno((e, "socket() for bcast in init_netlink()"));
// 设置CLOEXEC和非阻塞属性
    if (fcntl(netlink_bcast_fd, F_SETFD, FD_CLOEXEC) != 0)
 exit_log_errno((e, "fcntl(FD_CLOEXEC) for bcast in init_netlink()"));
    if (fcntl(netlink_bcast_fd, F_SETFL, O_NONBLOCK) != 0)
 exit_log_errno((e, "fcntl(O_NONBLOCK) for bcast in init_netlink()"));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE;
// 绑定进程
    if (bind(netlink_bcast_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
 exit_log_errno((e, "Failed to bind bcast socket in init_netlink() - Perhaps kernel has no CONFIG_XFRM_USER support"));
}

7.3.3 登记协议
 
/** linux_pfkey_register - Register via PFKEY our capabilities
 *
 */
static void
linux_pfkey_register(void)
{
// 只登记AH,ESP,IPCOMP协议, 没登记IPIP, 有点怪
// 协议登记还是要通过PF_LEY来进行, 但登记完后就不用PF_KEY了
    pfkey_register_proto(SADB_SATYPE_AH, "AH");
    pfkey_register_proto(SADB_SATYPE_ESP, "ESP");
    pfkey_register_proto(SADB_X_SATYPE_IPCOMP, "IPCOMP");
// 关闭PF_KEY
    pfkey_close();
}

7.3.4 登记回应

// 对PF_KEY登记的内核返回消息的处理
static void
linux_pfkey_register_response(const struct sadb_msg *msg)
{
    switch (msg->sadb_msg_satype)
    {
    case SADB_SATYPE_ESP:
#ifdef KERNEL_ALG
// ESP, 登记各种算法
     kernel_alg_register_pfkey(msg, msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
#endif
     break;
    case SADB_X_SATYPE_IPCOMP:
// IP压缩有效
 can_do_IPcomp = TRUE;
 break;
    default:
 break;
    }
}

7.3.5 消息处理

static void
netlink_process_msg(void)
{
// 如果能不能读取netlink消息就一直循环
// netlink_get函数返回TRUE表示读取失败,而读取成功返回FALSE的
    while (netlink_get())
 ;
}

static bool
netlink_get(void)
{
// 收到的netlink消息结构
    struct {
 struct nlmsghdr n;
 char data[1024];
    } rsp;
    ssize_t r;
    struct sockaddr_nl addr;
    socklen_t alen;
    alen = sizeof(addr);
// 从netlink广播套接口接收消息
    r = recvfrom(netlink_bcast_fd, &rsp, sizeof(rsp), 0
 , (struct sockaddr *)&addr, &alen);
    if (r < 0)
    {
// 读取失败
// 如果是EAGAIN返回FALSE跳出循环重新操作, 因为该套接口是非阻塞的,在没数据可读时
// 就返回EAGAIN错误而不阻塞
 if (errno == EAGAIN)
     return FALSE;
// 其他情况返回TRUE继续循环
 if (errno != EINTR)
     log_errno((e, "recvfrom() failed in netlink_get"));
 return TRUE;
    }
    else if ((size_t) r < sizeof(rsp.n))
    {
// 读到的数据长度小于netlink数据头基本长度不对, 也返回TRUE继续循环
 openswan_log("netlink_get read truncated message: %ld bytes; ignore message"
     , (long) r);
 return TRUE;
    }
    else if (addr.nl_pid != 0)
    {
// pid非0, 不是内核的消息, 也返回TRUE继续循环
 /* not for us: ignore */
 DBG(DBG_KLIPS,
     DBG_log("netlink_get: ignoring %s message from process %u"
  , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type)
  , addr.nl_pid));
 return TRUE;
    }
    else if ((size_t) r != rsp.n.nlmsg_len)
    {
// 读到的数据长度不对, 也返回TRUE继续循环
 openswan_log("netlink_get read message with length %ld that doesn't equal nlmsg_len %lu bytes; ignore message"
     , (long) r
     , (unsigned long) rsp.n.nlmsg_len);
 return TRUE;
    }
    DBG(DBG_KLIPS,
 DBG_log("netlink_get: %s message"
  , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type)));
// 根据消息类型处理
    switch (rsp.n.nlmsg_type)
    {
    case XFRM_MSG_ACQUIRE:
// 处理新建SA请求
 netlink_acquire(&rsp.n);
 break;
    case XFRM_MSG_POLEXPIRE:
// 策略过期消息
 netlink_policy_expire(&rsp.n);
 break;
    default:
 /* ignored */
 break;
    }
// 处理后继续循环读套接口
    return TRUE;
}
 
7.3.6 原始路由
 
用于编辑安全策略, XFRM不使用虚拟网卡方法, 因此其实也不是eroute了, 这只是为了接口的统一.
/** netlink_raw_eroute
 *
 * @param this_host ip_address
 * @param this_client ip_subnet
 * @param that_host ip_address
 * @param that_client ip_subnet
 * @param spi
 * @param proto int (Currently unused) Contains protocol (u=tcp, 17=udp, etc...)
 * @param transport_proto int (Currently unused) 0=tunnel, 1=transport
 * @param satype int
 * @param proto_info
 * @param lifetime (Currently unused)
 * @param ip int
 * @return boolean True if successful
 */
static bool
netlink_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 UNUSED
     , unsigned int satype
     , const struct pfkey_proto_info *proto_info
     , time_t use_lifetime UNUSED
     , unsigned int op
     , const char *text_said)
{
// 请求结构
    struct {
 struct nlmsghdr n;
 union {
     struct xfrm_userpolicy_info p;
     struct xfrm_userpolicy_id id;
 } u;
 char data[1024];
    } req;
    int shift;
    int dir;
    int family;
    int policy;
    bool ok;
    bool enoent_ok;
// 缺省策略: IPSEC处理
    policy = IPSEC_POLICY_IPSEC;
// 如果SA类型是内部定义SA类型
    if (satype == SADB_X_SATYPE_INT)
    {
 /* shunt route */
// 根据SPI来确定策略
 switch (ntohl(spi))
 {
// 旁路
 case SPI_PASS:
     policy = IPSEC_POLICY_NONE;
     break;
// 丢弃
 case SPI_DROP:
 case SPI_REJECT:
 default:
     policy = IPSEC_POLICY_DISCARD;
     break;
// 保持,陷阱
 case SPI_TRAP:
 case SPI_TRAPSUBNET:
 case SPI_HOLD:
     if (op & (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT))
     {
  return TRUE;
     }
     break;
 }
    }

// 填充请求结构
    memset(&req, 0, sizeof(req));
    req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
// 协议族
    family = that_client->addr.u.v4.sin_family;
    shift = (family == AF_INET) ? 5 : 7;
// 端口及端口掩码
    req.u.p.sel.sport = portof(&this_client->addr);
    req.u.p.sel.dport = portof(&that_client->addr);
    req.u.p.sel.sport_mask = (req.u.p.sel.sport) ? ~0:0;
    req.u.p.sel.dport_mask = (req.u.p.sel.dport) ? ~0:0;
// 子网地址及掩码
    ip2xfrm(&this_client->addr, &req.u.p.sel.saddr);
    ip2xfrm(&that_client->addr, &req.u.p.sel.daddr);
    req.u.p.sel.prefixlen_s = this_client->maskbits;
    req.u.p.sel.prefixlen_d = that_client->maskbits;
// 协议
    req.u.p.sel.proto = transport_proto;
    req.u.p.sel.family = family;
// 定义方向
    dir = XFRM_POLICY_OUT;
    if (op & (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT))
    {
 dir = XFRM_POLICY_IN;
    }

// 操作类型
    if ((op & ERO_MASK) == ERO_DELETE)
    {
// 删除策略
 req.u.id.dir = dir;
 req.n.nlmsg_type = XFRM_MSG_DELPOLICY;
 req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.u.id)));
    }
    else
    {
// 添加或修改策略
     int src, dst;
// 方向
 req.u.p.dir = dir;
// 源,目的
     src = req.u.p.sel.prefixlen_s;
     dst = req.u.p.sel.prefixlen_d;
 if (dir != XFRM_POLICY_OUT) {
     src = req.u.p.sel.prefixlen_d;
     dst = req.u.p.sel.prefixlen_s;
 }
 req.u.p.priority = MIN_SPD_PRIORITY
     + (((2 << shift) - src) << shift)
     + (2 << shift) - dst;
// 缺省XFRM动作: 允许
 req.u.p.action = XFRM_POLICY_ALLOW;
 if (policy == IPSEC_POLICY_DISCARD)
 {
// 如果是DISCARD则为阻塞
     req.u.p.action = XFRM_POLICY_BLOCK;
 }
// 软限制: 时间, 字节数, 包数
// XFRM_INF为不限制
 req.u.p.lft.soft_use_expires_seconds = use_lifetime;
 req.u.p.lft.soft_byte_limit = XFRM_INF;
 req.u.p.lft.soft_packet_limit = XFRM_INF;
// 硬限制: 时间, 字节数, 包数
 req.u.p.lft.hard_byte_limit = XFRM_INF;
 req.u.p.lft.hard_packet_limit = XFRM_INF;
// 缺省是新建策略
 req.n.nlmsg_type = XFRM_MSG_NEWPOLICY;
 if (op & (SADB_X_SAFLAGS_REPLACEFLOW << ERO_FLAG_SHIFT))
 {
// 根据标志也可能是替换策略
     req.n.nlmsg_type = XFRM_MSG_UPDPOLICY;
 }
 req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.u.p)));
    }

// 如果策略是IPSEC处理而且非删除操作
    if (policy == IPSEC_POLICY_IPSEC && (op & ERO_MASK) != ERO_DELETE)
    {
 struct rtattr *attr;
// 模板, 最多4种协议: AH,ESP,IPCOMP,IPIP
 struct xfrm_user_tmpl tmpl[4];
 int i;
 memset(tmpl, 0, sizeof(tmpl));
// 填充每种协议的模板
 for (i = 0; proto_info[i].proto; i++)
 {
     tmpl[i].reqid = proto_info[i].reqid;
     tmpl[i].id.proto = proto_info[i].proto;
     tmpl[i].optional =
  proto_info[i].proto == IPPROTO_COMP && dir != XFRM_POLICY_OUT;
     tmpl[i].aalgos = tmpl[i].ealgos = tmpl[i].calgos = ~0;
     tmpl[i].mode = 0;
     if (proto_info[i].encapsulation != ENCAPSULATION_MODE_TUNNEL
     || isanyaddr(that_host))
  continue;
     tmpl[i].mode = 1;
     ip2xfrm(this_host, &tmpl[i].saddr);
     ip2xfrm(that_host, &tmpl[i].id.daddr);
 }
// 将模板信息拷贝到netlink结构数据区
 attr = (struct rtattr *)((char *)&req + req.n.nlmsg_len);
 attr->rta_type = XFRMA_TMPL;
 attr->rta_len = i * sizeof(tmpl[0]);
 memcpy(RTA_DATA(attr), tmpl, attr->rta_len);
 attr->rta_len = RTA_LENGTH(attr->rta_len);
 req.n.nlmsg_len += attr->rta_len;
    }
    enoent_ok = FALSE;
    if (op == ERO_DEL_INBOUND)
    {
 enoent_ok = TRUE;
    }
    else if (op == ERO_DELETE && ntohl(spi) == SPI_HOLD
    && proto_info[0].encapsulation == ENCAPSULATION_MODE_TRANSPORT)
    {
 enoent_ok = TRUE;
    }
// 进行netlink策略操作
    ok = netlink_policy(&req.n, enoent_ok, text_said);
// 根据数据方向进行不同处理
    switch (dir)
    {
// 向内的数据
    case XFRM_POLICY_IN:
// 如果是删除操作, 将请求d的id方向改为转发
 if (req.n.nlmsg_type == XFRM_MSG_DELPOLICY)
 {
     req.u.id.dir = XFRM_POLICY_FWD;
 }
 else if (!ok)
 {
// 否则如果刚才netlink_policy命令成功就返回了
     break;
 }
 else if (proto_info[0].encapsulation != ENCAPSULATION_MODE_TUNNEL
 && satype != SADB_X_SATYPE_INT)
 {
// 非通道, 类型不是内部定义, 也可以返回了
     break;
 }
 else
 {
     req.u.p.dir = XFRM_POLICY_FWD;
 }
// 重新进行netlink_policy操作
 ok &= netlink_policy(&req.n, enoent_ok, text_said);
 break;
    }
    return ok;
}

// netlink策略处理
/** netlink_policy -
 *
 * @param hdr - Data to check
 * @param enoent_ok - Boolean - OK or not OK.
 * @param text_said - String
 * @return boolean
 */
static bool
netlink_policy(struct nlmsghdr *hdr, bool enoent_ok, const char *text_said)
{
// 回应结构
    struct {
 struct nlmsghdr n;
 struct nlmsgerr e;
    } rsp;
    int error;
    rsp.n.nlmsg_type = NLMSG_ERROR;
// 发送netlink消息
    if (!send_netlink_msg(hdr, &rsp.n, sizeof(rsp), "policy", text_said))
    {
// 发送失败, 返回FALSE
 return FALSE;
    }

// 检查回应信息的error参数
    error = -rsp.e.error;
    if (!error)
    {
// 无错误, 成功返回
 return TRUE;
    }
    if (error == ENOENT && enoent_ok)
    {
// 是NOENT(无相关环境)而且允许无环境(enoent_ok), 返回成功
 return TRUE;
    }
// 否则记录失败类型, 返回错误
    loglog(RC_LOG_SERIOUS
 , "ERROR: netlink %s response for flow %s included errno %d: %s"
 , sparse_val_show(xfrm_type_names, hdr->nlmsg_type)
 , text_said
 , error
 , strerror(error));
    return FALSE;
}

// 发送netlink数据包
/** send_netlink_msg
 *
 * @param hdr - Data to be sent.
 * @param rbuf - Return Buffer - contains data returned from the send.
 * @param rbuf_len - Length of rbuf
 * @param description - String - user friendly description of what is
 *                      being attempted.  Used for diagnostics
 * @param text_said - String
 * @return bool True if the message was succesfully sent.
 */
static bool
send_netlink_msg(struct nlmsghdr *hdr, struct nlmsghdr *rbuf, size_t rbuf_len
, const char *description, const char *text_said)
{
// 返回结果结构
    struct {
 struct nlmsghdr n;
 struct nlmsgerr e;
 char data[1024];
    } rsp;
    size_t len;
    ssize_t r;
    struct sockaddr_nl addr;
    static uint32_t seq;

// 如果内核没有IPSEC, 直接返回
    if (kern_interface == NO_KERNEL)
    {
 return TRUE;
    }
// netlink消息序号
    hdr->nlmsg_seq = ++seq;
    len = hdr->nlmsg_len;
// 通过netlink套接口向内核发送消息
    do {
 r = write(netlinkfd, hdr, len);
    } while (r < 0 && errno == EINTR);
    if (r < 0)
    {
// 发送失败, 返回错误
 log_errno((e
     , "netlink write() of %s message"
       " for %s %s failed"
     , sparse_val_show(xfrm_type_names, hdr->nlmsg_type)
     , description, text_said));
 return FALSE;
    }
    else if ((size_t)r != len)
    {
// 实际发送长度不等于数据长度, 返回失败
 loglog(RC_LOG_SERIOUS
     , "ERROR: netlink write() of %s message"
       " for %s %s truncated: %ld instead of %lu"
     , sparse_val_show(xfrm_type_names, hdr->nlmsg_type)
     , description, text_said
     , (long)r, (unsigned long)len);
 return FALSE;
    }
// 接收回应信息处理
    for (;;) {
 socklen_t alen;
 alen = sizeof(addr);
// 接收回应信息, 是可阻塞的
 r = recvfrom(netlinkfd, &rsp, sizeof(rsp), 0
     , (struct sockaddr *)&addr, &alen);
 if (r < 0)
 {
// 接收失败, 如果不是EINTR导致的中断, 返回失败
     if (errno == EINTR)
     {
  continue;
     }
     log_errno((e
  , "netlink recvfrom() of response to our %s message"
    " for %s %s failed"
  , sparse_val_show(xfrm_type_names, hdr->nlmsg_type)
  , description, text_said));
     return FALSE;
 }
 else if ((size_t) r < sizeof(rsp.n))
 {
// 接收长度小于netlink数据头长度, 忽略该消息, 继续
     openswan_log("netlink read truncated message: %ld bytes; ignore message"
  , (long) r);
     continue;
 }
 else if (addr.nl_pid != 0)
 {
// 非内核消息, 忽略该消息,继续
     /* not for us: ignore */
     DBG(DBG_KLIPS,
  DBG_log("netlink: ignoring %s message from process %u"
      , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type)
      , addr.nl_pid));
     continue;
 }
 else if (rsp.n.nlmsg_seq != seq)
 {
// 序号不匹配, 忽略, 继续
     DBG(DBG_KLIPS,
  DBG_log("netlink: ignoring out of sequence (%u/%u) message %s"
      , rsp.n.nlmsg_seq, seq
      , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type)));
     continue;
 }
 break;
    }
    if (rsp.n.nlmsg_len > (size_t) r)
    {
// 接收的消息不完整, 返回失败
 loglog(RC_LOG_SERIOUS
     , "netlink recvfrom() of response to our %s message"
       " for %s %s was truncated: %ld instead of %lu"
     , sparse_val_show(xfrm_type_names, hdr->nlmsg_type)
     , description, text_said
     , (long) len, (unsigned long) rsp.n.nlmsg_len);
 return FALSE;
    }
    else if (rsp.n.nlmsg_type != NLMSG_ERROR
    && (rbuf && rsp.n.nlmsg_type != rbuf->nlmsg_type))
    {
// 接收的消息类型不匹配, 返回错误
 loglog(RC_LOG_SERIOUS
     , "netlink recvfrom() of response to our %s message"
       " for %s %s was of wrong type (%s)"
     , sparse_val_show(xfrm_type_names, hdr->nlmsg_type)
     , description, text_said
     , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type));
 return FALSE;
    }
    else if (rbuf)
    {
// 如果提供了保存回应空间
 if ((size_t) r > rbuf_len)
 {
// 回应空间长度太小, 返回失败
     loglog(RC_LOG_SERIOUS
  , "netlink recvfrom() of response to our %s message"
    " for %s %s was too long: %ld > %lu"
  , sparse_val_show(xfrm_type_names, hdr->nlmsg_type)
  , description, text_said
  , (long)r, (unsigned long)rbuf_len);
     return FALSE;
 }
// 将回应消息复制到回应空间返回
 memcpy(rbuf, &rsp, r);
 return TRUE;
    }
    else if (rsp.n.nlmsg_type == NLMSG_ERROR && rsp.e.error)
    {
// 如果是错误信息, 返回失败
// 有点问题: 这个else if应该和上面那个替换才说得通嘛
 loglog(RC_LOG_SERIOUS
     , "ERROR: netlink response for %s %s included errno %d: %s"
     , description, text_said
     , -rsp.e.error
     , strerror(-rsp.e.error));
 return FALSE;
    }
// 否则返回成功, 回应消息内容不返回
    return TRUE;
}

7.3.7 增加SA

/** netlink_add_sa - Add an SA into the kernel SPDB via netlink
 *
 * @param sa Kernel SA to add/modify
 * @param replace boolean - true if this replaces an existing SA
 * @return bool True if successfull
 */
static bool
netlink_add_sa(const struct kernel_sa *sa, bool replace)
{
// netlink请求结构
    struct {
 struct nlmsghdr n;
 struct xfrm_usersa_info p;
 char data[1024];
    } req;
    struct rtattr *attr;
// 填充netlink请求信息
    memset(&req, 0, sizeof(req));
    req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
// 操作类型: 修改还是新建
    req.n.nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA;
// 源和目的地址
    ip2xfrm(sa->src, &req.p.saddr);
    ip2xfrm(sa->dst, &req.p.id.daddr);
// SPI, 协议, 协议族, 是否通道模式, 回放窗口大小, 软硬限制
    req.p.id.spi = sa->spi;
    req.p.id.proto = satype2proto(sa->satype);
    req.p.family = sa->src->u.v4.sin_family;
    req.p.mode = (sa->encapsulation == ENCAPSULATION_MODE_TUNNEL);
    req.p.replay_window = sa->replay_window > 32 ? 32 : sa->replay_window;
    req.p.reqid = sa->reqid;
    req.p.lft.soft_byte_limit = XFRM_INF;
    req.p.lft.soft_packet_limit = XFRM_INF;
    req.p.lft.hard_byte_limit = XFRM_INF;
    req.p.lft.hard_packet_limit = XFRM_INF;
    req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.p)));
// 相关属性
    attr = (struct rtattr *)((char *)&req + req.n.nlmsg_len);
    if (sa->authkeylen)
    {
 struct xfrm_algo algo;
 const char *name;
// 认证算法
 name = sparse_name(aalg_list, sa->authalg);
 if (!name) {
     loglog(RC_LOG_SERIOUS, "unknown authentication algorithm: %u"
  , sa->authalg);
     return FALSE;
 }
// 名称和密钥长度
 strcpy(algo.alg_name, name);
 algo.alg_key_len = sa->authkeylen * BITS_PER_BYTE;
 attr->rta_type = XFRMA_ALG_AUTH;
 attr->rta_len = RTA_LENGTH(sizeof(algo) + sa->authkeylen);
 memcpy(RTA_DATA(attr), &algo, sizeof(algo));
 memcpy((char *)RTA_DATA(attr) + sizeof(algo), sa->authkey
     , sa->authkeylen);
 req.n.nlmsg_len += attr->rta_len;
 attr = (struct rtattr *)((char *)attr + attr->rta_len);
    }
    /* Consider ESP_NULL particular case (enckeylen==0) */
    if (sa->enckeylen || sa->encalg==SADB_EALG_NULL)
    {
 struct xfrm_algo algo;
 const char *name;
// 加密算法
 name = sparse_name(ealg_list, sa->encalg);
 if (!name) {
     loglog(RC_LOG_SERIOUS, "unknown encryption algorithm: %u"
  , sa->encalg);
     return FALSE;
 }
// 名称和密钥长度
 strcpy(algo.alg_name, name);
 algo.alg_key_len = sa->enckeylen * BITS_PER_BYTE;
 attr->rta_type = XFRMA_ALG_CRYPT;
 attr->rta_len = RTA_LENGTH(sizeof(algo) + sa->enckeylen);
 memcpy(RTA_DATA(attr), &algo, sizeof(algo));
 memcpy((char *)RTA_DATA(attr) + sizeof(algo), sa->enckey
     , sa->enckeylen);
 req.n.nlmsg_len += attr->rta_len;
 attr = (struct rtattr *)((char *)attr + attr->rta_len);
    }
    if (sa->satype == SADB_X_SATYPE_COMP)
    {
 struct xfrm_algo algo;
 const char *name;
// 压缩算法
 name = sparse_name(calg_list, sa->encalg);
 if (!name) {
     loglog(RC_LOG_SERIOUS, "unknown compression algorithm: %u"
  , sa->encalg);
     return FALSE;
 }
// 算法和算法长度
 strcpy(algo.alg_name, name);
 algo.alg_key_len = 0;
 attr->rta_type = XFRMA_ALG_COMP;
 attr->rta_len = RTA_LENGTH(sizeof(algo));
 memcpy(RTA_DATA(attr), &algo, sizeof(algo));
 req.n.nlmsg_len += attr->rta_len;
 attr = (struct rtattr *)((char *)attr + attr->rta_len);
    }
#ifdef NAT_TRAVERSAL
    if (sa->natt_type)
    {
 struct xfrm_encap_tmpl natt;
// NAT穿越相关属性
// 类型
 natt.encap_type = sa->natt_type;
// 端口
 natt.encap_sport = ntohs(sa->natt_sport);
 natt.encap_dport = ntohs(sa->natt_dport);
 memset (&natt.encap_oa, 0, sizeof (natt.encap_oa));
 attr->rta_type = XFRMA_ENCAP;
 attr->rta_len = RTA_LENGTH(sizeof(natt));
 memcpy(RTA_DATA(attr), &natt, sizeof(natt));
 req.n.nlmsg_len += attr->rta_len;
 attr = (struct rtattr *)((char *)attr + attr->rta_len);
    }
#endif
// 发送netlink消息到内核
    return send_netlink_msg(&req.n, NULL, 0, "Add SA", sa->text_said);
}
 
7.3.8 删除SA

/** netlink_del_sa - Delete an SA from the Kernel
 *
 * @param sa Kernel SA to be deleted
 * @return bool True if successfull
 */
static bool
netlink_del_sa(const struct kernel_sa *sa)
{
// 发送的消息结构
    struct {
 struct nlmsghdr n;
 struct xfrm_usersa_id id;
 char data[1024];
    } req;
    memset(&req, 0, sizeof(req));
    req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
// 类型: 删除SA, 所需要的参数比较简单
    req.n.nlmsg_type = XFRM_MSG_DELSA;
// 目的地址
    ip2xfrm(sa->dst, &req.id.daddr);
// SPI
    req.id.spi = sa->spi;
// 协议和协议族
    req.id.family = sa->src->u.v4.sin_family;
    req.id.proto = sa->proto;
    req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.id)));
// 发送netlink消息到内核
    return send_netlink_msg(&req.n, NULL, 0, "Del SA", sa->text_said);
}

7.3.9 获取SPI

static ipsec_spi_t
netlink_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)
{
// 请求结构
    struct {
 struct nlmsghdr n;
 struct xfrm_userspi_info spi;
    } req;
// 回应结构
    struct {
 struct nlmsghdr n;
 union {
     struct nlmsgerr e;
     struct xfrm_usersa_info sa;
 } u;
 char data[1024];
    } rsp;
    static int get_cpi_bug;
// 填充请求结构
    memset(&req, 0, sizeof(req));
// 标志是请求操作
    req.n.nlmsg_flags = NLM_F_REQUEST;
// 类型是分配SPI
    req.n.nlmsg_type = XFRM_MSG_ALLOCSPI;
// 源和目的地址
    ip2xfrm(src, &req.spi.info.saddr);
    ip2xfrm(dst, &req.spi.info.id.daddr);
// 模式
    req.spi.info.mode = tunnel_mode;
// 请求ID
    req.spi.info.reqid = reqid;
// 协议和协议族
    req.spi.info.id.proto = proto;
    req.spi.info.family = src->u.v4.sin_family;
    req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.spi)));
// 回应结构类型是NEWSA
    rsp.n.nlmsg_type = XFRM_MSG_NEWSA;
retry:
// 设置SPI的范围: 最大和最小值
    req.spi.min = get_cpi_bug ? htonl(min) : min;
    req.spi.max = get_cpi_bug ? htonl(max) : max;
// 发送netlink消息
    if (!send_netlink_msg(&req.n, &rsp.n, sizeof(rsp), "Get SPI", text_said))
 return 0;
    else if (rsp.n.nlmsg_type == NLMSG_ERROR)
    {
// 发送错误
 if (rsp.u.e.error == -EINVAL && proto == IPPROTO_COMP && !get_cpi_bug)
 {
// 如果是参数非法, 可能是字序不对, 调整最大最小SPI的字序重新发送请求
     get_cpi_bug = 1;
     openswan_log("netlink_get_spi: Enabling workaround for"
    " kernel CPI allocation bug");
     goto retry;
 }
// 否则返回0表示没找到SPI
 loglog(RC_LOG_SERIOUS
     , "ERROR: netlink_get_spi for %s/%u/%u failed with errno %d: %s"
     , text_said
     , req.spi.min, req.spi.max
     , -rsp.u.e.error
     , strerror(-rsp.u.e.error));
 return 0;
    }
    else if (rsp.n.nlmsg_len < NLMSG_LENGTH(sizeof(rsp.u.sa)))
    {
// 结果长度不对也返回0
 openswan_log("netlink_get_spi: XFRM_MSG_ALLOCSPI returned message with length %lu < %lu bytes; ignore message"
     , (unsigned long) rsp.n.nlmsg_len
     , (unsigned long) sizeof(rsp.u.sa));
 return 0;
    }
// 命令成功, 返回获取的SPI值
    DBG(DBG_KLIPS,
 DBG_log("netlink_get_spi: allocated 0x%x for %s"
     , ntohl(rsp.u.sa.id.spi), text_said));
    return rsp.u.sa.id.spi;
}

7.3.10 执行命令

同7.2.10

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