diff --git a/conf/Makefile.am b/conf/Makefile.am index 72d9f258d..b7edaa8ee 100644 --- a/conf/Makefile.am +++ b/conf/Makefile.am @@ -73,6 +73,7 @@ plugins = \ plugins/ntru.opt \ plugins/openssl.opt \ plugins/osx-attr.opt \ + plugins/p-cscf.opt \ plugins/pkcs11.opt \ plugins/radattr.opt \ plugins/random.opt \ diff --git a/conf/plugins/p-cscf.opt b/conf/plugins/p-cscf.opt new file mode 100644 index 000000000..ec7f8153f --- /dev/null +++ b/conf/plugins/p-cscf.opt @@ -0,0 +1,11 @@ +charon.plugins.p-cscf.enable {} + Section to enable requesting P-CSCF server addresses for individual + connections. + +charon.plugins.p-cscf.enable. = no + is the name of a connection with an ePDG from which to request + P-CSCF server addresses. + + is the name of a connection with an ePDG from which to request + P-CSCF server addresses. Requests will be sent for addresses of the same + families for which internal IPs are requested. diff --git a/configure.ac b/configure.ac index f83b02546..e4217fd91 100644 --- a/configure.ac +++ b/configure.ac @@ -228,6 +228,7 @@ ARG_DISBL_SET([attr], [disable strongswan.conf based configuration att ARG_ENABL_SET([attr-sql], [enable SQL based configuration attribute plugin.]) ARG_ENABL_SET([dhcp], [enable DHCP based attribute provider plugin.]) ARG_ENABL_SET([osx-attr], [enable OS X SystemConfiguration attribute handler.]) +ARG_ENABL_SET([p-cscf], [enable plugin to request P-CSCF server addresses from an ePDG.]) ARG_DISBL_SET([resolve], [disable resolve DNS handler plugin.]) ARG_ENABL_SET([unity], [enables Cisco Unity extension plugin.]) # TNC modules/plugins @@ -1396,6 +1397,7 @@ ADD_PLUGIN([medsrv], [c charon]) ADD_PLUGIN([medcli], [c charon]) ADD_PLUGIN([dhcp], [c charon]) ADD_PLUGIN([osx-attr], [c charon cmd]) +ADD_PLUGIN([p-cscf], [c charon cmd]) ADD_PLUGIN([android-dns], [c charon]) ADD_PLUGIN([android-log], [c charon]) ADD_PLUGIN([ha], [c charon]) @@ -1500,6 +1502,7 @@ AM_CONDITIONAL(USE_MEDSRV, test x$medsrv = xtrue) AM_CONDITIONAL(USE_MEDCLI, test x$medcli = xtrue) AM_CONDITIONAL(USE_UCI, test x$uci = xtrue) AM_CONDITIONAL(USE_OSX_ATTR, test x$osx_attr = xtrue) +AM_CONDITIONAL(USE_P_CSCF, test x$p_cscf = xtrue) AM_CONDITIONAL(USE_ANDROID_DNS, test x$android_dns = xtrue) AM_CONDITIONAL(USE_ANDROID_LOG, test x$android_log = xtrue) AM_CONDITIONAL(USE_MAEMO, test x$maemo = xtrue) @@ -1839,6 +1842,7 @@ AC_CONFIG_FILES([ src/libcharon/plugins/coupling/Makefile src/libcharon/plugins/radattr/Makefile src/libcharon/plugins/osx_attr/Makefile + src/libcharon/plugins/p_cscf/Makefile src/libcharon/plugins/android_dns/Makefile src/libcharon/plugins/android_log/Makefile src/libcharon/plugins/maemo/Makefile diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 8f8c96588..55e6bc58b 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -156,6 +156,8 @@ endif LOCAL_SRC_FILES += $(call add_plugin, attr) +LOCAL_SRC_FILES += $(call add_plugin, p-cscf) + LOCAL_SRC_FILES += $(call add_plugin, eap-aka) LOCAL_SRC_FILES += $(call add_plugin, eap-aka-3gpp2) diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 61e57c1cb..9f0707813 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -489,6 +489,13 @@ if MONOLITHIC endif endif +if USE_P_CSCF + SUBDIRS += plugins/p_cscf +if MONOLITHIC + libcharon_la_LIBADD += plugins/p_cscf/libstrongswan-p-cscf.la +endif +endif + if USE_ANDROID_DNS SUBDIRS += plugins/android_dns if MONOLITHIC diff --git a/src/libcharon/attributes/attributes.c b/src/libcharon/attributes/attributes.c index 9fabcf4e4..0f28d55fa 100644 --- a/src/libcharon/attributes/attributes.c +++ b/src/libcharon/attributes/attributes.c @@ -17,7 +17,7 @@ #include "attributes.h" -ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, HOME_AGENT_ADDRESS, +ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, P_CSCF_IP6_ADDRESS, "INTERNAL_IP4_ADDRESS", "INTERNAL_IP4_NETMASK", "INTERNAL_IP4_DNS", @@ -36,8 +36,10 @@ ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, HOME_AGENT_ "MIP6_HOME_PREFIX", "INTERNAL_IP6_LINK", "INTERNAL_IP6_PREFIX", - "HOME_AGENT_ADDRESS"); -ENUM_NEXT(configuration_attribute_type_names, XAUTH_TYPE, XAUTH_ANSWER, HOME_AGENT_ADDRESS, + "HOME_AGENT_ADDRESS", + "P_CSCF_IP4_ADDRESS", + "P_CSCF_IP6_ADDRESS"); +ENUM_NEXT(configuration_attribute_type_names, XAUTH_TYPE, XAUTH_ANSWER, P_CSCF_IP6_ADDRESS, "XAUTH_TYPE", "XAUTH_USER_NAME", "XAUTH_USER_PASSWORD", @@ -65,7 +67,7 @@ ENUM_NEXT(configuration_attribute_type_names, UNITY_BANNER, UNITY_DDNS_HOSTNAME, "UNITY_DDNS_HOSTNAME"); ENUM_END(configuration_attribute_type_names, UNITY_DDNS_HOSTNAME); -ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, HOME_AGENT_ADDRESS, +ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, P_CSCF_IP6_ADDRESS, "ADDR", "MASK", "DNS", @@ -84,8 +86,10 @@ ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, HOME_ "MIP6HPFX", "LINK6", "PFX6", - "HOA"); -ENUM_NEXT(configuration_attribute_type_short_names, XAUTH_TYPE, XAUTH_ANSWER, HOME_AGENT_ADDRESS, + "HOA", + "PCSCF4", + "PCSCF6"); +ENUM_NEXT(configuration_attribute_type_short_names, XAUTH_TYPE, XAUTH_ANSWER, P_CSCF_IP6_ADDRESS, "X_TYPE", "X_USER", "X_PWD", diff --git a/src/libcharon/attributes/attributes.h b/src/libcharon/attributes/attributes.h index 5d1e9f9ba..dd1db4fc3 100644 --- a/src/libcharon/attributes/attributes.h +++ b/src/libcharon/attributes/attributes.h @@ -49,6 +49,9 @@ enum configuration_attribute_type_t { INTERNAL_IP6_LINK = 17, INTERNAL_IP6_PREFIX = 18, HOME_AGENT_ADDRESS = 19, + /* RFC 7651 */ + P_CSCF_IP4_ADDRESS = 20, + P_CSCF_IP6_ADDRESS = 21, /* XAUTH attributes */ XAUTH_TYPE = 16520, XAUTH_USER_NAME = 16521, diff --git a/src/libcharon/encoding/payloads/configuration_attribute.c b/src/libcharon/encoding/payloads/configuration_attribute.c index 0bc94708f..4ecdf569d 100644 --- a/src/libcharon/encoding/payloads/configuration_attribute.c +++ b/src/libcharon/encoding/payloads/configuration_attribute.c @@ -132,6 +132,7 @@ METHOD(payload_t, verify, status_t, case INTERNAL_IP4_NBNS: case INTERNAL_ADDRESS_EXPIRY: case INTERNAL_IP4_DHCP: + case P_CSCF_IP4_ADDRESS: if (this->length_or_value != 0 && this->length_or_value != 4) { failed = TRUE; @@ -160,6 +161,7 @@ METHOD(payload_t, verify, status_t, case INTERNAL_IP6_DNS: case INTERNAL_IP6_NBNS: case INTERNAL_IP6_DHCP: + case P_CSCF_IP6_ADDRESS: if (this->length_or_value != 0 && this->length_or_value != 16) { failed = TRUE; diff --git a/src/libcharon/plugins/attr/attr_provider.c b/src/libcharon/plugins/attr/attr_provider.c index cac0ae4bf..1de571c3f 100644 --- a/src/libcharon/plugins/attr/attr_provider.c +++ b/src/libcharon/plugins/attr/attr_provider.c @@ -54,6 +54,8 @@ struct attribute_entry_t { configuration_attribute_type_t type; /** attribute value */ chunk_t value; + /** associated IKE version */ + ike_version_t ike; }; /** @@ -65,27 +67,52 @@ static void attribute_destroy(attribute_entry_t *this) free(this); } +/** + * Data for attribute enumerator + */ +typedef struct { + rwlock_t *lock; + ike_version_t ike; +} enumerator_data_t; + /** * convert enumerator value from attribute_entry */ -static bool attr_enum_filter(void *null, attribute_entry_t **in, +static bool attr_enum_filter(enumerator_data_t *data, attribute_entry_t **in, configuration_attribute_type_t *type, void* none, chunk_t *value) { - *type = (*in)->type; - *value = (*in)->value; - return TRUE; + if ((*in)->ike == IKE_ANY || (*in)->ike == data->ike) + { + *type = (*in)->type; + *value = (*in)->value; + return TRUE; + } + return FALSE; +} + +CALLBACK(attr_enum_destroy, void, + enumerator_data_t *data) +{ + data->lock->unlock(data->lock); + free(data); } METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, private_attr_provider_t *this, linked_list_t *pools, ike_sa_t *ike_sa, linked_list_t *vips) { + enumerator_data_t *data; + if (vips->get_count(vips)) { + INIT(data, + .lock = this->lock, + .ike = ike_sa->get_version(ike_sa), + ); this->lock->read_lock(this->lock); return enumerator_create_filter( this->attributes->create_enumerator(this->attributes), - (void*)attr_enum_filter, this->lock, (void*)this->lock->unlock); + (void*)attr_enum_filter, data, attr_enum_destroy); } return enumerator_create_empty(); } @@ -116,8 +143,6 @@ static void add_legacy_entry(private_attr_provider_t *this, char *key, int nr, host = host_create_from_string(str, 0); if (host) { - entry = malloc_thing(attribute_entry_t); - if (host->get_family(host) == AF_INET6) { switch (type) @@ -132,8 +157,11 @@ static void add_legacy_entry(private_attr_provider_t *this, char *key, int nr, break; } } - entry->type = type; - entry->value = chunk_clone(host->get_address(host)); + INIT(entry, + .type = type, + .value = chunk_clone(host->get_address(host)), + .ike = IKE_ANY, + ); host->destroy(host); DBG2(DBG_CFG, "loaded legacy entry attribute %N: %#B", configuration_attribute_type_names, entry->type, &entry->value); @@ -149,18 +177,20 @@ typedef struct { char *name; configuration_attribute_type_t v4; configuration_attribute_type_t v6; + ike_version_t ike; } attribute_type_key_t; static attribute_type_key_t keys[] = { - {"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS}, - {"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS}, - {"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS}, - {"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP}, - {"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK}, - {"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER}, - {"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET}, - {"split-include", UNITY_SPLIT_INCLUDE, UNITY_SPLIT_INCLUDE}, - {"split-exclude", UNITY_LOCAL_LAN, UNITY_LOCAL_LAN}, + {"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS, IKE_ANY}, + {"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS, IKE_ANY}, + {"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS, IKE_ANY}, + {"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP, IKE_ANY}, + {"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK, IKE_ANY}, + {"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER, IKE_ANY}, + {"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET, IKE_ANY}, + {"p-cscf", P_CSCF_IP4_ADDRESS, P_CSCF_IP6_ADDRESS, IKEV2}, + {"split-include", UNITY_SPLIT_INCLUDE, UNITY_SPLIT_INCLUDE, IKEV1}, + {"split-exclude", UNITY_LOCAL_LAN, UNITY_LOCAL_LAN, IKEV1}, }; /** @@ -275,6 +305,7 @@ static void load_entries(private_attr_provider_t *this) INIT(entry, .type = type, .value = data, + .ike = mapped ? mapped->ike : IKE_ANY, ); DBG2(DBG_CFG, "loaded attribute %N: %#B", configuration_attribute_type_names, entry->type, &entry->value); diff --git a/src/libcharon/plugins/p_cscf/Makefile.am b/src/libcharon/plugins/p_cscf/Makefile.am new file mode 100644 index 000000000..1e00a56a8 --- /dev/null +++ b/src/libcharon/plugins/p_cscf/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-p-cscf.la +else +plugin_LTLIBRARIES = libstrongswan-p-cscf.la +endif + +libstrongswan_p_cscf_la_SOURCES = \ + p_cscf_plugin.c p_cscf_plugin.h \ + p_cscf_handler.c p_cscf_handler.h + +libstrongswan_p_cscf_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/p_cscf/p_cscf_handler.c b/src/libcharon/plugins/p_cscf/p_cscf_handler.c new file mode 100644 index 000000000..76633845e --- /dev/null +++ b/src/libcharon/plugins/p_cscf/p_cscf_handler.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "p_cscf_handler.h" + +#include +#include + +typedef struct private_p_cscf_handler_t private_p_cscf_handler_t; + +/** + * Private data + */ +struct private_p_cscf_handler_t { + + /** + * Public interface + */ + p_cscf_handler_t public; +}; + +METHOD(attribute_handler_t, handle, bool, + private_p_cscf_handler_t *this, ike_sa_t *ike_sa, + configuration_attribute_type_t type, chunk_t data) +{ + host_t *server; + int family = AF_INET6; + + switch (type) + { + case P_CSCF_IP4_ADDRESS: + family = AF_INET; + /* fall-through */ + case P_CSCF_IP6_ADDRESS: + server = host_create_from_chunk(family, data, 0); + if (!server) + { + DBG1(DBG_CFG, "received invalid P-CSCF server IP"); + return FALSE; + } + DBG1(DBG_CFG, "received P-CSCF server IP %H", server); + server->destroy(server); + return TRUE; + default: + return FALSE; + } +} + +METHOD(attribute_handler_t, release, void, + private_p_cscf_handler_t *this, ike_sa_t *ike_sa, + configuration_attribute_type_t type, chunk_t data) +{ + switch (type) + { + case P_CSCF_IP4_ADDRESS: + case P_CSCF_IP6_ADDRESS: + /* nothing to do as we only log the server IPs */ + break; + default: + break; + } +} + +/** + * Data for attribute enumerator + */ +typedef struct { + enumerator_t public; + bool request_ipv4; + bool request_ipv6; +} attr_enumerator_t; + +METHOD(enumerator_t, enumerate_attrs, bool, + attr_enumerator_t *this, configuration_attribute_type_t *type, + chunk_t *data) +{ + if (this->request_ipv4) + { + *type = P_CSCF_IP4_ADDRESS; + *data = chunk_empty; + this->request_ipv4 = FALSE; + return TRUE; + } + if (this->request_ipv6) + { + *type = P_CSCF_IP6_ADDRESS; + *data = chunk_empty; + this->request_ipv6 = FALSE; + return TRUE; + } + return FALSE; +} + +/** + * Check if the given host has a matching address family + */ +static bool is_family(host_t *host, int *family) +{ + return host->get_family(host) == *family; +} + +/** + * Check if a list has a host of a given family + */ +static bool has_host_family(linked_list_t *list, int family) +{ + return list->find_first(list, (void*)is_family, NULL, &family) == SUCCESS; +} + +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *, + private_p_cscf_handler_t *this, ike_sa_t *ike_sa, + linked_list_t *vips) +{ + attr_enumerator_t *enumerator; + + if (ike_sa->get_version(ike_sa) == IKEV1) + { + return enumerator_create_empty(); + } + + INIT(enumerator, + .public = { + .enumerate = (void*)_enumerate_attrs, + .destroy = (void*)free, + }, + ); + if (lib->settings->get_bool(lib->settings, "%s.plugins.p-cscf.enable.%s", + FALSE, lib->ns, ike_sa->get_name(ike_sa))) + { + enumerator->request_ipv4 = has_host_family(vips, AF_INET); + enumerator->request_ipv6 = has_host_family(vips, AF_INET6); + } + return &enumerator->public; +} + +METHOD(p_cscf_handler_t, destroy, void, + private_p_cscf_handler_t *this) +{ + free(this); +} + +/** + * See header + */ +p_cscf_handler_t *p_cscf_handler_create() +{ + private_p_cscf_handler_t *this; + + INIT(this, + .public = { + .handler = { + .handle = _handle, + .release = _release, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .destroy = _destroy, + }, + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/p_cscf/p_cscf_handler.h b/src/libcharon/plugins/p_cscf/p_cscf_handler.h new file mode 100644 index 000000000..ad4f1acce --- /dev/null +++ b/src/libcharon/plugins/p_cscf/p_cscf_handler.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup p_cscf_handler p_cscf_handler + * @{ @ingroup p_cscf + */ + +#ifndef P_CSCF_HANDLER_H_ +#define P_CSCF_HANDLER_H_ + +#include + +typedef struct p_cscf_handler_t p_cscf_handler_t; + +/** + * Attribute handler for P-CSCF server addresses. + */ +struct p_cscf_handler_t { + + /** + * Implements attribute_handler_t. + */ + attribute_handler_t handler; + + /** + * Destroy a p_cscf_handler_t. + */ + void (*destroy)(p_cscf_handler_t *this); +}; + +/** + * Create an p_cscf_handler_t instance. + */ +p_cscf_handler_t *p_cscf_handler_create(); + +#endif /** P_CSCF_HANDLER_H_ @}*/ diff --git a/src/libcharon/plugins/p_cscf/p_cscf_plugin.c b/src/libcharon/plugins/p_cscf/p_cscf_plugin.c new file mode 100644 index 000000000..8e2bc727e --- /dev/null +++ b/src/libcharon/plugins/p_cscf/p_cscf_plugin.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "p_cscf_plugin.h" +#include "p_cscf_handler.h" + +#include + +typedef struct private_p_cscf_plugin_t private_p_cscf_plugin_t; + +/** + * Private data + */ +struct private_p_cscf_plugin_t { + + /** + * Public interface + */ + p_cscf_plugin_t public; + + /** + * P-CSCF server address attribute handler + */ + p_cscf_handler_t *handler; +}; + +METHOD(plugin_t, get_name, char*, + private_p_cscf_plugin_t *this) +{ + return "p-cscf"; +} + +/** + * Register handler + */ +static bool plugin_cb(private_p_cscf_plugin_t *this, + plugin_feature_t *feature, bool reg, void *cb_data) +{ + if (reg) + { + charon->attributes->add_handler(charon->attributes, + &this->handler->handler); + } + else + { + charon->attributes->remove_handler(charon->attributes, + &this->handler->handler); + } + return TRUE; +} + +METHOD(plugin_t, get_features, int, + private_p_cscf_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL), + PLUGIN_PROVIDE(CUSTOM, "p-cscf"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_p_cscf_plugin_t *this) +{ + this->handler->destroy(this->handler); + free(this); +} + +/** + * See header + */ +plugin_t *p_cscf_plugin_create() +{ + private_p_cscf_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + .handler = p_cscf_handler_create(), + ); + + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/p_cscf/p_cscf_plugin.h b/src/libcharon/plugins/p_cscf/p_cscf_plugin.h new file mode 100644 index 000000000..51b17674d --- /dev/null +++ b/src/libcharon/plugins/p_cscf/p_cscf_plugin.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup p_cscf p_cscf + * @ingroup cplugins + * + * @defgroup p_cscf_plugin p_cscf_plugin + * @{ @ingroup p_cscf + */ + +#ifndef P_CSCF_PLUGIN_H_ +#define P_CSCF_PLUGIN_H_ + +#include + +typedef struct p_cscf_plugin_t p_cscf_plugin_t; + +/** + * Plugin that requests P-CSCF server addresses from an ePDG as specified + * in RFC 7651. + */ +struct p_cscf_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** P_CSCF_PLUGIN_H_ @}*/