diff --git a/res/res_pjsip_volte.c b/res/res_pjsip_volte.c
new file mode 100644
index 0000000000..11b199e224
--- /dev/null
+++ b/res/res_pjsip_volte.c
@@ -0,0 +1,287 @@
+/*** MODULEINFO
+ pjproject
+ res_pjsip
+ res_pjsip_session
+ core
+ ***/
+
+#include "asterisk.h"
+
+#include
+#include
+#include
+#include
+
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_session.h"
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+#include "asterisk/causes.h"
+#include "asterisk/threadpool.h"
+
+#define PJ_CONSUME PJ_TRUE
+
+const pj_str_t STR_SUPPORTED = { "Supported", 9 };
+const pj_str_t STR_REQUIRE = { "Require", 7 };
+const pj_str_t STR_PROXY_REQUIRE = { "Proxy-Require", 13 };
+const pj_str_t STR_PATH = { "path", 4 };
+const pj_str_t STR_SEC_AGREE = { "sec-agree", 9 };
+
+const pj_str_t STR_SECURITY_CLIENT = { "Security-Client", 15 };
+
+const char *ipsec_alg[] = {
+ "hmac-md5-96",
+ "hmac-sha-1-96",
+ NULL
+};
+
+const char *ipsec_ealg[] = {
+ "null",
+ NULL
+};
+
+
+static pj_status_t on_load(pjsip_endpoint *endpt)
+{
+ printf("endpoint!\n");
+ return PJ_SUCCESS;
+}
+
+/* Check if message is exchanged with IMS.
+ * This approach uses the host name of the URI. */
+static pj_bool_t is_ims(const pjsip_msg *msg)
+{
+ pjsip_fromto_hdr *hdr;
+ pjsip_sip_uri *uri;
+ size_t uri_len;
+
+ /* Get URI. */
+ hdr = pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL);
+ if (!hdr)
+ return PJ_FALSE;
+ uri = pjsip_uri_get_uri(hdr->uri);
+ uri_len = strlen(uri->host.ptr);
+
+ if (uri_len < 20)
+ return PJ_FALSE;
+
+ if (!!strncmp(uri->host.ptr, "ims.", 4))
+ return PJ_FALSE;
+
+ if (!!strncmp(uri->host.ptr + uri_len - 16, ".3gppnetwork.org", 16))
+ return PJ_FALSE;
+
+ return PJ_TRUE;
+}
+
+
+#warning ip xfrm state add src dst proto esp spi auth hmac-sha1-96 0x
+
+static void add_securety_client_hdr(pjsip_tx_data *tdata, const char *alg[], const char *ealg[], uint32_t spi_c, uint32_t spi_s, uint16_t port_c, uint16_t port_s)
+{
+ pjsip_generic_array_hdr *hdr;
+ char str[256];
+ int i, j;
+
+ puts("add sec");
+ /* Add Security-Client header. */
+ hdr = pjsip_generic_array_hdr_create(tdata->pool, &STR_SECURITY_CLIENT);
+ if (!hdr) {
+ ast_log(LOG_ERROR, "Failed to create header.");
+ return;
+ }
+
+ /* Create tupple for given algorithms. */
+ for (i = 0; alg[i]; i++) {
+ for (j = 0; ealg[j]; j++) {
+ snprintf(str, sizeof(str), "ipsec-3gpp; alg=%s; ealg=%s; spi-c=%u; spi-s=%u; port-c=%u; port-s=%u", alg[i], ealg[j], spi_c, spi_s, port_c, port_s);
+ if (hdr->count == PJSIP_GENERIC_ARRAY_MAX_COUNT) {
+ ast_log(LOG_ERROR, "Too many evalue in array, skipping '%s'.", str);
+ continue;
+ }
+ pj_strdup2(tdata->pool, &hdr->values[hdr->count++], str);
+ }
+ }
+
+ /* Append header */
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
+}
+
+static void add_value_string_hdr(pjsip_tx_data *tdata, const pj_str_t *name, const pj_str_t *value)
+{
+ pjsip_generic_string_hdr *hdr;
+
+ /* Add Proxy-Require header. */
+ hdr = pjsip_generic_string_hdr_create(tdata->pool, name, value);
+ if (!hdr) {
+ ast_log(LOG_ERROR, "Failed to create header.");
+ return;
+ }
+
+ /* Append header */
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
+}
+
+static void add_value_array_hdr(pjsip_tx_data *tdata, const pj_str_t *name, const pj_str_t *value)
+{
+ pjsip_generic_array_hdr *hdr;
+ pj_bool_t created = PJ_FALSE;
+
+ /* Create header, if not yet created. */
+ hdr = pjsip_msg_find_hdr_by_name(tdata->msg, name, NULL);
+ if (!hdr) {
+ hdr = pjsip_generic_array_hdr_create(tdata->pool, name);
+ created = PJ_TRUE;
+ }
+ if (!hdr) {
+ ast_log(LOG_ERROR, "Failed to create header.");
+ return;
+ }
+ if (hdr->count == PJSIP_GENERIC_ARRAY_MAX_COUNT) {
+ ast_log(LOG_ERROR, "Too many evalue in array, skipping %s.", value->ptr);
+ return;
+ }
+ pj_strdup(tdata->pool, &hdr->values[hdr->count++], value);
+
+ /* Append header, if created. */
+ if (created)
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
+}
+
+static pj_status_t on_tx_register_request(pjsip_tx_data *tdata)
+{
+ if (!is_ims(tdata->msg))
+ return PJ_SUCCESS;
+
+ /* "Require: sec-agree" */
+ add_value_array_hdr(tdata, &STR_REQUIRE, &STR_SEC_AGREE);
+ /* "Proxy-Require: sec-agree" */
+ add_value_string_hdr(tdata, &STR_PROXY_REQUIRE, &STR_SEC_AGREE);
+ /* "Supported: path,sec-agree" */
+ add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_PATH);
+ /* "Seciruty-Client: ..." */
+ add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_SEC_AGREE);
+ add_securety_client_hdr(tdata, ipsec_alg, ipsec_ealg, 0xbea96586, 0x14ac7e17, 43419, 42318);
+
+
+
+
+
+#warning HACKING: Asterisk must do this on first register! Or we must do it!
+ const pj_str_t STR_AUTHORIZATION = { "Authorization", 13 };
+ char xxx[] = "Digest uri=\"sip:ims.mnc001.mcc238.3gppnetwork.org\",username=\"238010000090828@ims.mnc001.mcc238.3gppnetwork.org\",response=\"\",realm=\"ims.mnc001.mcc238.3gppnetwork.org\",nonce=\"\"";
+ const pj_str_t STR_XXX = { xxx, strlen(xxx)};
+ add_value_string_hdr(tdata, &STR_AUTHORIZATION, &STR_XXX);
+
+
+
+
+
+ return PJ_SUCCESS;
+}
+
+/* What PJSIP will requests */
+static pj_status_t on_tx_request(pjsip_tx_data *tdata)
+{
+ const pjsip_msg *msg = tdata->msg;
+
+ puts("tx request");
+ if (!msg) {
+ ast_log(LOG_ERROR, "No message in request.");
+ return PJ_CONSUME;
+
+ }
+ const pj_str_t REFER_TO = { "User-Agent", 10 };
+ pjsip_generic_string_hdr *refer_to_hdr;
+
+ switch (msg->line.req.method.id) {
+ case PJSIP_REGISTER_METHOD:
+ puts("register");
+ return on_tx_register_request(tdata);
+ break;
+ case PJSIP_INVITE_METHOD:
+ break;
+ default:
+ break;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* What response PJSIP receives */
+static pj_bool_t on_rx_response(pjsip_rx_data *rdata)
+{
+ const pjsip_msg *msg = rdata->msg_info.msg;
+
+ puts("rx response");
+ if (!msg) {
+ ast_log(LOG_ERROR, "No message in request.");
+ return PJ_CONSUME;
+ }
+
+ const pj_str_t REFER_TO = { "Server", 6 };
+ pjsip_generic_string_hdr *refer_to_hdr;
+
+ refer_to_hdr = pjsip_msg_find_hdr_by_name(msg, &REFER_TO, NULL);
+ printf("server=%s\n", refer_to_hdr->hvalue.ptr);
+
+ printf("%d\n", msg->line.req.method.id);
+ switch ((int)msg->line.req.method.id) {
+ case 403:
+ puts("forbidden");
+ break;
+ default:
+ break;
+ }
+
+ return PJ_FALSE;
+}
+
+/* What request PJSIP receives */
+static pj_bool_t on_rx_request(pjsip_rx_data *rdata)
+{
+ puts("rx request");
+ return PJ_TRUE;
+}
+
+/* What PJSIP will response */
+static pj_status_t on_tx_response(pjsip_tx_data *tdata)
+{
+ puts("tx response");
+ return PJ_SUCCESS;
+}
+
+static pjsip_module volte_module = {
+ .name = {"VoLTE Support Module", 18},
+ .priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER,
+ .load = on_load,
+ .on_tx_request = on_tx_request,
+ .on_rx_response = on_rx_response,
+ .on_tx_response = on_tx_response,
+ .on_rx_request = on_rx_request,
+};
+
+
+static int load_module(void)
+{
+ if (ast_sip_register_service(&volte_module) != PJ_SUCCESS) {
+ ast_log(LOG_ERROR, "Failed to load VoLTE support module.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_sip_unregister_service(&volte_module);
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "VoLTE support",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+ .requires = "res_pjsip,res_pjsip_session", );