ims_ipsec_pcscf: support for multiple TCP connections
authorAleksandar Yosifov <alexyosifov@gmail.com>
Fri, 28 Jun 2019 13:59:57 +0000 (16:59 +0300)
committerAleksandar Yosifov <alexyosifov@gmail.com>
Thu, 4 Jul 2019 07:37:06 +0000 (10:37 +0300)
- spi list : fixed a bug with infinity loop.
- port generator: added port generator based on SPI list.
  The port generator is used to generate free Client and Server
  ports for IPSEC UDP/TCP connections.
- Added a method for parsing of the security parameters.
  Used for Re-registration process.
- Remove SA/Policy: Fixed the methods for removing
  SA/Policy. Now created SA/Policy are removed properly based
  on their IPs, Ports, SPIs.
- Added a configurable parameter for IPSEC maximum connections.
  It's necessary because all listen sockets should be initialized
  in mod_init().
- Changes in ipsec_create(): Register user callback only for
  initial Registration. For Re-registration through IPSEC, it's
  not needed to register a user callback for contact expire/delete.
- Set search flag for contact depending of msg type - Request or Reply.
- Added description of a new parameter used for
  the IPSec connections - ipsec_max_connections.

13 files changed:
src/modules/ims_ipsec_pcscf/cmd.c
src/modules/ims_ipsec_pcscf/cmd.h
src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml
src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c
src/modules/ims_ipsec_pcscf/ipsec.c
src/modules/ims_ipsec_pcscf/ipsec.h
src/modules/ims_ipsec_pcscf/port_gen.c [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/port_gen.h [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/sec_agree.c [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/sec_agree.h [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/spi_gen.c
src/modules/ims_ipsec_pcscf/spi_list.c
src/modules/ims_ipsec_pcscf/spi_list_tests.c

index f696ac9..db72618 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
  * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
+ * 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>
 
 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
 
index d3bbae3..497f97a 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
  * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
+
  *
  * The initial version of this code was written by Dragos Vingarzan
  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
 #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 */
index a73bf0b..6ef5067 100644 (file)
@@ -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>
 
@@ -116,6 +116,26 @@ modparam("ims_ipsec_pcscf", "ipsec_server_port", 5063)
     </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>
 
       <para>Each IPSec tunnel has a unique system-wide identifier. This and the following option
@@ -227,7 +247,7 @@ ipsec_forward("location");
         </listitem>
                  </itemizedlist>
       <example>
-        <title>ipsec_forward</title>
+        <title>ipsec_destroy</title>
 
         <programlisting format="linespecific">
 ...
index 4b1d5ec..c59552b 100644 (file)
@@ -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)
index da756e7..9d53b5c 100644 (file)
 #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));
index d334bba..68ff7ed 100644 (file)
@@ -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);
diff --git a/src/modules/ims_ipsec_pcscf/port_gen.c b/src/modules/ims_ipsec_pcscf/port_gen.c
new file mode 100644 (file)
index 0000000..590f26c
--- /dev/null
@@ -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);
+}
diff --git a/src/modules/ims_ipsec_pcscf/port_gen.h b/src/modules/ims_ipsec_pcscf/port_gen.h
new file mode 100644 (file)
index 0000000..1978f64
--- /dev/null
@@ -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_ */
diff --git a/src/modules/ims_ipsec_pcscf/sec_agree.c b/src/modules/ims_ipsec_pcscf/sec_agree.c
new file mode 100644 (file)
index 0000000..089e037
--- /dev/null
@@ -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;
+}
diff --git a/src/modules/ims_ipsec_pcscf/sec_agree.h b/src/modules/ims_ipsec_pcscf/sec_agree.h
new file mode 100644 (file)
index 0000000..958a79c
--- /dev/null
@@ -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
index fa7a3e7..ebd2820 100644 (file)
@@ -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;
         }
 
     }
index ca687fa..6d780e0 100644 (file)
@@ -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;
index c8afcd3..7c731dd 100644 (file)
@@ -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;
     }