本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn:
[email protected]
来源: 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;
}
...... 待续 ......