赞
踩
目录
strongswan ipsec 向内核下发SA和Policy部分可以是kernel_netlink插件的方式实现的。
其他的插件有:
Plugin Name | Description |
---|---|
kernel-libipsec | IPsec "kernel" interface in user-space using libipsec |
kernel-netlink | IPsec/Networking kernel interface using Linux Netlink |
kernel-pfkey | IPsec kernel interface using PF_KEY |
kernel-wfp | IPsec backend for the Windows platform, using the Windows Filtering Platform |
libstrongswan/plugins/plugin_loader.c里面会将指定路径下的库文件当做strongswan的插件来加载,所以strongswan会把kernel_netlink编译成库文件,libcharon/Makefile文件中关键内容如下:
Makefile
- am__append_108 = plugins/kernel_netlink
- #am__append_109 = plugins/kernel_netlink/libstrongswan-kernel-netlink.la
-
- /* file: libcharon/Makefile */
Makeifle.am
- if USE_KERNEL_NETLINK
- SUBDIRS += plugins/kernel_netlink
- if MONOLITHIC
- libcharon_la_LIBADD += plugins/kernel_netlink/libstrongswan-kernel-netlink.la
- endif
- endif
根据上述Makefile编译生成的libstrongswan-kernel-netlink.so文件如下:
- strongswan$ grep kernel_netlink_plugin_create . -r
- Binary file ./src/libcharon/plugins/kernel_netlink/.libs/libstrongswan-kernel-netlink.so matches
- Binary file ./src/libcharon/plugins/kernel_netlink/.libs/kernel_netlink_plugin.o matches
- ./src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c:plugin_t *kernel_netlink_plugin_create()
libstrongswan/plugins/plugin_loader.c文件中 _load_plugins函数会遍历路径下的库文件来加载kenel-netlink插件。
- METHOD(plugin_loader_t, load_plugins, bool, private_plugin_loader_t *this, char *list);
-
- plugin_loader_t *plugin_loader_create()
- {
- private_plugin_loader_t *this;
-
- INIT(this,
- .public = {
- .add_static_features = _add_static_features,
- .load = _load_plugins,
- .add_path = _add_path,
- .reload = _reload,
- .unload = _unload,
- .create_plugin_enumerator = _create_plugin_enumerator,
- .has_feature = _has_feature,
- .loaded_plugins = _loaded_plugins,
- .status = _status,
- .destroy = _destroy,
- },
- .plugins = linked_list_create(),
- .loaded = linked_list_create(),
- .features = hashlist_create(
- (hashtable_hash_t)registered_feature_hash,
- (hashtable_equals_t)registered_feature_equals, 64),
- .get_features = dlsym(RTLD_DEFAULT, "plugin_loader_feature_filter"),
- );
-
- if (!this->get_features)
- {
- this->get_features = get_features_default;
- }
-
- return &this->public;
- }
-
- /* file: libstrongswan/plugins/plugin_loader.c */
插件注册函数为kernel_ipsec_register();
- # grep kernel_ipsec_register . -r
- ./plugins/kernel_wfp/kernel_wfp_plugin.c: PLUGIN_CALLBACK(kernel_ipsec_register, kernel_wfp_ipsec_create),
- ./plugins/load_tester/load_tester_plugin.c: PLUGIN_CALLBACK(kernel_ipsec_register, load_tester_ipsec_create),
- ./plugins/kernel_pfkey/kernel_pfkey_plugin.c: PLUGIN_CALLBACK(kernel_ipsec_register, kernel_pfkey_ipsec_create),
- ./plugins/kernel_netlink/kernel_netlink_plugin.c: PLUGIN_CALLBACK(kernel_ipsec_register, kernel_netlink_ipsec_create),
- ./plugins/kernel_libipsec/kernel_libipsec_plugin.c: PLUGIN_CALLBACK(kernel_ipsec_register, kernel_libipsec_ipsec_create),
- ./kernel/kernel_ipsec.c:bool kernel_ipsec_register(plugin_t *plugin, plugin_feature_t *feature,
- ./kernel/kernel_ipsec.h:bool kernel_ipsec_register(plugin_t *plugin, plugin_feature_t *feature,
kernel_netlink插件中关键函数为kernel_netlink_plugin_create();
- METHOD(plugin_t, get_features, int,
- private_kernel_netlink_plugin_t *this, plugin_feature_t *features[])
- {
- static plugin_feature_t f[] = {
- PLUGIN_CALLBACK(kernel_ipsec_register, kernel_netlink_ipsec_create),
- PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
- PLUGIN_CALLBACK(kernel_net_register, kernel_netlink_net_create),
- PLUGIN_PROVIDE(CUSTOM, "kernel-net"),
- };
- *features = f;
- return countof(f);
- }
-
- plugin_t *kernel_netlink_plugin_create()
- {
- private_kernel_netlink_plugin_t *this;
-
- ......
-
- INIT(this,
- .public = {
- .plugin = {
- .get_name = _get_name,
- .get_features = _get_features,
- .reload = _reload,
- .destroy = _destroy,
- },
- },
- );
-
- reload(this);
-
- return &this->public.plugin;
- }
-
- /* file: libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c */
kernel_netlink_ipsec_create会对SA和Policy的管理接口初始化并初始化xfrm的用户态socket。
- kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
- {
- private_kernel_netlink_ipsec_t *this;
- bool register_for_events = TRUE;
-
- INIT(this,
- .public = {
- .interface = {
- .get_features = _get_features,
- .get_spi = _get_spi,
- .get_cpi = _get_cpi,
- .add_sa = _add_sa,
- .update_sa = _update_sa,
- .query_sa = _query_sa,
- .del_sa = _del_sa,
- .flush_sas = _flush_sas,
- .add_policy = _add_policy,
- .query_policy = _query_policy,
- .del_policy = _del_policy,
- .flush_policies = _flush_policies,
- .bypass_socket = _bypass_socket,
- .enable_udp_decap = _enable_udp_decap,
- .destroy = _destroy,
- },
- },
- .policies = hashtable_create((hashtable_hash_t)policy_hash,
- (hashtable_equals_t)policy_equals, 32),
- .sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash,
- (hashtable_equals_t)ipsec_sa_equals, 32),
- .bypass = array_create(sizeof(bypass_t), 0),
- .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
- .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
- .get_priority = dlsym(RTLD_DEFAULT,
- "kernel_netlink_get_priority_custom"),
- .policy_update = lib->settings->get_bool(lib->settings,
- "%s.plugins.kernel-netlink.policy_update", FALSE, lib->ns),
- .install_routes = lib->settings->get_bool(lib->settings,
- "%s.install_routes", TRUE, lib->ns),
- .proto_port_transport = lib->settings->get_bool(lib->settings,
- "%s.plugins.kernel-netlink.set_proto_port_transport_sa",
- FALSE, lib->ns),
- );
-
- if (streq(lib->ns, "starter"))
- { /* starter has no threads, so we do not register for kernel events */
- register_for_events = FALSE;
- }
-
- this->socket_xfrm = netlink_socket_create(NETLINK_XFRM, xfrm_msg_names,
- lib->settings->get_bool(lib->settings,
- "%s.plugins.kernel-netlink.parallel_xfrm", FALSE, lib->ns));
- if (!this->socket_xfrm)
- {
- destroy(this);
- return NULL;
- }
-
- setup_spd_hash_thresh(this, "ipv4", XFRMA_SPD_IPV4_HTHRESH, 32);
- setup_spd_hash_thresh(this, "ipv6", XFRMA_SPD_IPV6_HTHRESH, 128);
-
- if (register_for_events)
- {
- struct sockaddr_nl addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.nl_family = AF_NETLINK;
-
- /* create and bind XFRM socket for ACQUIRE, EXPIRE, MIGRATE & MAPPING */
- this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM);
- if (this->socket_xfrm_events <= 0)
- {
- DBG1(DBG_KNL, "unable to create XFRM event socket: %s (%d)",
- strerror(errno), errno);
- destroy(this);
- return NULL;
- }
- addr.nl_groups = XFRMNLGRP(ACQUIRE) | XFRMNLGRP(EXPIRE) |
- XFRMNLGRP(MIGRATE) | XFRMNLGRP(MAPPING);
- if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr)))
- {
- DBG1(DBG_KNL, "unable to bind XFRM event socket: %s (%d)",
- strerror(errno), errno);
- destroy(this);
- return NULL;
- }
- lib->watcher->add(lib->watcher, this->socket_xfrm_events, WATCHER_READ,
- (watcher_cb_t)receive_events, this);
- }
-
- netlink_find_offload_feature(lib->settings->get_str(lib->settings,
- "%s.plugins.kernel-netlink.hw_offload_feature_interface",
- "lo", lib->ns));
-
- return &this->public;
- }
-
- /* file: libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c */
_add_sa();的实现函数为,根据SA数据,组装成NetLink消息体下发给内核
- METHOD(kernel_ipsec_t, add_sa, status_t,
- private_kernel_netlink_ipsec_t *this, kernel_ipsec_sa_id_t *id,
- kernel_ipsec_add_sa_t *data)
- {
- netlink_buf_t request;
- const char *alg_name;
- char markstr[32] = "";
- struct nlmsghdr *hdr;
- struct xfrm_usersa_info *sa;
- uint16_t icv_size = 64, ipcomp = data->ipcomp;
- ipsec_mode_t mode = data->mode, original_mode = data->mode;
- traffic_selector_t *first_src_ts, *first_dst_ts;
- status_t status = FAILED;
-
- .......
-
- memset(&request, 0, sizeof(request));
- format_mark(markstr, sizeof(markstr), id->mark);
-
- ......
-
- hdr = &request.hdr;
- hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
- hdr->nlmsg_type = data->update ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA;
- hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info));
-
- sa = NLMSG_DATA(hdr);
- host2xfrm(id->src, &sa->saddr);
- host2xfrm(id->dst, &sa->id.daddr);
- sa->id.spi = id->spi;
- sa->id.proto = id->proto;
- sa->family = id->src->get_family(id->src);
- sa->mode = mode2kernel(mode);
-
- if (!data->copy_df)
- {
- sa->flags |= XFRM_STATE_NOPMTUDISC;
- }
-
- if (!data->copy_ecn)
- {
- sa->flags |= XFRM_STATE_NOECN;
- }
-
- if (data->inbound)
- {
- switch (data->copy_dscp)
- {
- case DSCP_COPY_YES:
- case DSCP_COPY_IN_ONLY:
- sa->flags |= XFRM_STATE_DECAP_DSCP;
- break;
- default:
- break;
- }
- }
- else
- {
- switch (data->copy_dscp)
- {
- case DSCP_COPY_IN_ONLY:
- case DSCP_COPY_NO:
- {
- /* currently the only extra flag */
- if (!add_uint32(hdr, sizeof(request), XFRMA_SA_EXTRA_FLAGS,
- XFRM_SA_XFLAG_DONT_ENCAP_DSCP))
- {
- goto failed;
- }
- break;
- }
- default:
- break;
- }
- }
-
- switch (mode)
- {
- case MODE_TUNNEL:
- sa->flags |= XFRM_STATE_AF_UNSPEC;
- break;
- case MODE_BEET:
- case MODE_TRANSPORT:
- if (original_mode == MODE_TUNNEL)
- { /* don't install selectors for switched SAs. because only one
- * selector can be installed other traffic would get dropped */
- break;
- }
- if (data->src_ts->get_first(data->src_ts,
- (void**)&first_src_ts) == SUCCESS &&
- data->dst_ts->get_first(data->dst_ts,
- (void**)&first_dst_ts) == SUCCESS)
- {
- sa->sel = ts2selector(first_src_ts, first_dst_ts,
- data->interface);
- if (!this->proto_port_transport)
- {
- /* don't install proto/port on SA. This would break
- * potential secondary SAs for the same address using a
- * different prot/port. */
- sa->sel.proto = 0;
- sa->sel.dport = sa->sel.dport_mask = 0;
- sa->sel.sport = sa->sel.sport_mask = 0;
- }
- }
- break;
- default:
- break;
- }
- if (id->proto == IPPROTO_AH && sa->family == AF_INET)
- { /* use alignment to 4 bytes for IPv4 instead of the incorrect 8 byte
- * alignment that's used by default but is only valid for IPv6 */
- sa->flags |= XFRM_STATE_ALIGN4;
- }
-
- sa->reqid = data->reqid;
- sa->lft.soft_byte_limit = XFRM_LIMIT(data->lifetime->bytes.rekey);
- sa->lft.hard_byte_limit = XFRM_LIMIT(data->lifetime->bytes.life);
- sa->lft.soft_packet_limit = XFRM_LIMIT(data->lifetime->packets.rekey);
- sa->lft.hard_packet_limit = XFRM_LIMIT(data->lifetime->packets.life);
- /* we use lifetimes since added, not since used */
- sa->lft.soft_add_expires_seconds = data->lifetime->time.rekey;
- sa->lft.hard_add_expires_seconds = data->lifetime->time.life;
- sa->lft.soft_use_expires_seconds = 0;
- sa->lft.hard_use_expires_seconds = 0;
-
- switch (data->enc_alg)
- {
- case ENCR_UNDEFINED:
- /* no encryption */
- break;
- case ENCR_AES_CCM_ICV16:
- case ENCR_AES_GCM_ICV16:
- case ENCR_NULL_AUTH_AES_GMAC:
- case ENCR_CAMELLIA_CCM_ICV16:
- case ENCR_CHACHA20_POLY1305:
- icv_size += 32;
- /* FALL */
- case ENCR_AES_CCM_ICV12:
- case ENCR_AES_GCM_ICV12:
- case ENCR_CAMELLIA_CCM_ICV12:
- icv_size += 32;
- /* FALL */
- case ENCR_AES_CCM_ICV8:
- case ENCR_AES_GCM_ICV8:
- case ENCR_CAMELLIA_CCM_ICV8:
- {
- struct xfrm_algo_aead *algo;
-
- alg_name = lookup_algorithm(ENCRYPTION_ALGORITHM, data->enc_alg);
- if (alg_name == NULL)
- {
- DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
- encryption_algorithm_names, data->enc_alg);
- goto failed;
- }
- DBG2(DBG_KNL, " using encryption algorithm %N with key size %d",
- encryption_algorithm_names, data->enc_alg,
- data->enc_key.len * 8);
-
- algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AEAD,
- sizeof(*algo) + data->enc_key.len);
- if (!algo)
- {
- goto failed;
- }
- algo->alg_key_len = data->enc_key.len * 8;
- algo->alg_icv_len = icv_size;
- strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name));
- algo->alg_name[sizeof(algo->alg_name) - 1] = '\0';
- memcpy(algo->alg_key, data->enc_key.ptr, data->enc_key.len);
- break;
- }
- default:
- {
- struct xfrm_algo *algo;
-
- alg_name = lookup_algorithm(ENCRYPTION_ALGORITHM, data->enc_alg);
- if (alg_name == NULL)
- {
- DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
- encryption_algorithm_names, data->enc_alg);
- goto failed;
- }
- DBG2(DBG_KNL, " using encryption algorithm %N with key size %d",
- encryption_algorithm_names, data->enc_alg,
- data->enc_key.len * 8);
-
- algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_CRYPT,
- sizeof(*algo) + data->enc_key.len);
- if (!algo)
- {
- goto failed;
- }
- algo->alg_key_len = data->enc_key.len * 8;
- strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name));
- algo->alg_name[sizeof(algo->alg_name) - 1] = '\0';
- memcpy(algo->alg_key, data->enc_key.ptr, data->enc_key.len);
- }
- }
- if (data->int_alg != AUTH_UNDEFINED)
- {
- u_int trunc_len = 0;
-
- alg_name = lookup_algorithm(INTEGRITY_ALGORITHM, data->int_alg);
- if (alg_name == NULL)
- {
- DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
- integrity_algorithm_names, data->int_alg);
- goto failed;
- }
- DBG2(DBG_KNL, " using integrity algorithm %N with key size %d",
- integrity_algorithm_names, data->int_alg, data->int_key.len * 8);
-
- switch (data->int_alg)
- {
- case AUTH_HMAC_MD5_128:
- case AUTH_HMAC_SHA2_256_128:
- trunc_len = 128;
- break;
- case AUTH_HMAC_SHA1_160:
- trunc_len = 160;
- break;
- case AUTH_HMAC_SHA2_256_256:
- trunc_len = 256;
- break;
- case AUTH_HMAC_SHA2_384_384:
- trunc_len = 384;
- break;
- case AUTH_HMAC_SHA2_512_512:
- trunc_len = 512;
- break;
- default:
- break;
- }
-
- ......
-
- if (data->encap)
- {
- struct xfrm_encap_tmpl *tmpl;
-
- tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl));
- if (!tmpl)
- {
- goto failed;
- }
- tmpl->encap_type = UDP_ENCAP_ESPINUDP;
- tmpl->encap_sport = htons(id->src->get_port(id->src));
- tmpl->encap_dport = htons(id->dst->get_port(id->dst));
- memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t));
- /* encap_oa could probably be derived from the
- * traffic selectors [rfc4306, p39]. In the netlink kernel
- * implementation pluto does the same as we do here but it uses
- * encap_oa in the pfkey implementation.
- * BUT as /usr/src/linux/net/key/af_key.c indicates the kernel ignores
- * it anyway
- * -> does that mean that NAT-T encap doesn't work in transport mode?
- * No. The reason the kernel ignores NAT-OA is that it recomputes
- * (or, rather, just ignores) the checksum. If packets pass the IPsec
- * checks it marks them "checksum ok" so OA isn't needed. */
- }
-
-
-
- if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id))
- {
- goto failed;
- }
-
-
- if (data->tfc && id->proto == IPPROTO_ESP && mode == MODE_TUNNEL)
- { /* the kernel supports TFC padding only for tunnel mode ESP SAs */
- if (!add_uint32(hdr, sizeof(request), XFRMA_TFCPAD, data->tfc))
- {
- goto failed;
- }
- }
-
- ......
-
- status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
- if (status == NOT_FOUND && data->update)
- {
- DBG1(DBG_KNL, "allocated SPI not found anymore, try to add SAD entry");
- hdr->nlmsg_type = XFRM_MSG_NEWSA;
- status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
- }
-
- ......
-
- status = SUCCESS;
-
- failed:
- memwipe(&request, sizeof(request));
- return status;
- }
-
- /* file: libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c */
_add_policy的实现函数为,函数中会调用add_policy_internal();最终通过NeiLink message来向内核下发policy。
- METHOD(kernel_ipsec_t, add_policy, status_t,
- private_kernel_netlink_ipsec_t *this, kernel_ipsec_policy_id_t *id,
- kernel_ipsec_manage_policy_t *data)
- {
- policy_entry_t *policy, *current;
- policy_sa_t *assigned_sa, *current_sa;
- enumerator_t *enumerator;
- bool found = FALSE, update = TRUE;
- char markstr[32] = "";
- uint32_t cur_priority = 0;
- int use_count;
-
- /* create a policy */
- INIT(policy,
- .sel = ts2selector(id->src_ts, id->dst_ts, id->interface),
- .mark = id->mark.value & id->mark.mask,
- .if_id = id->if_id,
- .direction = id->dir,
- .reqid = data->sa->reqid,
- );
- format_mark(markstr, sizeof(markstr), id->mark);
-
- /* find the policy, which matches EXACTLY */
- this->mutex->lock(this->mutex);
- current = this->policies->get(this->policies, policy);
- if (current)
- {
- if (current->reqid && data->sa->reqid &&
- current->reqid != data->sa->reqid)
- {
- DBG1(DBG_CFG, "unable to install policy %R === %R %N%s for reqid "
- "%u, the same policy for reqid %u exists",
- id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr,
- data->sa->reqid, current->reqid);
- policy_entry_destroy(this, policy);
- this->mutex->unlock(this->mutex);
- return INVALID_STATE;
- }
- /* use existing policy */
- DBG2(DBG_KNL, "policy %R === %R %N%s already exists, increasing "
- "refcount", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
- markstr);
- policy_entry_destroy(this, policy);
- policy = current;
- found = TRUE;
-
- policy->waiting++;
- while (policy->working)
- {
- this->condvar->wait(this->condvar, this->mutex);
- }
- policy->waiting--;
- policy->working = TRUE;
- }
- else
- { /* use the new one, if we have no such policy */
- policy->used_by = linked_list_create();
- this->policies->put(this->policies, policy, policy);
- }
-
- /* cache the assigned IPsec SA */
- assigned_sa = policy_sa_create(this, id->dir, data->type, data->src,
- data->dst, id->src_ts, id->dst_ts, id->mark,
- id->if_id, data->sa);
- assigned_sa->auto_priority = get_priority(policy, data->prio, id->interface);
- assigned_sa->priority = this->get_priority ? this->get_priority(id, data)
- : data->manual_prio;
- assigned_sa->priority = assigned_sa->priority ?: assigned_sa->auto_priority;
-
- /* insert the SA according to its priority */
- enumerator = policy->used_by->create_enumerator(policy->used_by);
- while (enumerator->enumerate(enumerator, (void**)¤t_sa))
- {
- if (current_sa->priority > assigned_sa->priority)
- {
- break;
- }
- if (current_sa->priority == assigned_sa->priority)
- {
- /* in case of equal manual prios order SAs by automatic priority */
- if (current_sa->auto_priority > assigned_sa->auto_priority)
- {
- break;
- }
- /* prefer SAs with a reqid over those without */
- if (current_sa->auto_priority == assigned_sa->auto_priority &&
- (!current_sa->sa->cfg.reqid || assigned_sa->sa->cfg.reqid))
- {
- break;
- }
- }
- if (update)
- {
- cur_priority = current_sa->priority;
- update = FALSE;
- }
- }
- policy->used_by->insert_before(policy->used_by, enumerator, assigned_sa);
- enumerator->destroy(enumerator);
-
- use_count = policy->used_by->get_count(policy->used_by);
- if (!update)
- { /* we don't update the policy if the priority is lower than that of
- * the currently installed one */
- policy_change_done(this, policy);
- DBG2(DBG_KNL, "not updating policy %R === %R %N%s [priority %u, "
- "refcount %d]", id->src_ts, id->dst_ts, policy_dir_names,
- id->dir, markstr, cur_priority, use_count);
- return SUCCESS;
- }
- policy->reqid = assigned_sa->sa->cfg.reqid;
-
- if (this->policy_update)
- {
- found = TRUE;
- }
-
- DBG2(DBG_KNL, "%s policy %R === %R %N%s [priority %u, refcount %d]",
- found ? "updating" : "adding", id->src_ts, id->dst_ts,
- policy_dir_names, id->dir, markstr, assigned_sa->priority, use_count);
-
- if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS)
- {
- DBG1(DBG_KNL, "unable to %s policy %R === %R %N%s",
- found ? "update" : "add", id->src_ts, id->dst_ts,
- policy_dir_names, id->dir, markstr);
- return FAILED;
- }
- return SUCCESS;
- }
-
- /* file: libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c */
参考资料:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。