2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include "stroke_config.h"
20 #include <threading/mutex.h>
21 #include <utils/lexparser.h>
23 typedef struct private_stroke_config_t private_stroke_config_t
;
26 * private data of stroke_config
28 struct private_stroke_config_t
{
33 stroke_config_t
public;
41 * mutex to lock config list
56 METHOD(backend_t
, create_peer_cfg_enumerator
, enumerator_t
*,
57 private_stroke_config_t
*this, identification_t
*me
, identification_t
*other
)
59 this->mutex
->lock(this->mutex
);
60 return enumerator_create_cleaner(this->list
->create_enumerator(this->list
),
61 (void*)this->mutex
->unlock
, this->mutex
);
65 * filter function for ike configs
67 static bool ike_filter(void *data
, peer_cfg_t
**in
, ike_cfg_t
**out
)
69 *out
= (*in
)->get_ike_cfg(*in
);
73 METHOD(backend_t
, create_ike_cfg_enumerator
, enumerator_t
*,
74 private_stroke_config_t
*this, host_t
*me
, host_t
*other
)
76 this->mutex
->lock(this->mutex
);
77 return enumerator_create_filter(this->list
->create_enumerator(this->list
),
78 (void*)ike_filter
, this->mutex
,
79 (void*)this->mutex
->unlock
);
82 METHOD(backend_t
, get_peer_cfg_by_name
, peer_cfg_t
*,
83 private_stroke_config_t
*this, char *name
)
85 enumerator_t
*e1
, *e2
;
86 peer_cfg_t
*current
, *found
= NULL
;
89 this->mutex
->lock(this->mutex
);
90 e1
= this->list
->create_enumerator(this->list
);
91 while (e1
->enumerate(e1
, ¤t
))
93 /* compare peer_cfgs name first */
94 if (streq(current
->get_name(current
), name
))
97 found
->get_ref(found
);
100 /* compare all child_cfg names otherwise */
101 e2
= current
->create_child_cfg_enumerator(current
);
102 while (e2
->enumerate(e2
, &child
))
104 if (streq(child
->get_name(child
), name
))
107 found
->get_ref(found
);
118 this->mutex
->unlock(this->mutex
);
123 * parse a proposal string, either into ike_cfg or child_cfg
125 static void add_proposals(private_stroke_config_t
*this, char *string
,
126 ike_cfg_t
*ike_cfg
, child_cfg_t
*child_cfg
)
132 proposal_t
*proposal
;
133 protocol_id_t proto
= PROTO_ESP
;
139 strict
= string
+ strlen(string
) - 1;
148 while ((single
= strsep(&string
, ",")))
150 proposal
= proposal_create_from_string(proto
, single
);
155 ike_cfg
->add_proposal(ike_cfg
, proposal
);
159 child_cfg
->add_proposal(child_cfg
, proposal
);
163 DBG1(DBG_CFG
, "skipped invalid proposal string: %s", single
);
169 /* add default porposal to the end if not strict */
173 ike_cfg
->add_proposal(ike_cfg
, proposal_create_default(PROTO_IKE
));
177 child_cfg
->add_proposal(child_cfg
, proposal_create_default(PROTO_ESP
));
182 * Build an IKE config from a stroke message
184 static ike_cfg_t
*build_ike_cfg(private_stroke_config_t
*this, stroke_msg_t
*msg
)
186 stroke_end_t tmp_end
;
191 host
= host_create_from_dns(msg
->add_conn
.other
.address
, 0, 0);
194 interface
= hydra
->kernel_interface
->get_interface(
195 hydra
->kernel_interface
, host
);
199 DBG2(DBG_CFG
, "left is other host, swapping ends");
200 tmp_end
= msg
->add_conn
.me
;
201 msg
->add_conn
.me
= msg
->add_conn
.other
;
202 msg
->add_conn
.other
= tmp_end
;
207 host
= host_create_from_dns(msg
->add_conn
.me
.address
, 0, 0);
210 interface
= hydra
->kernel_interface
->get_interface(
211 hydra
->kernel_interface
, host
);
215 DBG1(DBG_CFG
, "left nor right host is our side, "
216 "assuming left=local");
226 ike_cfg
= ike_cfg_create(msg
->add_conn
.other
.sendcert
!= CERT_NEVER_SEND
,
227 msg
->add_conn
.force_encap
,
228 msg
->add_conn
.me
.address
, msg
->add_conn
.me
.ikeport
,
229 msg
->add_conn
.other
.address
, msg
->add_conn
.other
.ikeport
);
230 add_proposals(this, msg
->add_conn
.algorithms
.ike
, ike_cfg
, NULL
);
235 * Add CRL constraint to config
237 static void build_crl_policy(auth_cfg_t
*cfg
, bool local
, int policy
)
239 /* CRL/OCSP policy, for remote config only */
245 /* if yes, we require a GOOD validation */
246 cfg
->add(cfg
, AUTH_RULE_CRL_VALIDATION
, VALIDATION_GOOD
);
248 case CRL_STRICT_IFURI
:
249 /* for ifuri, a SKIPPED validation is sufficient */
250 cfg
->add(cfg
, AUTH_RULE_CRL_VALIDATION
, VALIDATION_SKIPPED
);
259 * build authentication config
261 static auth_cfg_t
*build_auth_cfg(private_stroke_config_t
*this,
262 stroke_msg_t
*msg
, bool local
, bool primary
)
264 identification_t
*identity
;
265 certificate_t
*certificate
;
266 char *auth
, *id
, *cert
, *ca
;
267 stroke_end_t
*end
, *other_end
;
274 end
= &msg
->add_conn
.me
;
275 other_end
= &msg
->add_conn
.other
;
279 end
= &msg
->add_conn
.other
;
280 other_end
= &msg
->add_conn
.me
;
287 { /* leftid/rightid fallback to address */
292 if (ca
&& streq(ca
, "%same"))
302 { /* leftid2 falls back to leftid */
307 if (ca
&& streq(ca
, "%same"))
318 { /* "leftauth" not defined, fall back to deprecated "authby" */
319 switch (msg
->add_conn
.auth_method
)
322 case AUTH_CLASS_PUBKEY
:
334 { /* "rightauth" not defined, fall back to deprecated "eap" */
335 if (msg
->add_conn
.eap_type
)
337 if (msg
->add_conn
.eap_vendor
)
339 snprintf(eap_buf
, sizeof(eap_buf
), "eap-%d-%d",
340 msg
->add_conn
.eap_type
,
341 msg
->add_conn
.eap_vendor
);
345 snprintf(eap_buf
, sizeof(eap_buf
), "eap-%d",
346 msg
->add_conn
.eap_type
);
351 { /* not EAP => no constraints for this peer */
357 { /* no second authentication round, fine. But load certificates
358 * for other purposes (EAP-TLS) */
361 certificate
= this->cred
->load_peer(this->cred
, cert
);
364 certificate
->destroy(certificate
);
371 cfg
= auth_cfg_create();
373 /* add identity and peer certifcate */
374 identity
= identification_create_from_string(id
);
377 certificate
= this->cred
->load_peer(this->cred
, cert
);
382 this->ca
->check_for_hash_and_url(this->ca
, certificate
);
384 cfg
->add(cfg
, AUTH_RULE_SUBJECT_CERT
, certificate
);
385 if (identity
->get_type(identity
) == ID_ANY
||
386 !certificate
->has_subject(certificate
, identity
))
388 DBG1(DBG_CFG
, " id '%Y' not confirmed by certificate, "
389 "defaulting to '%Y'", identity
,
390 certificate
->get_subject(certificate
));
391 identity
->destroy(identity
);
392 identity
= certificate
->get_subject(certificate
);
393 identity
= identity
->clone(identity
);
397 cfg
->add(cfg
, AUTH_RULE_IDENTITY
, identity
);
402 identity
= identification_create_from_string(ca
);
403 certificate
= lib
->credmgr
->get_cert(lib
->credmgr
, CERT_X509
,
404 KEY_ANY
, identity
, TRUE
);
405 identity
->destroy(identity
);
408 cfg
->add(cfg
, AUTH_RULE_CA_CERT
, certificate
);
412 DBG1(DBG_CFG
, "CA certificate %s not found, discarding CA "
420 enumerator_t
*enumerator
;
423 enumerator
= enumerator_create_token(end
->groups
, ",", " ");
424 while (enumerator
->enumerate(enumerator
, &group
))
426 cfg
->add(cfg
, AUTH_RULE_GROUP
,
427 identification_create_from_string(group
));
429 enumerator
->destroy(enumerator
);
432 /* certificatePolicies */
433 if (end
->cert_policy
)
435 enumerator_t
*enumerator
;
438 enumerator
= enumerator_create_token(end
->cert_policy
, ",", " ");
439 while (enumerator
->enumerate(enumerator
, &policy
))
441 cfg
->add(cfg
, AUTH_RULE_CERT_POLICY
, strdup(policy
));
443 enumerator
->destroy(enumerator
);
446 /* authentication metod (class, actually) */
447 if (streq(auth
, "pubkey") ||
448 strneq(auth
, "rsa", strlen("rsa")) ||
449 strneq(auth
, "ecdsa", strlen("ecdsa")))
453 cfg
->add(cfg
, AUTH_RULE_AUTH_CLASS
, AUTH_CLASS_PUBKEY
);
454 build_crl_policy(cfg
, local
, msg
->add_conn
.crl_policy
);
456 if (sscanf(auth
, "rsa-%d", &strength
) == 1)
458 cfg
->add(cfg
, AUTH_RULE_RSA_STRENGTH
, (uintptr_t)strength
);
460 if (sscanf(auth
, "ecdsa-%d", &strength
) == 1)
462 cfg
->add(cfg
, AUTH_RULE_ECDSA_STRENGTH
, (uintptr_t)strength
);
465 else if (streq(auth
, "psk") || streq(auth
, "secret"))
467 cfg
->add(cfg
, AUTH_RULE_AUTH_CLASS
, AUTH_CLASS_PSK
);
469 else if (strneq(auth
, "eap", 3))
471 enumerator_t
*enumerator
;
473 int i
= 0, type
= 0, vendor
;
475 cfg
->add(cfg
, AUTH_RULE_AUTH_CLASS
, AUTH_CLASS_EAP
);
477 /* parse EAP string, format: eap[-type[-vendor]] */
478 enumerator
= enumerator_create_token(auth
, "-", " ");
479 while (enumerator
->enumerate(enumerator
, &str
))
484 type
= eap_type_from_string(str
);
490 DBG1(DBG_CFG
, "unknown EAP method: %s", str
);
494 cfg
->add(cfg
, AUTH_RULE_EAP_TYPE
, type
);
502 cfg
->add(cfg
, AUTH_RULE_EAP_VENDOR
, vendor
);
506 DBG1(DBG_CFG
, "unknown EAP vendor: %s", str
);
515 enumerator
->destroy(enumerator
);
517 if (msg
->add_conn
.eap_identity
)
519 if (streq(msg
->add_conn
.eap_identity
, "%identity"))
521 identity
= identification_create_from_encoding(ID_ANY
,
526 identity
= identification_create_from_string(
527 msg
->add_conn
.eap_identity
);
529 cfg
->add(cfg
, AUTH_RULE_EAP_IDENTITY
, identity
);
531 if (msg
->add_conn
.aaa_identity
)
533 cfg
->add(cfg
, AUTH_RULE_AAA_IDENTITY
,
534 identification_create_from_string(msg
->add_conn
.aaa_identity
));
539 if (!streq(auth
, "any"))
541 DBG1(DBG_CFG
, "authentication method %s unknown, fallback to any",
544 build_crl_policy(cfg
, local
, msg
->add_conn
.crl_policy
);
550 * build a peer_cfg from a stroke msg
552 static peer_cfg_t
*build_peer_cfg(private_stroke_config_t
*this,
553 stroke_msg_t
*msg
, ike_cfg_t
*ike_cfg
)
555 identification_t
*peer_id
= NULL
;
556 peer_cfg_t
*mediated_by
= NULL
;
558 unique_policy_t unique
;
559 u_int32_t rekey
= 0, reauth
= 0, over
, jitter
;
560 peer_cfg_t
*peer_cfg
;
561 auth_cfg_t
*auth_cfg
;
564 if (msg
->add_conn
.ikeme
.mediation
&& msg
->add_conn
.ikeme
.mediated_by
)
566 DBG1(DBG_CFG
, "a mediation connection cannot be a mediated connection "
567 "at the same time, aborting");
571 if (msg
->add_conn
.ikeme
.mediation
)
573 /* force unique connections for mediation connections */
574 msg
->add_conn
.unique
= 1;
577 if (msg
->add_conn
.ikeme
.mediated_by
)
579 mediated_by
= charon
->backends
->get_peer_cfg_by_name(charon
->backends
,
580 msg
->add_conn
.ikeme
.mediated_by
);
583 DBG1(DBG_CFG
, "mediation connection '%s' not found, aborting",
584 msg
->add_conn
.ikeme
.mediated_by
);
587 if (!mediated_by
->is_mediation(mediated_by
))
589 DBG1(DBG_CFG
, "connection '%s' as referred to by '%s' is "
590 "no mediation connection, aborting",
591 msg
->add_conn
.ikeme
.mediated_by
, msg
->add_conn
.name
);
592 mediated_by
->destroy(mediated_by
);
595 if (msg
->add_conn
.ikeme
.peerid
)
597 peer_id
= identification_create_from_string(msg
->add_conn
.ikeme
.peerid
);
599 else if (msg
->add_conn
.other
.id
)
601 peer_id
= identification_create_from_string(msg
->add_conn
.other
.id
);
606 jitter
= msg
->add_conn
.rekey
.margin
* msg
->add_conn
.rekey
.fuzz
/ 100;
607 over
= msg
->add_conn
.rekey
.margin
;
608 if (msg
->add_conn
.rekey
.reauth
)
610 reauth
= msg
->add_conn
.rekey
.ike_lifetime
- over
;
614 rekey
= msg
->add_conn
.rekey
.ike_lifetime
- over
;
616 if (msg
->add_conn
.me
.sourceip_mask
)
618 if (msg
->add_conn
.me
.sourceip
)
620 vip
= host_create_from_string(msg
->add_conn
.me
.sourceip
, 0);
623 { /* if it is set to something like %poolname, request an address */
624 if (msg
->add_conn
.me
.subnets
)
625 { /* use the same address as in subnet, if any */
626 if (strchr(msg
->add_conn
.me
.subnets
, '.'))
628 vip
= host_create_any(AF_INET
);
632 vip
= host_create_any(AF_INET6
);
637 if (strchr(ike_cfg
->get_my_addr(ike_cfg
), ':'))
639 vip
= host_create_any(AF_INET6
);
643 vip
= host_create_any(AF_INET
);
648 switch (msg
->add_conn
.unique
)
651 case 2: /* replace */
652 unique
= UNIQUE_REPLACE
;
655 unique
= UNIQUE_KEEP
;
661 if (msg
->add_conn
.dpd
.action
== 0)
662 { /* dpdaction=none disables DPD */
663 msg
->add_conn
.dpd
.delay
= 0;
666 /* other.sourceip is managed in stroke_attributes. If it is set, we define
667 * the pool name as the connection name, which the attribute provider
668 * uses to serve pool addresses. */
669 peer_cfg
= peer_cfg_create(msg
->add_conn
.name
,
670 msg
->add_conn
.ikev2 ?
2 : 1, ike_cfg
,
671 msg
->add_conn
.me
.sendcert
, unique
,
672 msg
->add_conn
.rekey
.tries
, rekey
, reauth
, jitter
, over
,
673 msg
->add_conn
.mobike
, msg
->add_conn
.dpd
.delay
,
674 vip
, msg
->add_conn
.other
.sourceip_mask ?
675 msg
->add_conn
.name
: msg
->add_conn
.other
.sourceip
,
676 msg
->add_conn
.ikeme
.mediation
, mediated_by
, peer_id
);
678 /* build leftauth= */
679 auth_cfg
= build_auth_cfg(this, msg
, TRUE
, TRUE
);
682 peer_cfg
->add_auth_cfg(peer_cfg
, auth_cfg
, TRUE
);
685 { /* we require at least one config on our side */
686 peer_cfg
->destroy(peer_cfg
);
689 /* build leftauth2= */
690 auth_cfg
= build_auth_cfg(this, msg
, TRUE
, FALSE
);
693 peer_cfg
->add_auth_cfg(peer_cfg
, auth_cfg
, TRUE
);
695 /* build rightauth= */
696 auth_cfg
= build_auth_cfg(this, msg
, FALSE
, TRUE
);
699 peer_cfg
->add_auth_cfg(peer_cfg
, auth_cfg
, FALSE
);
701 /* build rightauth2= */
702 auth_cfg
= build_auth_cfg(this, msg
, FALSE
, FALSE
);
705 peer_cfg
->add_auth_cfg(peer_cfg
, auth_cfg
, FALSE
);
711 * build a traffic selector from a stroke_end
713 static void add_ts(private_stroke_config_t
*this,
714 stroke_end_t
*end
, child_cfg_t
*child_cfg
, bool local
)
716 traffic_selector_t
*ts
;
720 ts
= traffic_selector_create_dynamic(end
->protocol
,
721 end
->port ? end
->port
: 0, end
->port ? end
->port
: 65535);
722 child_cfg
->add_traffic_selector(child_cfg
, local
, ts
);
730 net
= host_create_from_string(end
->address
, 0);
733 ts
= traffic_selector_create_from_subnet(net
, 0, end
->protocol
,
735 child_cfg
->add_traffic_selector(child_cfg
, local
, ts
);
740 char *del
, *start
, *bits
;
742 start
= end
->subnets
;
747 del
= strchr(start
, ',');
752 bits
= strchr(start
, '/');
756 intbits
= atoi(bits
+ 1);
759 net
= host_create_from_string(start
, 0);
762 ts
= traffic_selector_create_from_subnet(net
, intbits
,
763 end
->protocol
, end
->port
);
764 child_cfg
->add_traffic_selector(child_cfg
, local
, ts
);
768 DBG1(DBG_CFG
, "invalid subnet: %s, skipped", start
);
778 * build a child config from the stroke message
780 static child_cfg_t
*build_child_cfg(private_stroke_config_t
*this,
783 child_cfg_t
*child_cfg
;
785 lifetime_cfg_t lifetime
= {
787 .life
= msg
->add_conn
.rekey
.ipsec_lifetime
,
788 .rekey
= msg
->add_conn
.rekey
.ipsec_lifetime
- msg
->add_conn
.rekey
.margin
,
789 .jitter
= msg
->add_conn
.rekey
.margin
* msg
->add_conn
.rekey
.fuzz
/ 100
792 .life
= msg
->add_conn
.rekey
.life_bytes
,
793 .rekey
= msg
->add_conn
.rekey
.life_bytes
- msg
->add_conn
.rekey
.margin_bytes
,
794 .jitter
= msg
->add_conn
.rekey
.margin_bytes
* msg
->add_conn
.rekey
.fuzz
/ 100
797 .life
= msg
->add_conn
.rekey
.life_packets
,
798 .rekey
= msg
->add_conn
.rekey
.life_packets
- msg
->add_conn
.rekey
.margin_packets
,
799 .jitter
= msg
->add_conn
.rekey
.margin_packets
* msg
->add_conn
.rekey
.fuzz
/ 100
803 .value
= msg
->add_conn
.mark_in
.value
,
804 .mask
= msg
->add_conn
.mark_in
.mask
807 .value
= msg
->add_conn
.mark_out
.value
,
808 .mask
= msg
->add_conn
.mark_out
.mask
811 switch (msg
->add_conn
.dpd
.action
)
812 { /* map startes magic values to our action type */
816 case 3: /* =restart */
817 dpd
= ACTION_RESTART
;
824 child_cfg
= child_cfg_create(
825 msg
->add_conn
.name
, &lifetime
,
826 msg
->add_conn
.me
.updown
, msg
->add_conn
.me
.hostaccess
,
827 msg
->add_conn
.mode
, ACTION_NONE
, dpd
, dpd
, msg
->add_conn
.ipcomp
,
828 msg
->add_conn
.inactivity
, msg
->add_conn
.reqid
,
829 &mark_in
, &mark_out
, msg
->add_conn
.tfc
);
830 child_cfg
->set_mipv6_options(child_cfg
, msg
->add_conn
.proxy_mode
,
831 msg
->add_conn
.install_policy
);
832 add_ts(this, &msg
->add_conn
.me
, child_cfg
, TRUE
);
833 add_ts(this, &msg
->add_conn
.other
, child_cfg
, FALSE
);
835 add_proposals(this, msg
->add_conn
.algorithms
.esp
, NULL
, child_cfg
);
840 METHOD(stroke_config_t
, add
, void,
841 private_stroke_config_t
*this, stroke_msg_t
*msg
)
843 ike_cfg_t
*ike_cfg
, *existing_ike
;
844 peer_cfg_t
*peer_cfg
, *existing
;
845 child_cfg_t
*child_cfg
;
846 enumerator_t
*enumerator
;
847 bool use_existing
= FALSE
;
849 ike_cfg
= build_ike_cfg(this, msg
);
854 peer_cfg
= build_peer_cfg(this, msg
, ike_cfg
);
857 ike_cfg
->destroy(ike_cfg
);
861 enumerator
= create_peer_cfg_enumerator(this, NULL
, NULL
);
862 while (enumerator
->enumerate(enumerator
, &existing
))
864 existing_ike
= existing
->get_ike_cfg(existing
);
865 if (existing
->equals(existing
, peer_cfg
) &&
866 existing_ike
->equals(existing_ike
, peer_cfg
->get_ike_cfg(peer_cfg
)))
869 peer_cfg
->destroy(peer_cfg
);
871 peer_cfg
->get_ref(peer_cfg
);
872 DBG1(DBG_CFG
, "added child to existing configuration '%s'",
873 peer_cfg
->get_name(peer_cfg
));
877 enumerator
->destroy(enumerator
);
879 child_cfg
= build_child_cfg(this, msg
);
882 peer_cfg
->destroy(peer_cfg
);
885 peer_cfg
->add_child_cfg(peer_cfg
, child_cfg
);
889 peer_cfg
->destroy(peer_cfg
);
893 /* add config to backend */
894 DBG1(DBG_CFG
, "added configuration '%s'", msg
->add_conn
.name
);
895 this->mutex
->lock(this->mutex
);
896 this->list
->insert_last(this->list
, peer_cfg
);
897 this->mutex
->unlock(this->mutex
);
901 METHOD(stroke_config_t
, del
, void,
902 private_stroke_config_t
*this, stroke_msg_t
*msg
)
904 enumerator_t
*enumerator
, *children
;
907 bool deleted
= FALSE
;
909 this->mutex
->lock(this->mutex
);
910 enumerator
= this->list
->create_enumerator(this->list
);
911 while (enumerator
->enumerate(enumerator
, (void**)&peer
))
915 /* remove any child with such a name */
916 children
= peer
->create_child_cfg_enumerator(peer
);
917 while (children
->enumerate(children
, &child
))
919 if (streq(child
->get_name(child
), msg
->del_conn
.name
))
921 peer
->remove_child_cfg(peer
, children
);
922 child
->destroy(child
);
930 children
->destroy(children
);
932 /* if peer config matches, or has no children anymore, remove it */
933 if (!keep
|| streq(peer
->get_name(peer
), msg
->del_conn
.name
))
935 this->list
->remove_at(this->list
, enumerator
);
940 enumerator
->destroy(enumerator
);
941 this->mutex
->unlock(this->mutex
);
945 DBG1(DBG_CFG
, "deleted connection '%s'", msg
->del_conn
.name
);
949 DBG1(DBG_CFG
, "connection '%s' not found", msg
->del_conn
.name
);
953 METHOD(stroke_config_t
, destroy
, void,
954 private_stroke_config_t
*this)
956 this->list
->destroy_offset(this->list
, offsetof(peer_cfg_t
, destroy
));
957 this->mutex
->destroy(this->mutex
);
964 stroke_config_t
*stroke_config_create(stroke_ca_t
*ca
, stroke_cred_t
*cred
)
966 private_stroke_config_t
*this;
971 .create_peer_cfg_enumerator
= _create_peer_cfg_enumerator
,
972 .create_ike_cfg_enumerator
= _create_ike_cfg_enumerator
,
973 .get_peer_cfg_by_name
= _get_peer_cfg_by_name
,
979 .list
= linked_list_create(),
980 .mutex
= mutex_create(MUTEX_TYPE_RECURSIVE
),
985 return &this->public;