Răsfoiți Sursa

Merge pull request #2001 from alexyosifov/ipsec_tcp_multiple_conns

Ipsec tcp multiple conns
Tsvetomir Dimitrov 6 ani în urmă
părinte
comite
57961c9f48

+ 161 - 69
src/modules/ims_ipsec_pcscf/cmd.c

@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2012 Smile Communications, [email protected]
  * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2019 Aleksandar Yosifov
  *
  * The initial version of this code was written by Dragos Vingarzan
  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
@@ -49,6 +50,9 @@
 
 #include "ipsec.h"
 #include "spi_gen.h"
+#include "port_gen.h"
+#include "cmd.h"
+#include "sec_agree.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -58,9 +62,12 @@
 
 extern str ipsec_listen_addr;
 extern str ipsec_listen_addr6;
-extern short ipsec_listen_port;
-extern short ipsec_server_port;
-extern short ipsec_client_port;
+extern int ipsec_server_port;
+extern int ipsec_client_port;
+
+extern int spi_id_start;
+
+extern unsigned int init_flag;
 
 // check http://www.asipto.com/pub/kamailio-devel-guide/#c16return_values
 const int IPSEC_CMD_FAIL = -1;
@@ -69,6 +76,20 @@ const int IPSEC_CMD_SUCCESS = 1;
 extern usrloc_api_t ul;
 extern struct tm_binds tmb;
 
+int bind_ipsec_pcscf(ipsec_pcscf_api_t* api) {
+	if(!api){
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+	if(init_flag == 0){
+		LM_ERR("configuration error - trying to bind to ipsec pscscf module before being initialized\n");
+		return -1;
+	}
+
+	api->ipsec_on_expire = ipsec_on_expire;
+
+	return 0;
+}
 
 static str get_www_auth_param(const char* param_name, str www_auth)
 {
@@ -139,6 +160,7 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
         ci->via_port = uri.port_no ? uri.port_no : 5060;
         ci->via_prot = 0;
         ci->aor = m->first_line.u.request.uri;
+        ci->searchflag = SEARCH_NORMAL;
 
         req = m;
     }
@@ -168,6 +190,7 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
         ci->via_port = vb->port;
         ci->via_prot = vb->proto;
         ci->aor = cb->contacts->uri;
+        ci->searchflag = SEARCH_RECEIVED;
     }
     else {
         LM_ERR("Unknown first line type: %d\n", m->first_line.type);
@@ -270,10 +293,23 @@ static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m)
         return -1;
     }
 
+    s->port_pc = acquire_cport();
+    s->port_ps = acquire_sport();
+
+    if(s->port_pc == 0){
+        LM_ERR("No free client port for IPSEC tunnel creation\n");
+        return -1;
+    }
+
+    if(s->port_ps == 0){
+        LM_ERR("No free server port for IPSEC tunnel creation\n");
+        return -1;
+    }
+
     return 0;
 }
 
-static int create_ipsec_tunnel(const struct ip_addr *remote_addr, unsigned short proto, ipsec_t* s)
+static int create_ipsec_tunnel(const struct ip_addr *remote_addr, ipsec_t* s)
 {
     struct mnl_socket* sock = init_mnl_socket();
     if (sock == NULL) {
@@ -306,74 +342,91 @@ static int create_ipsec_tunnel(const struct ip_addr *remote_addr, unsigned short
         return -1;
     }
 
-    LM_DBG("Creating security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %s; client port %d server port %d; spi_ps %u, spi_pc %u, spi_us %u, spi_uc %u\n",
+    LM_DBG("Creating security associations: Local IP: %.*s port_pc: %d port_ps: %d; UE IP: %s; port_uc %d port_us %d; spi_pc %u, spi_ps %u, spi_uc %u, spi_us %u\n",
             remote_addr->af == AF_INET ? ipsec_listen_addr.len : ipsec_listen_addr6.len,
             remote_addr->af == AF_INET ? ipsec_listen_addr.s : ipsec_listen_addr6.s,
-            ipsec_client_port, ipsec_server_port, remote_addr_str, s->port_uc, s->port_us, s->spi_ps, s->spi_pc, s->spi_us, s->spi_uc);
+            s->port_pc, s->port_ps, remote_addr_str, s->port_uc, s->port_us, s->spi_pc, s->spi_ps, s->spi_uc, s->spi_us);
 
     // SA1 UE client to P-CSCF server
-    //                      src adrr     dst addr     src port    dst port
-    add_sa    (sock, proto, remote_addr, &ipsec_addr, s->port_uc, ipsec_server_port, s->spi_ps, s->ck, s->ik, s->r_alg);
-    add_policy(sock, proto, remote_addr, &ipsec_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
+    //               src adrr     dst addr     src port    dst port
+    add_sa    (sock, remote_addr, &ipsec_addr, s->port_uc, s->port_ps, s->spi_ps, s->ck, s->ik, s->r_alg);
+    add_policy(sock, remote_addr, &ipsec_addr, s->port_uc, s->port_ps, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
 
     // SA2 P-CSCF client to UE server
-    //                      src adrr     dst addr     src port           dst port
-    add_sa    (sock, proto, &ipsec_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, s->ck, s->ik, s->r_alg);
-    add_policy(sock, proto, &ipsec_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
+    //               src adrr     dst addr     src port           dst port
+    add_sa    (sock, &ipsec_addr, remote_addr, s->port_pc, s->port_us, s->spi_us, s->ck, s->ik, s->r_alg);
+    add_policy(sock, &ipsec_addr, remote_addr, s->port_pc, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
 
     // SA3 P-CSCF server to UE client
-    //                      src adrr     dst addr     src port           dst port
-    add_sa    (sock, proto, &ipsec_addr, remote_addr, ipsec_server_port, s->port_uc, s->spi_uc, s->ck, s->ik, s->r_alg);
-    add_policy(sock, proto, &ipsec_addr, remote_addr, ipsec_server_port, s->port_uc, s->spi_uc, IPSEC_POLICY_DIRECTION_OUT);
+    //               src adrr     dst addr     src port           dst port
+    add_sa    (sock, &ipsec_addr, remote_addr, s->port_ps, s->port_uc, s->spi_uc, s->ck, s->ik, s->r_alg);
+    add_policy(sock, &ipsec_addr, remote_addr, s->port_ps, s->port_uc, s->spi_uc, IPSEC_POLICY_DIRECTION_OUT);
 
     // SA4 UE server to P-CSCF client
-    //                      src adrr     dst addr     src port    dst port
-    add_sa    (sock, proto, remote_addr, &ipsec_addr, s->port_us, ipsec_client_port, s->spi_pc, s->ck, s->ik, s->r_alg);
-    add_policy(sock, proto, remote_addr, &ipsec_addr, s->port_us, ipsec_client_port, s->spi_pc, IPSEC_POLICY_DIRECTION_IN);
+    //               src adrr     dst addr     src port    dst port
+    add_sa    (sock, remote_addr, &ipsec_addr, s->port_us, s->port_pc, s->spi_pc, s->ck, s->ik, s->r_alg);
+    add_policy(sock, remote_addr, &ipsec_addr, s->port_us, s->port_pc, s->spi_pc, IPSEC_POLICY_DIRECTION_IN);
 
     close_mnl_socket(sock);
 
     return 0;
 }
 
-static int destroy_ipsec_tunnel(const str remote_addr, unsigned short proto, ipsec_t* s)
+static int destroy_ipsec_tunnel(str remote_addr, ipsec_t* s, unsigned short received_port)
 {
     struct mnl_socket* sock = init_mnl_socket();
     if (sock == NULL) {
         return -1;
     }
 
-    // TODO: pass ipsec listen address v4 or v6 to destroy the tunnel
+    ip_addr_t   ip_addr;
+    str         ipsec_addr;
+
+    // convert 'remote_addr' ip string to ip_addr_t
+    if(str2ipxbuf(&remote_addr, &ip_addr) < 0){
+        LM_ERR("Unable to convert remote address [%.*s]\n", remote_addr.len, remote_addr.s);
+        return -1;
+    }
+
+    if(ip_addr.af == AF_INET6){
+        ipsec_addr = ipsec_listen_addr6;
+    }else{
+        ipsec_addr = ipsec_listen_addr;
+    }
 
-    LM_DBG("Destroying security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d\n",
-            ipsec_listen_addr.len, ipsec_listen_addr.s, ipsec_client_port, ipsec_server_port,
-            remote_addr.len, remote_addr.s, s->port_uc, s->port_us);
+    LM_DBG("Destroying security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d; spi_ps %u, spi_pc %u, spi_us %u, spi_uc %u\n",
+            ipsec_addr.len, ipsec_addr.s, s->port_pc, s->port_ps,
+            remote_addr.len, remote_addr.s, s->port_uc, s->port_us, s->spi_ps, s->spi_pc, s->spi_us, s->spi_uc);
 
     // SA1 UE client to P-CSCF server
-    remove_sa    (sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps);
-    remove_policy(sock, proto, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
+    remove_sa    (sock, remote_addr, ipsec_addr, s->port_uc, s->port_ps, s->spi_ps, ip_addr.af);
+    remove_policy(sock, remote_addr, ipsec_addr, s->port_uc, s->port_ps, s->spi_ps, ip_addr.af, IPSEC_POLICY_DIRECTION_IN);
 
     // SA2 P-CSCF client to UE server
-    remove_sa    (sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us);
-    remove_policy(sock, proto, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
+    remove_sa    (sock, ipsec_addr, remote_addr, s->port_pc, s->port_us, s->spi_us, ip_addr.af);
+    remove_policy(sock, ipsec_addr, remote_addr, s->port_pc, s->port_us, s->spi_us, ip_addr.af, IPSEC_POLICY_DIRECTION_OUT);
 
     // SA3 P-CSCF server to UE client
-    remove_sa    (sock, ipsec_listen_addr, remote_addr, ipsec_server_port, s->port_uc, s->spi_uc);
-    remove_policy(sock, proto, ipsec_listen_addr, remote_addr, ipsec_server_port, s->port_uc, s->spi_uc, IPSEC_POLICY_DIRECTION_OUT);
+    remove_sa    (sock, ipsec_addr, remote_addr, s->port_ps, s->port_uc, s->spi_uc, ip_addr.af);
+    remove_policy(sock, ipsec_addr, remote_addr, s->port_ps, s->port_uc, s->spi_uc, ip_addr.af, IPSEC_POLICY_DIRECTION_OUT);
 
     // SA4 UE server to P-CSCF client
-    remove_sa    (sock, remote_addr, ipsec_listen_addr, s->port_us, ipsec_client_port, s->spi_pc);
-    remove_policy(sock, proto, remote_addr, ipsec_listen_addr, s->port_us, ipsec_client_port, s->spi_pc, IPSEC_POLICY_DIRECTION_IN);
+    remove_sa    (sock, remote_addr, ipsec_addr, s->port_us, s->port_pc, s->spi_pc, ip_addr.af);
+    remove_policy(sock, remote_addr, ipsec_addr, s->port_us, s->port_pc, s->spi_pc, ip_addr.af, IPSEC_POLICY_DIRECTION_IN);
 
     // Release SPIs
-    release_spi(s->spi_uc);
-    release_spi(s->spi_us);
+    release_spi(s->spi_pc);
+    release_spi(s->spi_ps);
+
+    // Release the client and the server ports
+    release_cport(s->port_pc);
+    release_sport(s->port_ps);
 
     close_mnl_socket(sock);
     return 0;
 }
 
-static void on_expire(struct pcontact *c, int type, void *param)
+void ipsec_on_expire(struct pcontact *c, int type, void *param)
 {
     if(type != PCSCF_CONTACT_EXPIRE && type != PCSCF_CONTACT_DELETE) {
         LM_ERR("Unexpected event type %d\n", type);
@@ -392,7 +445,7 @@ static void on_expire(struct pcontact *c, int type, void *param)
         return;
     }
 
-    destroy_ipsec_tunnel(c->received_host, c->received_proto, c->security_temp->data.ipsec);
+    destroy_ipsec_tunnel(c->received_host, c->security_temp->data.ipsec, c->contact_port);
 }
 
 int add_supported_secagree_header(struct sip_msg* m)
@@ -441,7 +494,7 @@ int add_security_server_header(struct sip_msg* m, ipsec_t* s)
     memset(sec_hdr_buf, 0, sizeof(sec_hdr_buf));
     sec_header->len = snprintf(sec_hdr_buf, sizeof(sec_hdr_buf) - 1,
                                 "Security-Server: ipsec-3gpp;prot=esp;mod=trans;spi-c=%d;spi-s=%d;port-c=%d;port-s=%d;alg=%.*s;ealg=%.*s\r\n",
-                                s->spi_pc, s->spi_ps, ipsec_client_port, ipsec_server_port,
+                                s->spi_pc, s->spi_ps, s->port_pc, s->port_ps,
                                 s->r_alg.len, s->r_alg.s,
                                 s->r_ealg.len, s->r_ealg.s
                               );
@@ -497,12 +550,6 @@ int ipsec_create(struct sip_msg* m, udomain_t* d)
         goto cleanup;
     }
 
-    ipsec_t* s = pcontact->security_temp->data.ipsec;
-
-    if(update_contact_ipsec_params(s, m) != 0) {
-        goto cleanup;
-    }
-
     // Get request from reply
     struct cell *t = tmb.t_gett();
     if (!t || t == (void*) -1) {
@@ -511,38 +558,83 @@ int ipsec_create(struct sip_msg* m, udomain_t* d)
     }
 
     struct sip_msg* req = t->uas.request;
-    ////
 
-    if(create_ipsec_tunnel(&req->rcv.src_ip, ci.received_proto, s) != 0) {
-        goto cleanup;
-    }
+    // Update contacts only for initial registration, for re-registration the existing contacts shouldn't be updated.
+    if(ci.via_port == SIP_PORT){
+        LM_DBG("Registration for contact with AOR [%.*s], VIA [%d://%.*s:%d], received_host [%d://%.*s:%d]\n",
+                ci.aor.len, ci.aor.s, ci.via_prot, ci.via_host.len, ci.via_host.s, ci.via_port,
+                ci.received_proto, ci.received_host.len, ci.received_host.s, ci.received_port);
 
-    // TODO: Save security_tmp to security!!!!!
+        ipsec_t* s = pcontact->security_temp->data.ipsec;
 
-    if (ul.update_pcontact(d, &ci, pcontact) != 0) {
-        LM_ERR("Error updating contact\n");
-        goto cleanup;
-    }
+        if(update_contact_ipsec_params(s, m) != 0) {
+            goto cleanup;
+        }
 
-    // Update temp security parameters
-    if(ul.update_temp_security(d, pcontact->security_temp->type, pcontact->security_temp, pcontact) != 0)
-    {
-        LM_ERR("Error updating temp security\n");
-    }
+        if(create_ipsec_tunnel(&req->rcv.src_ip, s) != 0){
+            goto cleanup;
+        }
 
-    // Destroy the tunnel, if the contact expires
-    if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, on_expire, NULL) != 1) {
-        LM_ERR("Error subscribing for contact\n");
-        goto cleanup;
-    }
+        if (ul.update_pcontact(d, &ci, pcontact) != 0){
+            LM_ERR("Error updating contact\n");
+            goto cleanup;
+        }
 
+        // Update temp security parameters
+        if(ul.update_temp_security(d, pcontact->security_temp->type, pcontact->security_temp, pcontact) != 0){
+            LM_ERR("Error updating temp security\n");
+        }
 
-    if(add_supported_secagree_header(m) != 0) {
-        goto cleanup;
-    }
+        if(add_supported_secagree_header(m) != 0) {
+            goto cleanup;
+        }
 
-    if(add_security_server_header(m, s) != 0) {
-        goto cleanup;
+        if(add_security_server_header(m, s) != 0) {
+            goto cleanup;
+        }
+
+        if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, ipsec_on_expire, (void*)&pcontact->received_port) != 1) {
+            LM_ERR("Error subscribing for contact\n");
+            goto cleanup;
+        }
+    }else{
+        LM_DBG("RE-Registration for contact with AOR [%.*s], VIA [%d://%.*s:%d], received_host [%d://%.*s:%d]\n",
+                ci.aor.len, ci.aor.s, ci.via_prot, ci.via_host.len, ci.via_host.s, ci.via_port,
+                ci.received_proto, ci.received_host.len, ci.received_host.s, ci.received_port);
+        
+        security_t* req_sec_params = NULL;
+
+        // Parse security parameters from the REGISTER request and get some data for the new tunnels
+        if((req_sec_params = cscf_get_security(req)) == NULL) {
+            LM_CRIT("No security parameters in REGISTER request\n");
+            goto cleanup;
+        }
+
+        if(update_contact_ipsec_params(req_sec_params->data.ipsec, m) != 0) {
+            goto cleanup;
+        }
+
+        if(create_ipsec_tunnel(&req->rcv.src_ip, req_sec_params->data.ipsec) != 0){
+            goto cleanup;
+        }
+
+        // if (ul.update_pcontact(d, &ci, pcontact) != 0){
+        //     LM_ERR("Error updating contact\n");
+        //     goto cleanup;
+        // }
+
+        // // Update temp security parameters
+        // if(ul.update_temp_security(d, req_sec_params->type, req_sec_params, pcontact) != 0){
+        //     LM_ERR("Error updating temp security\n");
+        // }
+
+        if(add_supported_secagree_header(m) != 0) {
+            goto cleanup;
+        }
+
+        if(add_security_server_header(m, req_sec_params->data.ipsec) != 0) {
+            goto cleanup;
+        }
     }
 
     ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
@@ -633,7 +725,7 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
         dst_proto = req->rcv.proto;
 
         // for Reply and TCP sends from P-CSCF server port, for Reply and UDP sends from P-CSCF client port
-        src_port = dst_proto == PROTO_TCP ? ipsec_server_port : ipsec_client_port;
+        src_port = dst_proto == PROTO_TCP ? s->port_ps : s->port_pc;
 
         // for Reply and TCP sends to UE client port, for Reply and UDP sends to UE server port
         dst_port = dst_proto == PROTO_TCP ? s->port_uc : s->port_us;
@@ -642,7 +734,7 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
         dst_proto = pcontact->received_proto;
 
         // for Request sends from P-CSCF client port
-        src_port = ipsec_client_port;
+        src_port = s->port_pc;
         
         // for Request sends to UE server port
         dst_port = s->port_us;
@@ -742,7 +834,7 @@ int ipsec_destroy(struct sip_msg* m, udomain_t* d)
         goto cleanup;
     }
 
-    destroy_ipsec_tunnel(ci.received_host, ci.received_proto, pcontact->security_temp->data.ipsec);
+    destroy_ipsec_tunnel(ci.received_host, pcontact->security_temp->data.ipsec, pcontact->contact_port);
 
     ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
 

+ 12 - 0
src/modules/ims_ipsec_pcscf/cmd.h

@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2012 Smile Communications, [email protected]
  * Copyright (C) 2012 Smile Communications, [email protected]
+
  *
  * The initial version of this code was written by Dragos Vingarzan
  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
@@ -46,6 +47,16 @@
 #ifndef IPSEC_CMD_H
 #define IPSEC_CMD_H
 
+typedef void (*contact_expired_t)(pcontact_t* c, int type, void* param);
+
+/*! ipsec pcscf API export structure */
+typedef struct ipsec_pcscf_api {
+    contact_expired_t ipsec_on_expire;
+} ipsec_pcscf_api_t;
+
+/*! ipsec pcscf API export bind function */
+typedef int (*bind_ipsec_pcscf_t)(ipsec_pcscf_api_t* api);
+
 struct sip_msg;
 struct udomain_t;
 
@@ -53,5 +64,6 @@ int ipsec_create(struct sip_msg* m, udomain_t* d);
 int ipsec_forward(struct sip_msg* m, udomain_t* d);
 int ipsec_destroy(struct sip_msg* m, udomain_t* d);
 int ipsec_cleanall();
+void ipsec_on_expire(pcontact_t* c, int type, void* param);
 
 #endif /* IPSEC_CMD_H */

+ 24 - 4
src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml

@@ -82,9 +82,9 @@ modparam("ims_ipsec_pcscf", "ipsec_listen_addr6", "")
     <section>
       <title><varname>ipsec_client_port</varname> (int)</title>
 
-      <para>Port number which will be bound for incoming (server) IPSec traffic.</para>
+      <para>Start port number which will be bound for incoming (server) IPSec traffic.</para>
 
-      <para><emphasis>Default value is 5963.</emphasis></para>
+      <para><emphasis>Default value is 5062.</emphasis></para>
 
       <example>
         <title><varname>ipsec_client_port</varname> parameter usage</title>
@@ -100,7 +100,7 @@ modparam("ims_ipsec_pcscf", "ipsec_client_port", 5062)
     <section>
       <title><varname>ipsec_server_port</varname> (int)</title>
 
-      <para>Port number which will be bound for incoming (server) IPSec traffic.</para>
+      <para>Start port number which will be bound for incoming (server) IPSec traffic.</para>
 
       <para><emphasis>Default value is 5063.</emphasis></para>
 
@@ -115,6 +115,26 @@ modparam("ims_ipsec_pcscf", "ipsec_server_port", 5063)
       </example>
     </section>
 
+    <section>
+      <title><varname>ipsec_max_connections</varname> (int)</title>
+
+      <para>Maximum IPSec connections for the process. E.g. if ipsec_client_port=5100, ipsec_server_port=6100 and
+      ipsec_max_connections=10, all client ports between 5100 and 5109 and all server ports between 6100 and 6109
+      will be used for maximum to 10 IPSec connections.</para>
+
+      <para><emphasis>Default value is 2.</emphasis></para>
+
+      <example>
+        <title><varname>ipsec_max_connections</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("ims_ipsec_pcscf", "ipsec_max_connections", 10)
+...
+        </programlisting>
+      </example>
+    </section>
+
     <section>
       <title><varname>ipsec_spi_id_start</varname> (int)</title>
 
@@ -227,7 +247,7 @@ ipsec_forward("location");
         </listitem>
 		  </itemizedlist>
       <example>
-        <title>ipsec_forward</title>
+        <title>ipsec_destroy</title>
 
         <programlisting format="linespecific">
 ...

+ 170 - 74
src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c

@@ -2,6 +2,7 @@
  * IMS IPSEC PCSCF module
  *
  * Copyright (C) 2018 Tsvetomir Dimitrov
+ * Copyright (C) 2019 Aleksandar Yosifov
  *
  * This file is part of Kamailio, a free SIP server.
  *
@@ -27,6 +28,7 @@
 
 #include "cmd.h"
 #include "spi_gen.h"
+#include "port_gen.h"
 
 
 MODULE_VERSION
@@ -39,6 +41,7 @@ str ipsec_listen_addr = STR_NULL;
 str ipsec_listen_addr6 = STR_NULL;
 int ipsec_client_port =  5062;
 int ipsec_server_port =  5063;
+int ipsec_max_connections = 2;
 int spi_id_start = 100;
 int spi_id_range = 1000;
 int xfrm_user_selector = 143956232;
@@ -55,6 +58,10 @@ static int w_destroy(struct sip_msg* _m, char* _d, char* _cflags);
 static int domain_fixup(void** param, int param_no);
 static int save_fixup2(void** param, int param_no);
 
+extern int bind_ipsec_pcscf(usrloc_api_t* api);
+
+int init_flag = 0;
+
 /*! \brief
  * Exported functions
  */
@@ -62,6 +69,7 @@ static cmd_export_t cmds[] = {
 	{"ipsec_create",  (cmd_function)w_create,  1, save_fixup2, 0, ONREPLY_ROUTE },
 	{"ipsec_forward", (cmd_function)w_forward, 1, save_fixup2, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
 	{"ipsec_destroy", (cmd_function)w_destroy, 1, save_fixup2, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
+    {"bind_ims_ipsec_pcscf", (cmd_function)bind_ipsec_pcscf, 1, 0, 0, 0},
 	{0, 0, 0, 0, 0, 0}
 };
 
@@ -69,12 +77,13 @@ static cmd_export_t cmds[] = {
  * Exported parameters
  */
 static param_export_t params[] = {
-	{"ipsec_listen_addr",  PARAM_STR, &ipsec_listen_addr   },
-	{"ipsec_listen_addr6",  PARAM_STR, &ipsec_listen_addr6   },
-	{"ipsec_client_port",	INT_PARAM, &ipsec_client_port   },
-	{"ipsec_server_port",	INT_PARAM, &ipsec_server_port   },
-	{"ipsec_spi_id_start",	INT_PARAM, &spi_id_start},
-	{"ipsec_spi_id_range",	INT_PARAM, &spi_id_range},
+	{"ipsec_listen_addr",		PARAM_STR, &ipsec_listen_addr		},
+	{"ipsec_listen_addr6",  	PARAM_STR, &ipsec_listen_addr6		},
+	{"ipsec_client_port",		INT_PARAM, &ipsec_client_port		},
+	{"ipsec_server_port",		INT_PARAM, &ipsec_server_port		},
+	{"ipsec_max_connections",	INT_PARAM, &ipsec_max_connections	},
+	{"ipsec_spi_id_start",		INT_PARAM, &spi_id_start			},
+	{"ipsec_spi_id_range",		INT_PARAM, &spi_id_range			},
 	{0, 0, 0}
 };
 
@@ -95,13 +104,152 @@ struct module_exports exports = {
 };
 
 
+static void ipsec_print_all_socket_lists()
+{
+	struct socket_info *si;
+	struct socket_info** list;
+	struct addr_info* ai;
+	unsigned short proto;
+
+    LM_INFO("Listening on:\n");
+
+	proto=PROTO_UDP;
+	do{
+		list=get_sock_info_list(proto);
+		for(si=list?*list:0; si; si=si->next){
+            char buf[1024];
+            int cnt=0;
+
+            memset(buf, 0, sizeof(buf));
+
+			if(si->addr_info_lst){
+                sprintf(buf, "%s: (%s", get_valid_proto_name(proto), si->address_str.s);
+                cnt = strlen(buf);
+
+				for(ai=si->addr_info_lst; ai; ai=ai->next){
+                    sprintf(buf + cnt, ", %s", ai->address_str.s);
+                    cnt = strlen(buf);
+				}
+
+                if(si->port_no_str.s){
+				    sprintf(buf + cnt, "):%s%s%s", si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : "");
+                }else{
+                    sprintf(buf + cnt, "):%u%s%s", si->port_no, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : "");
+                }
+                cnt = strlen(buf);
+			}else{
+				sprintf(buf, "%s: %s", get_valid_proto_name(proto), si->name.s);
+                cnt = strlen(buf);
+
+				if(!(si->flags & SI_IS_IP)){
+                    if(si->address_str.s){
+					    sprintf(buf + cnt, " [%s]", si->address_str.s);
+                        cnt = strlen(buf);
+                    }
+				}
+
+                if(si->port_no_str.s){
+				    sprintf(buf + cnt, ":%s%s%s", si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : "");
+                }else{
+                    sprintf(buf + cnt, ":%u%s%s", si->port_no, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : "");
+                }
+                cnt = strlen(buf);
+
+				if(si->useinfo.name.s){
+					printf(buf + cnt, " advertise %s:%d", si->useinfo.name.s, si->useinfo.port_no);
+                    cnt = strlen(buf);
+				}
+			}
+
+            LM_INFO("%s\n", buf);
+		}
+	}while((proto=next_proto(proto)));
+}
+
+static int ipsec_add_listen_ifaces()
+{
+    char addr4[128];
+	char addr6[128];
+    int i;
+
+    for(i = 0; i < ipsec_max_connections; ++i){
+        if(ipsec_listen_addr.len) {
+            if(ipsec_listen_addr.len > sizeof(addr4)-1) {
+                LM_ERR("Bad value for ipsec listen address IPv4: %.*s\n", ipsec_listen_addr.len, ipsec_listen_addr.s);
+                return -1;
+            }
+
+            memset(addr4, 0, sizeof(addr4));
+            memcpy(addr4, ipsec_listen_addr.s, ipsec_listen_addr.len);
+
+            //add listen interfaces for IPv4
+            if(add_listen_iface(addr4, NULL, ipsec_client_port + i, PROTO_TCP, 0) != 0) {
+                LM_ERR("Error adding listen ipsec client TCP interface for IPv4\n");
+                return -1;
+            }
+
+            if(add_listen_iface(addr4, NULL, ipsec_server_port + i, PROTO_TCP, 0) != 0) {
+                LM_ERR("Error adding listen ipsec server TCP interface for IPv4\n");
+                return -1;
+            }
+
+            if(add_listen_iface(addr4, NULL, ipsec_client_port + i, PROTO_UDP, 0) != 0) {
+                LM_ERR("Error adding listen ipsec client UDP interface for IPv4\n");
+                return -1;
+            }
+
+            if(add_listen_iface(addr4, NULL, ipsec_server_port + i, PROTO_UDP, 0) != 0) {
+                LM_ERR("Error adding listen ipsec server UDP interface for IPv4\n");
+                return -1;
+            }
+        }
+
+        if(ipsec_listen_addr6.len) {
+            if(ipsec_listen_addr6.len > sizeof(addr6)-1) {
+                LM_ERR("Bad value for ipsec listen address IPv6: %.*s\n", ipsec_listen_addr6.len, ipsec_listen_addr6.s);
+                return -1;
+            }
+
+            memset(addr6, 0, sizeof(addr6));
+            memcpy(addr6, ipsec_listen_addr6.s, ipsec_listen_addr6.len);
+
+            //add listen interfaces for IPv6
+            if(add_listen_iface(addr6, NULL, ipsec_client_port + i, PROTO_TCP, 0) != 0) {
+                LM_ERR("Error adding listen ipsec client TCP interface for IPv6\n");
+                return -1;
+            }
+
+            if(add_listen_iface(addr6, NULL, ipsec_server_port + i, PROTO_TCP, 0) != 0) {
+                LM_ERR("Error adding listen ipsec server TCP interface for IPv6\n");
+                return -1;
+            }
+
+            if(add_listen_iface(addr6, NULL, ipsec_client_port + i, PROTO_UDP, 0) != 0) {
+                LM_ERR("Error adding listen ipsec client UDP interface for IPv6\n");
+                return -1;
+            }
+
+            if(add_listen_iface(addr6, NULL, ipsec_server_port + i, PROTO_UDP, 0) != 0) {
+                LM_ERR("Error adding listen ipsec server UDP interface for IPv6\n");
+                return -1;
+            }
+        }
+    }
+
+    if(fix_all_socket_lists() != 0) {
+        LM_ERR("Error calling fix_all_socket_lists()\n");
+        return -1;
+    }
+
+    ipsec_print_all_socket_lists();
+
+    return 0;
+}
+
 /*! \brief
  * Initialize parent
  */
 static int mod_init(void) {
-    char addr4[128];
-	char addr6[128];
-
 	bind_usrloc_t bind_usrloc;
 
 	bind_usrloc = (bind_usrloc_t) find_export("ul_bind_ims_usrloc_pcscf", 1, 0);
@@ -122,70 +270,7 @@ static int mod_init(void) {
 	}
 	LM_INFO("Successfully bound to TM module\n");
 
-	if(ipsec_listen_addr.len) {
-		if(ipsec_listen_addr.len > sizeof(addr4)-1) {
-        	LM_ERR("Bad value for ipsec listen address IPv4: %.*s\n", ipsec_listen_addr.len, ipsec_listen_addr.s);
-        	return -1;
-    	}
-
-	    memset(addr4, 0, sizeof(addr4));
-	    memcpy(addr4, ipsec_listen_addr.s, ipsec_listen_addr.len);
-
-		//add listen interfaces for IPv4
-		if(add_listen_iface(addr4, NULL, ipsec_client_port, PROTO_TCP, 0) != 0) {
-			LM_ERR("Error adding listen ipsec client TCP interface for IPv4\n");
-			return -1;
-		}
-
-		if(add_listen_iface(addr4, NULL, ipsec_server_port, PROTO_TCP, 0) != 0) {
-			LM_ERR("Error adding listen ipsec server TCP interface for IPv4\n");
-			return -1;
-		}
-
-		if(add_listen_iface(addr4, NULL, ipsec_client_port, PROTO_UDP, 0) != 0) {
-			LM_ERR("Error adding listen ipsec client UDP interface for IPv4\n");
-			return -1;
-		}
-
-		if(add_listen_iface(addr4, NULL, ipsec_server_port, PROTO_UDP, 0) != 0) {
-			LM_ERR("Error adding listen ipsec server UDP interface for IPv4\n");
-			return -1;
-		}
-	}
-
-	if(ipsec_listen_addr6.len) {
-		if(ipsec_listen_addr6.len > sizeof(addr6)-1) {
-			LM_ERR("Bad value for ipsec listen address IPv6: %.*s\n", ipsec_listen_addr6.len, ipsec_listen_addr6.s);
-        	return -1;
-		}
-
-		memset(addr6, 0, sizeof(addr6));
-    	memcpy(addr6, ipsec_listen_addr6.s, ipsec_listen_addr6.len);
-
-		//add listen interfaces for IPv6
-		if(add_listen_iface(addr6, NULL, ipsec_client_port, PROTO_TCP, 0) != 0) {
-			LM_ERR("Error adding listen ipsec client TCP interface for IPv6\n");
-			return -1;
-		}
-
-		if(add_listen_iface(addr6, NULL, ipsec_server_port, PROTO_TCP, 0) != 0) {
-			LM_ERR("Error adding listen ipsec server TCP interface for IPv6\n");
-			return -1;
-		}
-
-		if(add_listen_iface(addr6, NULL, ipsec_client_port, PROTO_UDP, 0) != 0) {
-			LM_ERR("Error adding listen ipsec client UDP interface for IPv6\n");
-			return -1;
-		}
-
-		if(add_listen_iface(addr6, NULL, ipsec_server_port, PROTO_UDP, 0) != 0) {
-			LM_ERR("Error adding listen ipsec server UDP interface for IPv6\n");
-			return -1;
-		}
-	}
-
-    if(fix_all_socket_lists() != 0) {
-        LM_ERR("Error calling fix_all_socket_lists() during module initialisation\n");
+	if(ipsec_add_listen_ifaces() != 0){
         return -1;
     }
 
@@ -195,11 +280,18 @@ static int mod_init(void) {
 	}
 
     int res = 0;
-    if((res = init_spi_gen(spi_id_start, spi_id_start + spi_id_range)) != 0) {
+    if((res = init_spi_gen(spi_id_start, spi_id_range)) != 0) {
         LM_ERR("Error initialising spi generator. Error: %d\n", res);
         return -1;
     }
 
+    if((res = init_port_gen(ipsec_server_port, ipsec_client_port, ipsec_max_connections)) != 0) {
+        LM_ERR("Error initialising port generator. Error: %d\n", res);
+        return -1;
+    }
+
+    init_flag = 1;
+
 	return 0;
 }
 
@@ -212,6 +304,10 @@ static void mod_destroy(void)
     if(destroy_spi_gen() != 0) {
         LM_ERR("Error destroying spi generator\n");
     }
+
+    if(destroy_port_gen() != 0){
+        LM_ERR("Error destroying port generator\n");
+    }
 }
 
 static int child_init(int rank)

+ 105 - 61
src/modules/ims_ipsec_pcscf/ipsec.c

@@ -27,14 +27,13 @@
 #include "../../core/dprint.h"
 #include "../../core/mem/pkg.h"
 #include "../../core/ip_addr.h"
+#include "../../core/resolve.h"
 
 #include <errno.h>
 #include <arpa/inet.h>
 #include <libmnl/libmnl.h>
 #include <linux/xfrm.h>
 #include <time.h>
-//#include <stdio.h>
-//#include <string.h>
 
 
 #define XFRM_TMPLS_BUF_SIZE 1024
@@ -43,8 +42,6 @@
 
 
 extern int xfrm_user_selector;
-extern int spi_id_start;
-extern int spi_id_range;
 
 struct xfrm_buffer {
     char buf[NLMSG_DELETEALL_BUF_SIZE];
@@ -91,25 +88,25 @@ static void string_to_key(char* dst, const str key_string)
 }
 
 // Converts the protocol enum used in Kamailio to the constants used in Linux
-unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto)
-{
-    switch(kamailio_proto) {
-        case PROTO_UDP:
-            return IPPROTO_UDP;
-        case PROTO_TCP:
-            return IPPROTO_TCP;
-        case PROTO_NONE:
-        case PROTO_TLS:
-        case PROTO_SCTP:
-	    case PROTO_WS:
-        case PROTO_WSS:
-        case PROTO_OTHER:
-        default:
-            return IPPROTO_MAX;
-    };
-}
-
-int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int s_port, int d_port, int long id, str ck, str ik, str r_alg)
+// unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto)
+// {
+//     switch(kamailio_proto) {
+//         case PROTO_UDP:
+//             return IPPROTO_UDP;
+//         case PROTO_TCP:
+//             return IPPROTO_TCP;
+//         case PROTO_NONE:
+//         case PROTO_TLS:
+//         case PROTO_SCTP:
+// 	    case PROTO_WS:
+//         case PROTO_WSS:
+//         case PROTO_OTHER:
+//         default:
+//             return IPPROTO_MAX;
+//     };
+// }
+
+int add_sa(struct mnl_socket* nl_sock, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int s_port, int d_port, int long id, str ck, str ik, str r_alg)
 {
     char l_msg_buf[MNL_SOCKET_BUFFER_SIZE];
     char l_auth_algo_buf[XFRM_TMPLS_BUF_SIZE];
@@ -125,11 +122,11 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_add
     memset(l_auth_algo_buf, 0, sizeof(l_auth_algo_buf));
     memset(l_enc_algo_buf, 0, sizeof(l_enc_algo_buf));
 
-    unsigned sel_proto = 0;
-    if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) {
-        LM_ERR("Invalid port was passed to the function: %d\n", proto);
-        return -1;
-    }
+    // unsigned sel_proto = 0;
+    // if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) {
+    //     LM_ERR("Invalid port was passed to the function: %d\n", proto);
+    //     return -1;
+    // }
 
     // nlmsghdr initialization
     l_nlh = mnl_nlmsg_put_header(l_msg_buf);
@@ -220,7 +217,7 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_add
 }
 
 
-int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id)
+int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id, unsigned int af)
 {
     char* src_addr = NULL;
     char* dest_addr = NULL;
@@ -243,26 +240,47 @@ int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_para
     memcpy(src_addr, src_addr_param.s, src_addr_param.len);
     memcpy(dest_addr, dest_addr_param.s, dest_addr_param.len);
 
-
     struct {
         struct nlmsghdr n;
         struct xfrm_usersa_id   xsid;
         char buf[XFRM_TMPLS_BUF_SIZE];
 
     } req = {
-        .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)),
-        .n.nlmsg_flags = NLM_F_REQUEST,
-        .n.nlmsg_type = XFRM_MSG_DELSA,
-        .xsid.spi = htonl(id),
-        .xsid.family = AF_INET,
-        .xsid.proto = IPPROTO_ESP,
-        .xsid.daddr.a4 = inet_addr(dest_addr)
+        .n.nlmsg_len    = NLMSG_LENGTH(sizeof(req.xsid)),
+        .n.nlmsg_flags  = NLM_F_REQUEST,
+        .n.nlmsg_type   = XFRM_MSG_DELSA,
+        .n.nlmsg_pid    = id,
+        .xsid.spi       = htonl(id),
+        .xsid.family    = af,
+        .xsid.proto     = IPPROTO_ESP
     };
 
-    // SADDR
     xfrm_address_t saddr;
     memset(&saddr, 0, sizeof(saddr));
-    saddr.a4 = inet_addr(src_addr);
+
+    if(af == AF_INET6){
+        ip_addr_t ip_addr;
+
+        if(str2ipxbuf(&dest_addr_param, &ip_addr) < 0){
+            LM_ERR("Unable to convert dest address [%.*s]\n", dest_addr_param.len, dest_addr_param.s);
+            pkg_free(src_addr);
+            pkg_free(dest_addr);
+            return -1;
+        }
+        memcpy(req.xsid.daddr.a6, ip_addr.u.addr32, sizeof(req.xsid.daddr.a6));
+
+        memset(&ip_addr, 0, sizeof(ip_addr_t));
+        if(str2ipxbuf(&src_addr_param, &ip_addr) < 0){
+            LM_ERR("Unable to convert src address [%.*s]\n", src_addr_param.len, src_addr_param.s);
+            pkg_free(src_addr);
+            pkg_free(dest_addr);
+            return -1;
+        }
+        memcpy(saddr.a6, ip_addr.u.addr32, sizeof(saddr.a6));
+    }else{
+        req.xsid.daddr.a4   = inet_addr(dest_addr);
+        saddr.a4            = inet_addr(src_addr);
+    }
 
     mnl_attr_put(&req.n, XFRMA_SRCADDR, sizeof(saddr), (void *)&saddr);
 
@@ -281,18 +299,18 @@ int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_para
 }
 
 
-int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir)
+int add_policy(struct mnl_socket* mnl_socket, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir)
 {
     char                            l_msg_buf[MNL_SOCKET_BUFFER_SIZE];
     char                            l_tmpls_buf[XFRM_TMPLS_BUF_SIZE];
     struct nlmsghdr*                l_nlh;
     struct xfrm_userpolicy_info*    l_xpinfo;
 
-    unsigned sel_proto = 0;
-    if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) {
-        LM_ERR("Invalid port was passed to the function: %d\n", proto);
-        return -1;
-    }
+    // unsigned sel_proto = 0;
+    // if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) {
+    //     LM_ERR("Invalid port was passed to the function: %d\n", proto);
+    //     return -1;
+    // }
 
     memset(l_msg_buf, 0, sizeof(l_msg_buf));
     memset(l_tmpls_buf, 0, sizeof(l_tmpls_buf));
@@ -375,14 +393,13 @@ int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, const struct
     return 0;
 }
 
-int remove_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir)
+int remove_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, unsigned int af, enum ipsec_policy_direction dir)
 {
-    unsigned sel_proto = 0;
-    if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) {
-        LM_ERR("Invalid port was passed to the function: %d\n", proto);
-        return -1;
-    }
-
+    // unsigned sel_proto = 0;
+    // if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) {
+    //     LM_ERR("Invalid port was passed to the function: %d\n", proto);
+    //     return -1;
+    // }
     unsigned char policy_dir = 0;
 
     if(dir == IPSEC_POLICY_DIRECTION_IN) {
@@ -392,7 +409,7 @@ int remove_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_a
          policy_dir = XFRM_POLICY_OUT;
     }
     else {
-        LM_ERR("Invalid direction parameter passed to add_policy: %d\n", dir);
+        LM_ERR("Invalid direction parameter passed to remove_policy: %d\n", dir);
         return -1;
     }
 
@@ -422,22 +439,49 @@ int remove_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_a
         struct xfrm_userpolicy_id xpid;
         char buf[XFRM_TMPLS_BUF_SIZE];
     } req = {
-        .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)),
-        .n.nlmsg_flags = NLM_F_REQUEST,
-        .n.nlmsg_type = XFRM_MSG_DELPOLICY,
+        .n.nlmsg_len            = NLMSG_LENGTH(sizeof(req.xpid)),
+        .n.nlmsg_flags          = NLM_F_REQUEST,
+        .n.nlmsg_type           = XFRM_MSG_DELPOLICY,
+        .n.nlmsg_pid            = p_id,
         .xpid.dir               = policy_dir,
-        .xpid.sel.family        = AF_INET,
-        .xpid.sel.daddr.a4      = inet_addr(dest_addr),
-        .xpid.sel.saddr.a4      = inet_addr(src_addr),
+        .xpid.sel.family        = af,
         .xpid.sel.dport         = htons(dst_port),
         .xpid.sel.dport_mask    = 0xFFFF,
-        .xpid.sel.prefixlen_d   = 32,
         .xpid.sel.sport         = htons(src_port),
         .xpid.sel.sport_mask    = 0xFFFF,
-        .xpid.sel.prefixlen_s   = 32//,
+        .xpid.sel.user          = htonl(xfrm_user_selector)
         //.xpid.sel.proto         = sel_proto
     };
 
+    if(af == AF_INET6){
+        ip_addr_t ip_addr;
+
+        if(str2ipxbuf(&dest_addr_param, &ip_addr) < 0){
+            LM_ERR("Unable to convert dest address [%.*s]\n", dest_addr_param.len, dest_addr_param.s);
+            pkg_free(src_addr);
+            pkg_free(dest_addr);
+            return -1;
+        }
+        memcpy(req.xpid.sel.daddr.a6, ip_addr.u.addr32, sizeof(req.xpid.sel.daddr.a6));
+
+        if(str2ipxbuf(&src_addr_param, &ip_addr) < 0){
+            LM_ERR("Unable to convert src address [%.*s]\n", src_addr_param.len, src_addr_param.s);
+            pkg_free(src_addr);
+            pkg_free(dest_addr);
+            return -1;
+        }
+        memcpy(req.xpid.sel.saddr.a6, ip_addr.u.addr32, sizeof(req.xpid.sel.saddr.a6));
+
+        req.xpid.sel.prefixlen_d = 128;
+        req.xpid.sel.prefixlen_s = 128;
+    }else{
+        req.xpid.sel.daddr.a4       = inet_addr(dest_addr);
+        req.xpid.sel.saddr.a4       = inet_addr(src_addr);
+
+        req.xpid.sel.prefixlen_d    = 32;
+        req.xpid.sel.prefixlen_s    = 32;
+    }
+
     if(mnl_socket_sendto(mnl_socket, &req.n, req.n.nlmsg_len) < 0)
     {
         LM_ERR("Failed to send Netlink message, error: %s\n", strerror(errno));

+ 5 - 6
src/modules/ims_ipsec_pcscf/ipsec.h

@@ -39,14 +39,13 @@ enum ipsec_policy_direction {
 
 struct mnl_socket* init_mnl_socket();
 void close_mnl_socket(struct mnl_socket* sock);
-unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto);
+//unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto);
 
+int add_sa(struct mnl_socket* nl_sock, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int s_port, int d_port, int long id, str ck, str ik, str r_alg);
+int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id, unsigned int af);
 
-int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int s_port, int d_port, int long id, str ck, str ik, str r_alg);
-int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id);
-
-int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir);
-int remove_policy(struct mnl_socket* mnl_socket,  unsigned short proto, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir);
+int add_policy(struct mnl_socket* mnl_socket, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir);
+int remove_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, unsigned int af, enum ipsec_policy_direction dir);
 
 int clean_sa(struct mnl_socket*  mnl_socket);
 int clean_policy(struct mnl_socket*  mnl_socket);

+ 145 - 0
src/modules/ims_ipsec_pcscf/port_gen.c

@@ -0,0 +1,145 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ * Copyright (C) 2019 Aleksandar Yosifov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "spi_gen.h"
+#include "spi_list.h"
+#include <pthread.h>
+
+pthread_mutex_t sport_mut;  // server port mutex
+pthread_mutex_t cport_mut;  // client port mutex
+spi_list_t used_sports;     // list with used server ports
+spi_list_t used_cports;     // list with used client ports
+uint32_t sport_val;         // the last acquired server port
+uint32_t cport_val;         // the last acquired client port
+uint32_t min_sport;
+uint32_t min_cport;
+uint32_t max_sport;
+uint32_t max_cport;
+
+int init_port_gen(uint32_t sport_start_val, uint32_t cport_start_val, uint32_t range)
+{
+    if(sport_start_val < 1 || cport_start_val < 1){
+        return 1;
+    }
+
+    if((UINT32_MAX - range < sport_start_val) || (UINT32_MAX - range < cport_start_val)){
+        return 2;
+    }
+
+    if(pthread_mutex_init(&sport_mut, NULL) || pthread_mutex_init(&cport_mut, NULL)){
+        return 3;
+    }
+
+    used_sports = create_list();
+    used_cports = create_list();
+
+    sport_val = min_sport = sport_start_val;
+    cport_val = min_cport = cport_start_val;
+    max_sport = sport_start_val + range;
+    max_cport = cport_start_val + range;
+
+    return 0;
+}
+
+uint32_t acquire_port(spi_list_t* used_ports, pthread_mutex_t* port_mut, uint32_t* port_val, uint32_t min_port, uint32_t max_port)
+{
+    //save the initial value for the highly unlikely case where there are no free server PORTs
+    uint32_t initial_val = *port_val;
+    uint32_t ret = 0; // by default return invalid port
+
+    if(pthread_mutex_lock(port_mut) != 0) {
+        return ret;
+    }
+
+    while(1){
+        if(spi_in_list(used_ports, *port_val) == 0) {
+            ret = *port_val;
+            (*port_val)++;
+            break;
+        }
+
+        (*port_val)++; //the current server port is not available - increment
+
+        if(*port_val >= max_port) { //reached the top of the range - reset
+            *port_val = min_port;
+        }
+
+        if(*port_val == initial_val) { //there are no free server ports
+            pthread_mutex_unlock(port_mut);
+            return ret;
+        }
+    }
+
+    // found unused server port - add it to the used list
+    if(spi_add(used_ports, ret) != 0) {
+        ret = 0;
+    }
+
+    pthread_mutex_unlock(port_mut);
+    return ret;
+}
+
+uint32_t acquire_sport()
+{
+    return acquire_port(&used_sports, &sport_mut, &sport_val, min_sport, max_sport);
+}
+
+uint32_t acquire_cport()
+{
+    return acquire_port(&used_cports, &cport_mut, &cport_val, min_cport, max_cport);
+}
+
+int release_sport(uint32_t port)
+{
+    if(pthread_mutex_lock(&sport_mut) != 0){
+        return 1;
+    }
+
+    spi_remove(&used_sports, port);
+
+    pthread_mutex_unlock(&sport_mut);
+    return 0;
+}
+
+int release_cport(uint32_t port)
+{
+    if(pthread_mutex_lock(&cport_mut) != 0){
+        return 1;
+    }
+
+    spi_remove(&used_cports, port);
+
+    pthread_mutex_unlock(&cport_mut);
+    return 0;
+}
+
+int destroy_port_gen()
+{
+    int ret = pthread_mutex_destroy(&sport_mut);
+    if(ret != 0){
+        return ret;
+    }
+
+    return pthread_mutex_destroy(&cport_mut);
+}

+ 40 - 0
src/modules/ims_ipsec_pcscf/port_gen.h

@@ -0,0 +1,40 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ * Copyright (C) 2019 Aleksandar Yosifov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _SPI_GEN_H_
+
+#include <stdint.h>
+
+//
+// PORT GEN is based on SPI list, because the logics of the SPI gen and PORT gen are basically the same.
+// It is used as an unique port generator for the TCP client and server ports.
+
+int init_port_gen(uint32_t sport_start_val, uint32_t cport_start_val, uint32_t range);
+int destroy_port_gen();
+uint32_t acquire_sport(); // acquare server port
+uint32_t acquire_cport(); // acquare client port
+int release_sport(uint32_t port); // release server port
+int release_cport(uint32_t port); // release client port
+
+#endif /*  _SPI_GEN_H_ */

+ 264 - 0
src/modules/ims_ipsec_pcscf/sec_agree.c

@@ -0,0 +1,264 @@
+/**
+ * Copyright (C) 2017 kamailio.org
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "sec_agree.h"
+
+#include "../../core/str.h"
+#include "../../core/parser/msg_parser.h"
+#include "../../core/mem/mem.h"
+
+static uint32_t parse_digits(str value)
+{
+    uint32_t ret = 0;
+
+    int buf_len = value.len+1;
+    char* buf = (char*)malloc(buf_len);
+
+    if(!buf) {
+        return ret;
+    }
+
+    memset(buf, 0, buf_len);
+    memcpy(buf, value.s, value.len);
+
+    ret = atoll(buf);
+
+    free(buf);
+
+    return ret;
+}
+
+static void trim_whitespaces(str* string) {
+    // skip leading whitespace
+    while(string->len && (string->s[0]==' ' || string->s[0]=='\t' || string->s[0]=='<')){
+        string->s = string->s + 1;
+        string->len --;
+    }
+
+    // skip trailing whitespace
+    while(string->len && (string->s[string->len-1]==' ' || string->s[string->len-1]=='\t')){
+        string->len--;
+    }
+}
+
+#define SEC_COPY_STR_PARAM(DST, SRC)\
+        DST.s = shm_malloc(SRC.len);\
+        if(DST.s == NULL) {\
+            return -1;\
+        }\
+        memcpy(DST.s, SRC.s, SRC.len);\
+        DST.len = SRC.len;
+
+
+static int process_sec_agree_param(str name, str value, ipsec_t *ret)
+{
+    trim_whitespaces(&name);
+    trim_whitespaces(&value);
+
+    if(strncasecmp(name.s, "alg", name.len) == 0) {
+        SEC_COPY_STR_PARAM(ret->r_alg, value);
+    }
+    else if(strncasecmp(name.s, "prot", name.len) == 0) {
+        SEC_COPY_STR_PARAM(ret->prot, value);
+    }
+    else if(strncasecmp(name.s, "mod", name.len) == 0) {
+        SEC_COPY_STR_PARAM(ret->mod, value);
+    }
+    else if(strncasecmp(name.s, "ealg", name.len) == 0) {
+        SEC_COPY_STR_PARAM(ret->r_ealg, value);
+    }
+    else if(strncasecmp(name.s, "spi-c", name.len) == 0) {
+        ret->spi_uc = parse_digits(value);
+    }
+    else if(strncasecmp(name.s, "spi-s", name.len) == 0) {
+        ret->spi_us = parse_digits(value);
+    }
+    else if(strncasecmp(name.s, "port-c", name.len) == 0) {
+        ret->port_uc = parse_digits(value);
+    }
+    else if(strncasecmp(name.s, "port-s", name.len) == 0) {
+        ret->port_us = parse_digits(value);
+    }
+    else {
+        //unknown parameter
+    }
+
+    return 0;
+}
+
+static security_t* parse_sec_agree(struct hdr_field* h)
+{
+    int i = 0;
+
+    str name = {0,0};
+    str value = {0,0};
+    str mechanism_name = {0,0};
+    security_t* params = NULL;
+    str body = h->body;
+
+    trim_whitespaces(&body);
+
+    // find mechanism name end
+    for(i = 0; body.s[i] != ';' && i < body.len; i++);
+
+    mechanism_name.s = body.s;
+    mechanism_name.len = i;
+
+    if(strncasecmp(mechanism_name.s, "ipsec-3gpp", 10) != 0) {
+        // unsupported mechanism
+        LM_ERR("Unsupported mechanism: %.*s\n", STR_FMT(&mechanism_name));
+        goto cleanup;
+    }
+
+    // allocate shm memory for security_t (it will be saved in contact)
+    if ((params = shm_malloc(sizeof(security_t))) == NULL) {
+        LM_ERR("Error allocating shm memory for security_t parameters during sec-agree parsing\n");
+        return NULL;
+    }
+    memset(params, 0, sizeof(security_t));
+
+    if((params->sec_header.s = shm_malloc(h->name.len)) == NULL) {
+        LM_ERR("Error allocating shm memory for security_t sec_header parameter during sec-agree parsing\n");
+        goto cleanup;
+    }
+    memcpy(params->sec_header.s, h->name.s, h->name.len);
+    params->sec_header.len = h->name.len;
+
+    // allocate memory for ipsec_t in security_t
+    params->data.ipsec = shm_malloc(sizeof(ipsec_t));
+    if(!params->data.ipsec) {
+        LM_ERR("Error allocating memory for ipsec parameters during sec-agree parsing\n");
+        goto cleanup;
+    }
+    memset(params->data.ipsec, 0, sizeof(ipsec_t));
+
+
+    // set security type to IPSEC
+    params->type = SECURITY_IPSEC;
+
+    body.s=body.s+i+1;
+    body.len=body.len-i-1;
+
+    // get the rest of the parameters
+    i = 0;
+    while(i <= body.len) {
+        //look for end of buffer or parameter separator
+        if(i == body.len || body.s[i] == ';' ) {
+            if(name.len) {
+                // if(name.len) => a param name is parsed
+                // and now i points to the end of its value
+                value.s = body.s;
+                value.len = i;
+            }
+            //else - name is not read but there is a value
+            //so there is some error - skip ahead
+            body.s=body.s+i+1;
+            body.len=body.len-i-1;
+
+            i=0;
+
+            if(name.len && value.len) {
+                if(process_sec_agree_param(name, value, params->data.ipsec)) {
+                    goto cleanup;
+                }
+            }
+            //else - something's wrong. Ignore!
+
+            //processing is done - reset
+            name.len=0;
+            value.len=0;
+        }
+        //look for param=value separator
+        else if(body.s[i] == '=') {
+            name.s = body.s;
+            name.len = i;
+
+            //position saved - skip ahead
+            body.s=body.s+i+1;
+            body.len=body.len-i-1;
+
+            i=0;
+        }
+        //nothing interesting - move on
+        else {
+            i++;
+        }
+    }
+
+    return params;
+
+cleanup:
+    // The same piece of code also lives in modules/ims_usrloc_pcscf/pcontact.c
+    // Function - free_security()
+    // Keep them in sync!
+    if (params) {
+        shm_free(params->sec_header.s);
+
+        if(params->type == SECURITY_IPSEC && params->data.ipsec) {
+            shm_free(params->data.ipsec->ealg.s);
+            shm_free(params->data.ipsec->r_ealg.s);
+            shm_free(params->data.ipsec->ck.s);
+            shm_free(params->data.ipsec->alg.s);
+            shm_free(params->data.ipsec->r_alg.s);
+            shm_free(params->data.ipsec->ik.s);
+            shm_free(params->data.ipsec->prot.s);
+            shm_free(params->data.ipsec->mod.s);
+            shm_free(params->data.ipsec);
+        }
+
+        shm_free(params);
+    }
+
+    return NULL;
+}
+
+static str s_security_client={"Security-Client",15};
+/**
+ * Looks for the Security-Client header
+ * @param msg - the sip message
+ * @param params - ptr to struct sec_agree_params, where parsed values will be saved
+ * @returns 0 on success, error code on failure
+ */
+security_t* cscf_get_security(struct sip_msg *msg)
+{
+    struct hdr_field *h = NULL;
+
+    if (!msg) return NULL;
+
+    if (parse_headers(msg, HDR_EOH_F, 0)<0) {
+        return NULL;
+    }
+
+    h = msg->headers;
+    while(h)
+    {
+        if (h->name.len == s_security_client.len && strncasecmp(h->name.s, s_security_client.s, s_security_client.len)==0)
+        {
+            return parse_sec_agree(h);
+        }
+
+        h = h->next;
+    }
+
+    LM_INFO("No security parameters found\n");
+
+    return NULL;
+}

+ 35 - 0
src/modules/ims_ipsec_pcscf/sec_agree.h

@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2017 kamailio.org
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef SEC_AGREE_H
+#define SEC_AGREE_H
+
+#include "../ims_usrloc_pcscf/usrloc.h"
+
+/**
+ * Looks for the Security-Client header
+ * @param msg - the sip message
+ * @param params - ptr to struct sec_agree_params, where parsed values will be saved
+ * @returns 0 on success, error code on failure
+ */
+security_t* cscf_get_security(struct sip_msg *msg);
+
+#endif // SEC_AGREE_H

+ 2 - 1
src/modules/ims_ipsec_pcscf/spi_gen.c

@@ -76,7 +76,8 @@ uint32_t acquire_spi()
         }
 
         if(spi_val == initial_val) { //there are no free SPIs
-            break;
+            pthread_mutex_unlock(&spis_mut);
+            return ret;
         }
 
     }

+ 2 - 0
src/modules/ims_ipsec_pcscf/spi_list.c

@@ -150,6 +150,8 @@ int spi_in_list(spi_list_t* list, uint32_t id)
     while(n) {
         if (n->id == id)
             return 1;
+        
+        n = n->next;
     }
 
     return 0;

+ 27 - 5
src/modules/ims_ipsec_pcscf/spi_list_tests.c

@@ -300,16 +300,38 @@ void case13() //No duplicates
 void case14()
 {
     spi_list_t list = create_list();
-    spi_add(&list, 1);
     spi_add(&list, 2);
+    spi_add(&list, 3);
+    spi_add(&list, 5);
+    spi_add(&list, 6);
+
+    if(spi_in_list(&list, 1) != 0) {
+        printf("%s: failed. 1 is not in list, but spi_in_list() returns true.\n", __func__);
+        return;
+    }
+
+    if(spi_in_list(&list, 4) != 0) {
+        printf("%s: failed. 4 is not in list, but spi_in_list() returns true.\n", __func__);
+        return;
+    }
+
+    if(spi_in_list(&list, 7) != 0) {
+        printf("%s: failed. 7 is not in list, but spi_in_list() returns true.\n", __func__);
+        return;
+    }
+
+    if(spi_in_list(&list, 2) != 1) {
+        printf("%s: failed. 2 is in list, but spi_in_list() returns false.\n", __func__);
+        return;
+    }
 
-    if(spi_in_list(&list, 1) != 1) {
-        printf("%s: failed. 1 is in list, but spi_in_list() returns false.\n", __func__);
+    if(spi_in_list(&list, 3) != 1) {
+        printf("%s: failed. 3 is in list, but spi_in_list() returns false.\n", __func__);
         return;
     }
 
-    if(spi_in_list(&list, 3) != 0) {
-        printf("%s: failed. 3 is not in list, but spi_in_list() returns true.\n", __func__);
+    if(spi_in_list(&list, 6) != 1) {
+        printf("%s: failed. 6 is in list, but spi_in_list() returns false.\n", __func__);
         return;
     }
 

+ 15 - 0
src/modules/ims_registrar_pcscf/ims_registrar_pcscf_mod.c

@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2012 Smile Communications, [email protected]
  * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2019 Aleksandar Yosifov
  *
  * The initial version of this code was written by Dragos Vingarzan
  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
@@ -57,6 +58,7 @@
 #include "../../modules/sl/sl.h"
 #include "../../core/mod_fix.h"
 #include "../../core/cfg/cfg_struct.h"
+#include "../ims_ipsec_pcscf/cmd.h"
 
 /* Bindings to PUA */
 #include "../pua/pua_bind.h"
@@ -73,6 +75,7 @@ usrloc_api_t ul;						/**!< Structure containing pointers to usrloc functions*/
 sl_api_t slb;							/**!< SL API structure */
 struct tm_binds tmb;					/**!< TM API structure */
 pua_api_t pua; 							/**!< PUA API structure */
+ipsec_pcscf_api_t ipsec_pcscf;			/**!< Structure containing pointers to ipsec pcscf functions*/
 
 int publish_reginfo = 0;
 int subscribe_to_reginfo = 0;
@@ -228,6 +231,7 @@ int fix_parameters() {
 static int mod_init(void) {
 	bind_usrloc_t bind_usrloc;
 	bind_pua_t bind_pua;
+	bind_ipsec_pcscf_t bind_ipsec_pcscf;
 
 	/*register space for event processor*/
 	register_procs(1);
@@ -259,6 +263,17 @@ static int mod_init(void) {
 	}
 	LM_DBG("Successfully bound to PCSCF Usrloc module\n");
 
+	bind_ipsec_pcscf = (bind_ipsec_pcscf_t) find_export("bind_ims_ipsec_pcscf", 1, 0);
+	if (!bind_ipsec_pcscf) {
+		LM_ERR("can't bind ims_ipsec_pcscf\n");
+		return -1;
+	}
+
+	if (bind_ipsec_pcscf(&ipsec_pcscf) < 0) {
+		return -1;
+	}
+	LM_INFO("Successfully bound to PCSCF IPSEC module\n");
+
        if(subscribe_to_reginfo == 1){
                /* Bind to PUA: */
                bind_pua = (bind_pua_t) find_export("bind_pua", 1, 0);

+ 29 - 13
src/modules/ims_registrar_pcscf/save.c

@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2012 Smile Communications, [email protected]
  * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2019 Aleksandar Yosifov
  * 
  * The initial version of this code was written by Dragos Vingarzan
  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
@@ -50,6 +51,7 @@
 #include "subscribe.h"
 
 #include "../pua/pua_bind.h"
+#include "../ims_ipsec_pcscf/cmd.h"
 #include "sec_agree.h"
 
 extern struct tm_binds tmb;
@@ -59,6 +61,7 @@ extern unsigned int pending_reg_expires;
 extern int subscribe_to_reginfo;
 extern int subscription_expires;
 extern pua_api_t pua;
+extern ipsec_pcscf_api_t ipsec_pcscf;
 
 struct sip_msg* get_request_from_reply(struct sip_msg* reply)
 {
@@ -228,7 +231,17 @@ static inline int update_contacts(struct sip_msg *req,struct sip_msg *rpl, udoma
 								expires-local_time_now);
 						ci.reg_state = PCONTACT_REGISTERED;
 						if (ul.update_pcontact(_d, &ci, pcontact) != 0) {
-							LM_ERR("failed to update pcscf contact\n");
+							LM_DBG("failed to update pcscf contact\n");
+						}else{
+							// Register callback to destroy related tunnels to this contact.
+							// The registration should be exact here, after the successfuly registration of the UE
+							LM_DBG("ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE...)\n");
+							if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, ipsec_pcscf.ipsec_on_expire, NULL) != 1){
+								LM_DBG("Error subscribing for contact\n");
+							}
+
+							// After successful registration try to unregister all callbacks for pending contacts ralated to this contact.
+							ul.unreg_pending_contacts_cb(_d, pcontact, PCSCF_CONTACT_EXPIRE);
 						}
 						pcontact->expires = expires;
 					}
@@ -337,21 +350,25 @@ int save_pending(struct sip_msg* _m, udomain_t* _d) {
     // Parse security parameters
     security_t* sec_params = NULL;
     if((sec_params = cscf_get_security(_m)) == NULL) {
-        LM_ERR("Will save pending contact without security parameters\n");
+        LM_DBG("Will save pending contact without security parameters\n");
     }
 
 	// Parse security-verify parameters
     security_t* sec_verify_params = NULL;
     if((sec_verify_params = cscf_get_security_verify(_m)) == NULL){
-        LM_ERR("Will save pending contact without security-verify parameters\n");
+        LM_DBG("Will save pending contact without security-verify parameters\n");
     }else{
 		if(sec_params){
 			// for REGISTER request try to set spi pc and spi ps from security-verify header
 			sec_params->data.ipsec->spi_ps = sec_verify_params->data.ipsec->spi_us;
 			sec_params->data.ipsec->spi_pc = sec_verify_params->data.ipsec->spi_uc;
 
-			LM_DBG("Will save pending contact with security-verify parameters, spc_ps %u, spi_pc %u\n",
-					sec_params->data.ipsec->spi_ps, sec_params->data.ipsec->spi_pc);
+			// Get from verify header pcscf server and client ports
+			sec_params->data.ipsec->port_ps = sec_verify_params->data.ipsec->port_us;
+			sec_params->data.ipsec->port_pc = sec_verify_params->data.ipsec->port_uc;
+
+			LM_DBG("Will save pending contact with security-verify parameters, spc_ps %u, spi_pc %u, port_ps %u, port_pc %u\n",
+					sec_params->data.ipsec->spi_ps, sec_params->data.ipsec->spi_pc, sec_params->data.ipsec->port_ps, sec_params->data.ipsec->port_pc);
 		}
 	}
 
@@ -364,19 +381,18 @@ int save_pending(struct sip_msg* _m, udomain_t* _d) {
 		} else {
 			LM_DBG("registering for UL callback\n");
 			ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE | PCSCF_CONTACT_UPDATE, callback_pcscf_contact_cb, NULL);
+
+			// Update security parameters only for the pending contacts
+			if(sec_params){
+				if(ul.update_temp_security(_d, sec_params->type, sec_params, pcontact) != 0){
+					LM_ERR("Error updating temp security\n");
+				}
+			}
 		}
 	} else { //contact already exists - update
         LM_DBG("Contact already exists - not doing anything for now\n");
 	}
 
-    // Update security parameters
-    if(sec_params) {
-        if(ul.update_temp_security(_d, sec_params->type, sec_params, pcontact) != 0)
-        {
-            LM_ERR("Error updating temp security\n");
-        }
-    }
-
 	ul.unlock_udomain(_d, &ci.via_host, ci.via_port, ci.via_prot);
 
 

+ 101 - 0
src/modules/ims_usrloc_pcscf/udomain.c

@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2012 Smile Communications, [email protected]
  * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2019 Aleksandar Yosifov
  * 
  * The initial version of this code was written by Dragos Vingarzan
  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
@@ -696,6 +697,106 @@ int delete_pcontact(udomain_t* _d, /*str* _aor, str* _received_host, int _receiv
 	return 0;
 }
 
+int unreg_pending_contacts_cb(udomain_t* _d, pcontact_t* _c, int type)
+{
+	pcontact_t*		c;
+	pcontact_info_t	contact_info;
+	unsigned int	aorhash, sl, i;
+
+	contact_info.via_host = _c->via_host;
+	contact_info.via_port = SIP_PORT;
+	contact_info.via_prot = _c->via_proto;
+	contact_info.reg_state = PCONTACT_ANY;
+
+	LM_DBG("Searching for contact in P-CSCF usrloc based on VIA [%d://%.*s:%d], reg state 0x%02X\n",
+			contact_info.via_prot, contact_info.via_host.len, contact_info.via_host.s, contact_info.via_port, contact_info.reg_state);
+	
+	aorhash = get_aor_hash(_d, &contact_info.via_host, contact_info.via_port, contact_info.via_prot);
+	sl = aorhash & (_d->size - 1);
+        
+    LM_DBG("get_pcontact slot is [%d]\n", sl);
+	c = _d->table[sl].first;
+
+	for(i = 0; i < _d->table[sl].n; i++){
+		LM_DBG("comparing contact with aorhash [%u], aor [%.*s]\n", c->aorhash, c->aor.len, c->aor.s);
+		LM_DBG("contact host [%.*s:%d]\n", c->contact_host.len, c->contact_host.s, c->contact_port);
+
+		if(c->aorhash == aorhash){
+			ip_addr_t c_ip_addr;
+			ip_addr_t ci_ip_addr;
+
+			// convert 'contact->contact host' ip string to ip_addr_t
+			if (str2ipxbuf(&c->contact_host, &c_ip_addr) < 0){
+				LM_ERR("Unable to convert c->contact_host [%.*s]\n", c->contact_host.len, c->contact_host.s);
+				return 1;
+			}
+
+			// convert 'contact info->via host' ip string to ip_addr_t
+			if(str2ipxbuf(&contact_info.via_host, &ci_ip_addr) < 0){
+				LM_ERR("Unable to convert contact_info.via_host [%.*s]\n", contact_info.via_host.len, contact_info.via_host.s);
+				return 1;
+			}
+
+			// compare 'contact->contact host' and 'contact info->via host'
+			if(ip_addr_cmp(&c_ip_addr, &ci_ip_addr) && (c->contact_port == contact_info.via_port)){
+				LM_DBG("found contact with URI [%.*s]\n", c->aor.len, c->aor.s);
+
+				// finally check state being searched for
+				if((contact_info.reg_state != PCONTACT_ANY) && ((contact_info.reg_state & c->reg_state) == 0)){
+					LM_DBG("can't find contact for requested reg state [%d] - (have [%d])\n", contact_info.reg_state, c->reg_state);
+					c = c->next;
+					continue;
+				}
+
+				// check for equal ipsec parameters
+				if(c->security_temp == NULL || _c->security_temp == NULL){
+					LM_DBG("Invalid temp security\n");
+					c = c->next;
+					continue;
+				}
+
+				if(c->security_temp->type != SECURITY_IPSEC){
+					LM_DBG("Invalid temp security type\n");
+					c = c->next;
+					continue;
+				}
+
+				if(c->security_temp->data.ipsec == NULL || _c->security_temp->data.ipsec == NULL){
+					LM_DBG("Invalid ipsec\n");
+					c = c->next;
+					continue;
+				}
+
+				LM_DBG("=========== c->reg_state 0x%02X, %u-%u | %u-%u | %u-%u | %u-%u | %u-%u | %u-%u | %u-%u | %u-%u |",
+					c->reg_state,
+					c->security_temp->data.ipsec->port_pc, _c->security_temp->data.ipsec->port_pc,
+				    c->security_temp->data.ipsec->port_ps, _c->security_temp->data.ipsec->port_ps,
+				    c->security_temp->data.ipsec->port_uc, _c->security_temp->data.ipsec->port_uc,
+				    c->security_temp->data.ipsec->port_us, _c->security_temp->data.ipsec->port_us,
+				    c->security_temp->data.ipsec->spi_pc, _c->security_temp->data.ipsec->spi_pc,
+				    c->security_temp->data.ipsec->spi_ps, _c->security_temp->data.ipsec->spi_ps,
+				    c->security_temp->data.ipsec->spi_uc, _c->security_temp->data.ipsec->spi_uc,
+				    c->security_temp->data.ipsec->spi_us, _c->security_temp->data.ipsec->spi_us);
+
+				if(c->security_temp->data.ipsec->port_pc == _c->security_temp->data.ipsec->port_pc &&
+				   c->security_temp->data.ipsec->port_ps == _c->security_temp->data.ipsec->port_ps &&
+				   c->security_temp->data.ipsec->port_uc == _c->security_temp->data.ipsec->port_uc &&
+				   c->security_temp->data.ipsec->port_us == _c->security_temp->data.ipsec->port_us &&
+				   c->security_temp->data.ipsec->spi_pc == _c->security_temp->data.ipsec->spi_pc &&
+				   c->security_temp->data.ipsec->spi_ps == _c->security_temp->data.ipsec->spi_ps &&
+				   c->security_temp->data.ipsec->spi_uc == _c->security_temp->data.ipsec->spi_uc &&
+				   c->security_temp->data.ipsec->spi_us == _c->security_temp->data.ipsec->spi_us){
+					// deregister user callback only for contacts with exact sec parameters like registerd contact
+					delete_ulcb(c, type);
+				}
+			}
+		}
+		c = c->next;
+	}
+
+	return 0;
+}
+
 /*!
  * \brief Convert database values into pcontact_info
  *

+ 1 - 0
src/modules/ims_usrloc_pcscf/udomain.h

@@ -77,6 +77,7 @@ int insert_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci
 int get_pcontact(udomain_t* _d, pcontact_info_t* contact_info, struct pcontact** _r);
 int assert_identity(udomain_t* _d, str * _host, unsigned short _port, unsigned short _proto, str * _identity);
 int delete_pcontact(udomain_t* _d, struct pcontact* _r);
+int unreg_pending_contacts_cb(udomain_t* _d, pcontact_t* _c, int type);
 int update_security(udomain_t* _d, security_type _t, security_t* _s, struct pcontact* _c);
 int update_temp_security(udomain_t* _d, security_type _t, security_t* _s, struct pcontact* _c);
 

+ 44 - 0
src/modules/ims_usrloc_pcscf/ul_callback.c

@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2012 Smile Communications, [email protected]
  * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2019 Aleksandar Yosifov
  * 
  * The initial version of this code was written by Dragos Vingarzan
  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
@@ -138,6 +139,49 @@ int register_ulcb( struct pcontact *c, int types, ul_cb f, void *param )
 	return 1;
 }
 
+void delete_ulcb(struct pcontact* c, int type)
+{
+	struct ul_callback* cur;
+	struct ul_callback* prev;
+
+	if(c->cbs.first == 0 || ((c->cbs.reg_types) & type) == 0){
+		return;
+	}
+
+	// if the target is the first callback
+	cur = c->cbs.first;
+	if(cur->types & type){
+		if(cur->param){
+			if(*((unsigned short*)cur->param) == c->received_port){
+				LM_DBG("Removed ulcb from the head for contact: aor[%.*s], via port %u, received port %u, types 0x%02X\n", c->aor.len, c->aor.s, c->via_port, c->received_port, cur->types);
+				c->cbs.first = cur->next;
+				shm_free(cur);
+				return;
+			}
+		}
+	}
+
+	prev = c->cbs.first;
+	cur = c->cbs.first->next;
+	while(cur){
+		if(cur->types & type){
+			if(cur->param){
+				if(*((unsigned short*)cur->param) == c->received_port){
+					prev->next = cur->next;
+					LM_DBG("Removed ulcb for contact: aor[%.*s], via port %u, received port %u, types 0x%02X\n", c->aor.len, c->aor.s, c->via_port, c->received_port, cur->types);
+					shm_free(cur);
+					return;
+				}
+			}
+		}
+
+		prev = cur;
+		cur = cur->next;
+	}
+
+	LM_DBG("No ulcb has been deleted for contact: aor[%.*s], via port %u, received port %u\n", c->aor.len, c->aor.s, c->via_port, c->received_port);
+}
+
 /*! \brief run all transaction callbacks for an event type */
 void run_ul_callbacks( int type , struct pcontact *c)
 {

+ 1 - 0
src/modules/ims_usrloc_pcscf/ul_callback.h

@@ -81,6 +81,7 @@ int init_ulcb_list(void);
 void destroy_ulcb_list(void);
 void destroy_ul_callbacks_list(struct ul_callback* cb);
 int register_ulcb( struct pcontact *c, int types, ul_cb f, void *param);
+void delete_ulcb(struct pcontact* c, int type);
 void run_ul_callbacks( int type , struct pcontact *c);
 void run_ul_create_callbacks(struct pcontact *c);
 

+ 1 - 0
src/modules/ims_usrloc_pcscf/usrloc.c

@@ -70,6 +70,7 @@ int bind_usrloc(usrloc_api_t* api) {
 	api->unlock_udomain = unlock_udomain;
 	api->insert_pcontact = insert_pcontact;
 	api->delete_pcontact = delete_pcontact;
+    api->unreg_pending_contacts_cb = unreg_pending_contacts_cb;
 	api->get_pcontact = get_pcontact;
 	api->assert_identity = assert_identity;
 	api->update_pcontact = update_pcontact;

+ 4 - 0
src/modules/ims_usrloc_pcscf/usrloc.h

@@ -107,6 +107,8 @@ typedef struct ipsec {
     unsigned int spi_ps; /**< SPI Server to use					*/
     unsigned short port_uc; /**< Port UE Client						*/
     unsigned short port_us; /**< Port UE Server						*/
+    unsigned short port_pc; /**< Port Proxy Client		*/
+    unsigned short port_ps; /**< Port Proxy Server      */
 
     str ealg; /**< Cypher Algorithm - ESP				*/
     str r_ealg; /**< received Cypher Algorithm - ESP	*/
@@ -241,6 +243,7 @@ typedef int (*assert_identity_t)(struct udomain* _d, str * _host, unsigned short
 
 typedef int (*insert_pcontact_t)(struct udomain* _d, str* _aor, struct pcontact_info* ci, struct pcontact** _c);
 typedef int (*delete_pcontact_t)(struct udomain* _d, struct pcontact* _c);
+typedef int (*unreg_pending_contacts_cb_t)(struct udomain* _d, struct pcontact* _c, int type);
 typedef int (*update_pcontact_t)(struct udomain* _d, struct pcontact_info* ci, struct pcontact* _c);
 typedef int (*update_rx_regsession_t)(struct udomain* _d, str* session_id, struct pcontact* _c);
 
@@ -266,6 +269,7 @@ typedef struct usrloc_api {
 
     insert_pcontact_t insert_pcontact;
     delete_pcontact_t delete_pcontact;
+    unreg_pending_contacts_cb_t unreg_pending_contacts_cb;
     get_pcontact_t get_pcontact;
     assert_identity_t assert_identity;
 

+ 85 - 34
src/modules/ims_usrloc_pcscf/usrloc_db.c

@@ -3,6 +3,8 @@
  *
  *  Created on: Nov 11, 2013
  *      Author: carlos
+ * 
+ * Copyright (C) 2019 Aleksandar Yosifov
  */
 
 #include "../../lib/srdb1/db.h"
@@ -32,6 +34,8 @@ str ck_col				= str_init(CK_COL);
 str ik_col 				= str_init(IK_COL);
 str ealg_col			= str_init(EALG_COL);
 str ialg_col 			= str_init(IALG_COL);
+str port_pc_col			= str_init(PORTPC_COL);
+str port_ps_col 		= str_init(PORTPS_COL);
 str port_uc_col			= str_init(PORTUC_COL);
 str port_us_col 		= str_init(PORTUS_COL);
 str spi_pc_col 			= str_init(SPIPC_COL);
@@ -45,6 +49,8 @@ str t_ck_col			= str_init(T_CK_COL);
 str t_ik_col 			= str_init(T_IK_COL);
 str t_ealg_col			= str_init(T_EALG_COL);
 str t_ialg_col 			= str_init(T_IALG_COL);
+str t_port_pc_col		= str_init(T_PORTPC_COL);
+str t_port_ps_col 		= str_init(T_PORTPS_COL);
 str t_port_uc_col		= str_init(T_PORTUC_COL);
 str t_port_us_col 		= str_init(T_PORTUS_COL);
 str t_spi_pc_col 		= str_init(T_SPIPC_COL);
@@ -121,22 +127,27 @@ int db_update_pcontact(pcontact_t* _c)
 {
 	str impus, service_routes;
 
-	db_val_t match_values[1];
-	db_key_t match_keys[1] = { &aor_col };
-        db_op_t op[1];
+	db_val_t match_values[2];
+	db_key_t match_keys[2] = { &aor_col, &received_port_col };
+    db_op_t op[2];
 	db_key_t update_keys[8] = { &expires_col, &reg_state_col,
 								&service_routes_col, &received_col,
 								&received_port_col, &received_proto_col,
 								&rx_session_id_col, &public_ids_col };
 	db_val_t values[8];
         
-        LM_DBG("updating pcontact: %.*s\n", _c->aor.len, _c->aor.s);
+    LM_DBG("updating pcontact: aor[%.*s], received port %u\n", _c->aor.len, _c->aor.s, _c->received_port);
 
 	VAL_TYPE(match_values) = DB1_STR;
-
 	VAL_NULL(match_values) = 0;
 	VAL_STR(match_values) = _c->aor;
-        op[0]=OP_EQ;
+
+	VAL_TYPE(match_values + 1)	= DB1_INT;
+	VAL_NULL(match_values + 1)	= 0;
+	VAL_INT(match_values + 1)	= _c->received_port;
+	
+	op[0]=OP_EQ;
+	op[1]=OP_EQ;
 
 	if (use_location_pcscf_table(_c->domain) < 0) {
 		LM_ERR("Error trying to use table %.*s\n", _c->domain->len, _c->domain->s);
@@ -184,7 +195,7 @@ int db_update_pcontact(pcontact_t* _c)
 	SET_PROPER_NULL_FLAG(impus, values, 7);
 	SET_STR_VALUE(values + 7, impus);
 
-	if((ul_dbf.update(ul_dbh, match_keys, op, match_values, update_keys,values, 1, 8)) !=0){
+	if((ul_dbf.update(ul_dbh, match_keys, op, match_values, update_keys,values, 2, 8)) !=0){
 		LM_ERR("could not update database info\n");
 	    return -1;
 	}
@@ -201,22 +212,27 @@ int db_update_pcontact(pcontact_t* _c)
 
 int db_delete_pcontact(pcontact_t* _c)
 {
-	LM_DBG("Trying to delete contact: %.*s\n", _c->aor.len, _c->aor.s);
+	LM_DBG("Trying to delete contact: aor[%.*s], received port %u\n", _c->aor.len, _c->aor.s, _c->received_port);
 	db_val_t values[1];
-	db_key_t match_keys[1] = { &aor_col};
+	db_key_t match_keys[2] = { &aor_col, &received_port_col };
 
 	VAL_TYPE(values) = DB1_STR;
 	VAL_NULL(values) = 0;
 	SET_STR_VALUE(values, _c->aor);
 
+	VAL_TYPE(values + 1) = DB1_INT;
+	VAL_NULL(values + 1) = 0;
+	VAL_INT(values + 1)	 = _c->received_port;
+
 	if (use_location_pcscf_table(_c->domain) < 0) {
 		LM_ERR("Error trying to use table %.*s\n", _c->domain->len, _c->domain->s);
 		return -1;
 	}
 
-    if(ul_dbf.delete(ul_dbh, match_keys, 0, values, 1) < 0) {
-    	LM_ERR("Failed to delete database information: aor[%.*s], rx_session_id=[%.*s]\n",
+    if(ul_dbf.delete(ul_dbh, match_keys, 0, values, 2) < 0) {
+    	LM_ERR("Failed to delete database information: aor[%.*s], received port %u, rx_session_id=[%.*s]\n",
     													_c->aor.len, _c->aor.s,
+														_c->received_port,
     													_c->rx_session_id.len, _c->rx_session_id.s);
         return -1;
     }
@@ -334,20 +350,27 @@ int db_insert_pcontact(struct pcontact* _c)
 }
 
 int db_update_pcontact_security_temp(struct pcontact* _c, security_type _t, security_t* _s) {
-	db_val_t match_values[1];
-	db_key_t match_keys[1] = { &aor_col };
-        db_op_t op[1];
-	db_key_t update_keys[13] = { &t_security_type_col, &t_protocol_col,
-			&t_mode_col, &t_ck_col, &t_ik_col, &t_ealg_col, &t_ialg_col, &t_port_uc_col,
+	db_val_t match_values[2];
+	db_key_t match_keys[2] = { &aor_col, &received_port_col };
+    db_op_t op[2];
+
+	db_key_t update_keys[15] = { &t_security_type_col, &t_protocol_col,
+			&t_mode_col, &t_ck_col, &t_ik_col, &t_ealg_col, &t_ialg_col, &t_port_pc_col, &t_port_ps_col, &t_port_uc_col,
 			&t_port_us_col, &t_spi_pc_col, &t_spi_ps_col, &t_spi_uc_col, &t_spi_us_col };
-	db_val_t values[13];
+	db_val_t values[15];
 
-	LM_DBG("updating temp security for pcontact: %.*s\n", _c->aor.len, _c->aor.s);
+	LM_CRIT("updating temp security for pcontact: aor[%.*s], received port %u\n", _c->aor.len, _c->aor.s, _c->received_port);
 
 	VAL_TYPE(match_values) = DB1_STR;
 	VAL_NULL(match_values) = 0;
 	VAL_STR(match_values) = _c->aor;
-        op[0]=OP_EQ;
+
+	VAL_TYPE(match_values + 1)	= DB1_INT;
+	VAL_NULL(match_values + 1)	= 0;
+	VAL_INT(match_values + 1)	= _c->received_port;
+
+    op[0]=OP_EQ;
+	op[1]=OP_EQ;
 
 	if (use_location_pcscf_table(_c->domain) < 0) {
 		LM_ERR("Error trying to use table %.*s\n", _c->domain->len, _c->domain->s);
@@ -374,42 +397,58 @@ int db_update_pcontact_security_temp(struct pcontact* _c, security_type _t, secu
 		VAL_TYPE(values + ++i) = DB1_STR;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_STR(values + i) = ipsec?ipsec->ck:s_empty;
+
 		VAL_TYPE(values + ++i) = DB1_STR;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_STR(values + i) = ipsec?ipsec->ik:s_empty;
+
 		VAL_TYPE(values + ++i) = DB1_STR;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_STR(values + i) = ipsec?ipsec->ealg:s_empty;
+
 		VAL_TYPE(values + ++i) = DB1_STR;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_STR(values + i) = ipsec?ipsec->alg:s_empty;
+
+		VAL_TYPE(values + ++i) = DB1_INT;
+		VAL_NULL(values + i) = ipsec?0:1;
+		VAL_INT(values + i) = ipsec?ipsec->port_pc:0;
+
+		VAL_TYPE(values + ++i) = DB1_INT;
+		VAL_NULL(values + i) = ipsec?0:1;
+		VAL_INT(values + i) = ipsec?ipsec->port_ps:0;
+
 		VAL_TYPE(values + ++i) = DB1_INT;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_INT(values + i) = ipsec?ipsec->port_uc:0;
+
 		VAL_TYPE(values + ++i) = DB1_INT;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_INT(values + i) = ipsec?ipsec->port_us:0;
+
 		VAL_TYPE(values + ++i) = DB1_BIGINT;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_BIGINT(values + i) = ipsec?ipsec->spi_pc:0;
+
 		VAL_TYPE(values + ++i) = DB1_BIGINT;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_BIGINT(values + i) = ipsec?ipsec->spi_ps:0;
+
 		VAL_TYPE(values + ++i) = DB1_BIGINT;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_BIGINT(values + i) = ipsec?ipsec->spi_uc:0;
+
 		VAL_TYPE(values + ++i) = DB1_BIGINT;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_BIGINT(values + i) = ipsec?ipsec->spi_us:0;
 
-		if ((ul_dbf.update(ul_dbh, match_keys, op, match_values, update_keys,
-				values, 1, 13)) != 0) {
+		if ((ul_dbf.update(ul_dbh, match_keys, op, match_values, update_keys, values, 2, 15)) != 0) {
 			LM_ERR("could not update database info\n");
 			return -1;
 		}
 
 		if (ul_dbf.affected_rows && ul_dbf.affected_rows(ul_dbh) == 0) {
-			LM_DBG("no existing rows for an update... doing insert\n");
+			LM_CRIT("no existing rows for an update... doing insert\n");
 			if (db_insert_pcontact(_c) != 0) {
 				LM_ERR("Failed to insert a pcontact on update\n");
 			}
@@ -425,21 +464,27 @@ int db_update_pcontact_security_temp(struct pcontact* _c, security_type _t, secu
 }
 
 int db_update_pcontact_security(struct pcontact* _c, security_type _t, security_t* _s) {
-	db_val_t match_values[1];
-	db_key_t match_keys[1] = { &aor_col };
-        db_op_t op[1];
-        
-	db_key_t update_keys[13] = { &security_type_col, &protocol_col,
-			&mode_col, &ck_col, &ik_col, &ealg_col, &ialg_col, &port_uc_col,
+	db_val_t match_values[2];
+	db_key_t match_keys[2] = { &aor_col, &received_port_col };
+    db_op_t op[2];
+
+    db_key_t update_keys[15] = { &security_type_col, &protocol_col,
+			&mode_col, &ck_col, &ik_col, &ealg_col, &ialg_col, &port_pc_col, &port_ps_col, &port_uc_col,
 			&port_us_col, &spi_pc_col, &spi_ps_col, &spi_uc_col, &spi_us_col };
-	db_val_t values[13];
+	db_val_t values[15];
 
-	LM_DBG("updating security for pcontact: %.*s\n", _c->aor.len, _c->aor.s);
+	LM_DBG("updating security for pcontact: aor[%.*s], received port %u\n", _c->aor.len, _c->aor.s, _c->received_port);
 
 	VAL_TYPE(match_values) = DB1_STR;
 	VAL_NULL(match_values) = 0;
 	VAL_STR(match_values) = _c->aor;
-        op[0]=OP_EQ;
+
+	VAL_TYPE(match_values + 1)	= DB1_INT;
+	VAL_NULL(match_values + 1)	= 0;
+	VAL_INT(match_values + 1)	= _c->received_port;
+	
+	op[0]=OP_EQ;
+	op[1]=OP_EQ;
 
 	if (use_location_pcscf_table(_c->domain) < 0) {
 		LM_ERR("Error trying to use table %.*s\n", _c->domain->len, _c->domain->s);
@@ -449,7 +494,6 @@ int db_update_pcontact_security(struct pcontact* _c, security_type _t, security_
 	VAL_TYPE(values) = DB1_INT;
 	VAL_TIME(values) = _s?_s->type:0;
 	VAL_NULL(values) = 0;
-        
 
 	switch (_t) {
 	case SECURITY_IPSEC: {
@@ -480,6 +524,14 @@ int db_update_pcontact_security(struct pcontact* _c, security_type _t, security_
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_STR(values + i) = ipsec?ipsec->alg:s_empty;
 
+		VAL_TYPE(values + ++i) = DB1_INT;
+		VAL_NULL(values + i) = ipsec?0:1;
+		VAL_INT(values + i) = ipsec?ipsec->port_pc:0;
+
+		VAL_TYPE(values + ++i) = DB1_INT;
+		VAL_NULL(values + i) = ipsec?0:1;
+		VAL_INT(values + i) = ipsec?ipsec->port_ps:0;
+
 		VAL_TYPE(values + ++i) = DB1_INT;
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_INT(values + i) = ipsec?ipsec->port_uc:0;
@@ -504,8 +556,7 @@ int db_update_pcontact_security(struct pcontact* _c, security_type _t, security_
 		VAL_NULL(values + i) = ipsec?0:1;
 		VAL_BIGINT(values + i) = ipsec?ipsec->spi_us:0;
 
-		if ((ul_dbf.update(ul_dbh, match_keys, op, match_values, update_keys,
-				values, 1, 13)) != 0) {
+		if ((ul_dbf.update(ul_dbh, match_keys, op, match_values, update_keys, values, 2, 15)) != 0) {
 			LM_ERR("could not update database info\n");
 			return -1;
 		}

+ 4 - 0
src/modules/ims_usrloc_pcscf/usrloc_db.h

@@ -77,6 +77,8 @@ typedef enum location_pcscf_fields_idx {
 #define IK_COL				"ik"
 #define EALG_COL			"ealg"
 #define IALG_COL			"ialg"
+#define PORTPC_COL			"port_pc"
+#define PORTPS_COL			"port_ps"
 #define PORTUC_COL			"port_uc"
 #define PORTUS_COL			"port_us"
 #define SPIPC_COL			"spi_pc"
@@ -90,6 +92,8 @@ typedef enum location_pcscf_fields_idx {
 #define T_IK_COL			"t_ik"
 #define T_EALG_COL			"t_ealg"
 #define T_IALG_COL			"t_ialg"
+#define T_PORTPC_COL		"t_port_pc"
+#define T_PORTPS_COL		"t_port_ps"
 #define T_PORTUC_COL		"t_port_uc"
 #define T_PORTUS_COL		"t_port_us"
 #define T_SPIPC_COL			"t_spi_pc"

+ 5 - 1
utils/kamctl/mysql/ims_usrloc_pcscf-create.sql

@@ -1,4 +1,4 @@
-INSERT INTO version (table_name, table_version) values ('location','6');
+INSERT INTO version (table_name, table_version) values ('location','7');
 CREATE TABLE `location` (
   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
   `domain` varchar(64) NOT NULL,
@@ -23,6 +23,8 @@ CREATE TABLE `location` (
   `ik` varchar(100) DEFAULT NULL,
   `ealg` char(20) DEFAULT NULL,
   `ialg` char(20) DEFAULT NULL,
+  `port_pc` int(11) unsigned DEFAULT NULL,
+  `port_ps` int(11) unsigned DEFAULT NULL,
   `port_uc` int(11) unsigned DEFAULT NULL,
   `port_us` int(11) unsigned DEFAULT NULL,
   `spi_pc` int(11) unsigned DEFAULT NULL,
@@ -30,6 +32,8 @@ CREATE TABLE `location` (
   `spi_uc` int(11) unsigned DEFAULT NULL,
   `spi_us` int(11) unsigned DEFAULT NULL,
   `t_security_type` int(11) DEFAULT NULL,
+  `t_port_pc` int(11) unsigned DEFAULT NULL,
+  `t_port_ps` int(11) unsigned DEFAULT NULL,
   `t_port_uc` int(11) unsigned DEFAULT NULL,
   `t_port_us` int(11) unsigned DEFAULT NULL,
   `t_spi_pc` int(11) unsigned DEFAULT NULL,