Browse Source

ims_ipsec_pcscf: IPv6 support

- Set authentication algorithm for SA depending of
  parsed security-client info from REGISTER request msg.
  For now, the supported algorithms are md5 and sha1,
  the default algorithm is sha1.

- fill_contact() changes:
  No significant changes, but added a debug message
  and code is more readable.

- Added support for IPv6.
  A new parameter IPSEC_LISTEN_ADDR6 is added in pcscf.cfg
  file that describe IPv6 listen address.
  A new module parameter ipsec_listen_addr6 is added in
  kamailio.cfg to pass IPv6 listen address into the ipsec module.

- mode_init() changes:
  Add ipv4 and/or ipv6 listen interfaces depending of configured
  parameters in pcscf/kamailio config files.

- create_ipsec_tunnel() changes:
  Update temp security parameters for contact.
  Add 4 SAs and polisies (UE client->Proxy server, Proxy client->
  UE server, Proxy server->UE client and UE server->Proxy client).
  Server->client SAs are used for TCP connections.

- destroy_ipsec_tunnel() changes:
  Remove all SAs and policies.

- ipsec_forward() changes:
  Refactored to use both UDP and TCP protocols. Selects the protocol
  type, sourse and destination ports based on message type.

- Removed unused method convert_ip_address(). Replaced by core methods
  str2ipbuf() and str2ip6buf().

- changes in README
  README regenerated via ims_ipsec_pscscf_admin.xml.
  Added a new parameter for listen IPv6 address.
Aleksandar Yosifov 6 năm trước cách đây
mục cha
commit
5fe9c7d5a8

+ 49 - 31
src/modules/ims_ipsec_pcscf/README

@@ -44,10 +44,11 @@ Tsvetomir Dimitrov
         3. Parameters
         3. Parameters
 
 
               3.1. ipsec_listen_addr (string)
               3.1. ipsec_listen_addr (string)
-              3.2. ipsec_client_port (int)
-              3.3. ipsec_server_port (int)
-              3.4. ipsec_spi_id_start (int)
-              3.5. ipsec_spi_id_range (int)
+              3.2. ipsec_listen_addr6 (string)
+              3.3. ipsec_client_port (int)
+              3.4. ipsec_server_port (int)
+              3.5. ipsec_spi_id_start (int)
+              3.6. ipsec_spi_id_range (int)
 
 
         4. Functions
         4. Functions
 
 
@@ -58,13 +59,14 @@ Tsvetomir Dimitrov
    List of Examples
    List of Examples
 
 
    1.1. ipsec_listen_addr parameter usage
    1.1. ipsec_listen_addr parameter usage
-   1.2. ipsec_client_port parameter usage
-   1.3. ipsec_server_port parameter usage
-   1.4. ipsec_spi_id_start parameter usage
-   1.5. ipsec_spi_id_range parameter usage
-   1.6. ipsec_create
-   1.7. ipsec_forward
+   1.2. ipsec_listen_addr6 parameter usage
+   1.3. ipsec_client_port parameter usage
+   1.4. ipsec_server_port parameter usage
+   1.5. ipsec_spi_id_start parameter usage
+   1.6. ipsec_spi_id_range parameter usage
+   1.7. ipsec_create
    1.8. ipsec_forward
    1.8. ipsec_forward
+   1.9. ipsec_forward
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -79,10 +81,11 @@ Chapter 1. Admin Guide
    3. Parameters
    3. Parameters
 
 
         3.1. ipsec_listen_addr (string)
         3.1. ipsec_listen_addr (string)
-        3.2. ipsec_client_port (int)
-        3.3. ipsec_server_port (int)
-        3.4. ipsec_spi_id_start (int)
-        3.5. ipsec_spi_id_range (int)
+        3.2. ipsec_listen_addr6 (string)
+        3.3. ipsec_client_port (int)
+        3.4. ipsec_server_port (int)
+        3.5. ipsec_spi_id_start (int)
+        3.6. ipsec_spi_id_range (int)
 
 
    4. Functions
    4. Functions
 
 
@@ -114,46 +117,61 @@ Chapter 1. Admin Guide
 3. Parameters
 3. Parameters
 
 
    3.1. ipsec_listen_addr (string)
    3.1. ipsec_listen_addr (string)
-   3.2. ipsec_client_port (int)
-   3.3. ipsec_server_port (int)
-   3.4. ipsec_spi_id_start (int)
-   3.5. ipsec_spi_id_range (int)
+   3.2. ipsec_listen_addr6 (string)
+   3.3. ipsec_client_port (int)
+   3.4. ipsec_server_port (int)
+   3.5. ipsec_spi_id_start (int)
+   3.6. ipsec_spi_id_range (int)
 
 
 3.1. ipsec_listen_addr (string)
 3.1. ipsec_listen_addr (string)
 
 
    IP address which the Proxy-CSCF will use for incoming/outgoing SIP
    IP address which the Proxy-CSCF will use for incoming/outgoing SIP
    traffic over IPSec.
    traffic over IPSec.
 
 
-   Default value is "127.0.0.1"
+   Default value is empty string (null) - IPv4 listen interface will not
+   be added
 
 
    Example 1.1. ipsec_listen_addr parameter usage
    Example 1.1. ipsec_listen_addr parameter usage
 ...
 ...
-modparam("ims_ipsec_pcscf", "ipsec_listen_addr", "127.0.0.1")
+modparam("ims_ipsec_pcscf", "ipsec_listen_addr", "")
 ...
 ...
 
 
-3.2. ipsec_client_port (int)
+3.2. ipsec_listen_addr6 (string)
+
+   IPv6 address which the Proxy-CSCF will use for incoming/outgoing SIP
+   traffic over IPSec.
+
+   Default value is empty string (null) - IPv6 listen interface will not
+   be added
+
+   Example 1.2. ipsec_listen_addr6 parameter usage
+...
+modparam("ims_ipsec_pcscf", "ipsec_listen_addr6", "")
+...
+
+3.3. ipsec_client_port (int)
 
 
    Port number which will be bound for incoming (server) IPSec traffic.
    Port number which will be bound for incoming (server) IPSec traffic.
 
 
    Default value is 5963.
    Default value is 5963.
 
 
-   Example 1.2. ipsec_client_port parameter usage
+   Example 1.3. ipsec_client_port parameter usage
 ...
 ...
 modparam("ims_ipsec_pcscf", "ipsec_client_port", 5062)
 modparam("ims_ipsec_pcscf", "ipsec_client_port", 5062)
 ...
 ...
 
 
-3.3. ipsec_server_port (int)
+3.4. ipsec_server_port (int)
 
 
    Port number which will be bound for incoming (server) IPSec traffic.
    Port number which will be bound for incoming (server) IPSec traffic.
 
 
    Default value is 5063.
    Default value is 5063.
 
 
-   Example 1.3. ipsec_server_port parameter usage
+   Example 1.4. ipsec_server_port parameter usage
 ...
 ...
 modparam("ims_ipsec_pcscf", "ipsec_server_port", 5063)
 modparam("ims_ipsec_pcscf", "ipsec_server_port", 5063)
 ...
 ...
 
 
-3.4. ipsec_spi_id_start (int)
+3.5. ipsec_spi_id_start (int)
 
 
    Each IPSec tunnel has a unique system-wide identifier. This and the
    Each IPSec tunnel has a unique system-wide identifier. This and the
    following option allows to tune the SPIs used by Kamailio in order to
    following option allows to tune the SPIs used by Kamailio in order to
@@ -162,12 +180,12 @@ modparam("ims_ipsec_pcscf", "ipsec_server_port", 5063)
 
 
    Default value is 100.
    Default value is 100.
 
 
-   Example 1.4. ipsec_spi_id_start parameter usage
+   Example 1.5. ipsec_spi_id_start parameter usage
 ...
 ...
 modparam("ims_ipsec_pcscf", "ipsec_spi_id_start", 100)
 modparam("ims_ipsec_pcscf", "ipsec_spi_id_start", 100)
 ...
 ...
 
 
-3.5. ipsec_spi_id_range (int)
+3.6. ipsec_spi_id_range (int)
 
 
    How many SPIs to be allocated for the process. E.g. if
    How many SPIs to be allocated for the process. E.g. if
    ipsec_spi_id_start = 100 and ipsec_spi_id_range = 1000, SPIs between
    ipsec_spi_id_start = 100 and ipsec_spi_id_range = 1000, SPIs between
@@ -175,7 +193,7 @@ modparam("ims_ipsec_pcscf", "ipsec_spi_id_start", 100)
 
 
    Default value is 1000.
    Default value is 1000.
 
 
-   Example 1.5. ipsec_spi_id_range parameter usage
+   Example 1.6. ipsec_spi_id_range parameter usage
 ...
 ...
 modparam("ims_ipsec_pcscf", "ipsec_spi_id_range", 1000)
 modparam("ims_ipsec_pcscf", "ipsec_spi_id_range", 1000)
 ...
 ...
@@ -197,7 +215,7 @@ modparam("ims_ipsec_pcscf", "ipsec_spi_id_range", 1000)
      * domain - Logical domain within the registrar. If a database is used
      * domain - Logical domain within the registrar. If a database is used
        then this must be name of the table which stores the contacts.
        then this must be name of the table which stores the contacts.
 
 
-   Example 1.6. ipsec_create
+   Example 1.7. ipsec_create
 ...
 ...
 ipsec_create("location");
 ipsec_create("location");
 ...
 ...
@@ -211,7 +229,7 @@ ipsec_create("location");
      * domain - Logical domain within the registrar. If a database is used
      * domain - Logical domain within the registrar. If a database is used
        then this must be name of the table which stores the contacts.
        then this must be name of the table which stores the contacts.
 
 
-   Example 1.7. ipsec_forward
+   Example 1.8. ipsec_forward
 ...
 ...
 ipsec_forward("location");
 ipsec_forward("location");
 ...
 ...
@@ -224,7 +242,7 @@ ipsec_forward("location");
      * domain - Logical domain within the registrar. If a database is used
      * domain - Logical domain within the registrar. If a database is used
        then this must be name of the table which stores the contacts.
        then this must be name of the table which stores the contacts.
 
 
-   Example 1.8. ipsec_forward
+   Example 1.9. ipsec_forward
 ...
 ...
 ipsec_destroy("location");
 ipsec_destroy("location");
 ...
 ...

+ 123 - 84
src/modules/ims_ipsec_pcscf/cmd.c

@@ -57,6 +57,7 @@
 
 
 
 
 extern str ipsec_listen_addr;
 extern str ipsec_listen_addr;
+extern str ipsec_listen_addr6;
 extern short ipsec_listen_port;
 extern short ipsec_listen_port;
 extern short ipsec_server_port;
 extern short ipsec_server_port;
 extern short ipsec_client_port;
 extern short ipsec_client_port;
@@ -111,23 +112,19 @@ static str get_www_auth_param(const char* param_name, str www_auth)
     return val;
     return val;
 }
 }
 
 
-
 static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
 static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
 {
 {
     contact_body_t* cb = NULL;
     contact_body_t* cb = NULL;
     struct via_body* vb = NULL;
     struct via_body* vb = NULL;
-    unsigned short port, proto = 0;
     struct sip_msg* req = NULL;
     struct sip_msg* req = NULL;
 
 
-
     if(!ci) {
     if(!ci) {
-        LM_ERR("fill_contact() called with null ptr\n");
+        LM_ERR("called with null ptr\n");
         return -1;
         return -1;
     }
     }
 
 
     memset(ci, 0, sizeof(struct pcontact_info));
     memset(ci, 0, sizeof(struct pcontact_info));
 
 
-
     if(m->first_line.type == SIP_REQUEST) {
     if(m->first_line.type == SIP_REQUEST) {
         struct sip_uri uri;
         struct sip_uri uri;
         memset(&uri, 0, sizeof(struct sip_uri));
         memset(&uri, 0, sizeof(struct sip_uri));
@@ -140,21 +137,15 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
         // populate host,port, aor in CI
         // populate host,port, aor in CI
         ci->via_host = uri.host;
         ci->via_host = uri.host;
         ci->via_port = uri.port_no ? uri.port_no : 5060;
         ci->via_port = uri.port_no ? uri.port_no : 5060;
-        ci->via_prot = proto;
+        ci->via_prot = 0;
         ci->aor = m->first_line.u.request.uri;
         ci->aor = m->first_line.u.request.uri;
 
 
         req = m;
         req = m;
     }
     }
     else if(m->first_line.type == SIP_REPLY) {
     else if(m->first_line.type == SIP_REPLY) {
-
-        cb = cscf_parse_contacts(m);
-        vb = cscf_get_ue_via(m);
-        port = vb->port?vb->port:5060;
-        proto = vb->proto;
-
         struct cell *t = tmb.t_gett();
         struct cell *t = tmb.t_gett();
         if (!t || t == (void*) -1) {
         if (!t || t == (void*) -1) {
-            LM_ERR("fill_contact(): Reply without transaction\n");
+            LM_ERR("Reply without transaction\n");
             return -1;
             return -1;
         }
         }
 
 
@@ -162,18 +153,24 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
 
 
         cb = cscf_parse_contacts(req);
         cb = cscf_parse_contacts(req);
         if (!cb || (!cb->contacts)) {
         if (!cb || (!cb->contacts)) {
-            LM_ERR("fill_contact(): No contact headers\n");
+            LM_ERR("Reply No contact headers\n");
+            return -1;
+        }
+
+        vb = cscf_get_ue_via(m);
+        if (!vb) {
+            LM_ERR("Reply No via body headers\n");
             return -1;
             return -1;
         }
         }
 
 
         // populate CI with bare minimum
         // populate CI with bare minimum
         ci->via_host = vb->host;
         ci->via_host = vb->host;
-        ci->via_port = port;
-        ci->via_prot = proto;
+        ci->via_port = vb->port;
+        ci->via_prot = vb->proto;
         ci->aor = cb->contacts->uri;
         ci->aor = cb->contacts->uri;
     }
     }
     else {
     else {
-        LM_ERR("fill_contact(): Unknown first line type: %d\n", m->first_line.type);
+        LM_ERR("Unknown first line type: %d\n", m->first_line.type);
         return -1;
         return -1;
     }
     }
 
 
@@ -189,6 +186,11 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
     ci->received_port = req->rcv.src_port;
     ci->received_port = req->rcv.src_port;
     ci->received_proto = req->rcv.proto;
     ci->received_proto = req->rcv.proto;
 
 
+    LM_DBG("SIP %s fill contact with AOR [%.*s], VIA [%d://%.*s:%d], received_host [%d://%.*s:%d]\n",
+            m->first_line.type == SIP_REQUEST ? "REQUEST" : "REPLY",
+            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);
+
     // Set to default, if not set:
     // Set to default, if not set:
     if (ci->received_port == 0)
     if (ci->received_port == 0)
         ci->received_port = 5060;
         ci->received_port = 5060;
@@ -271,49 +273,6 @@ static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m)
     return 0;
     return 0;
 }
 }
 
 
-static int convert_ip_address(const str ip_addr, const unsigned int af, struct ip_addr* result) {
-    memset(result, 0, sizeof(struct ip_addr));
-    int return_code = -1;
-
-    //Allocate dynamically memory in order to avoid buffer overflows
-    char* ipaddr_str = NULL;
-    if((ipaddr_str = pkg_malloc(ip_addr.len + 1)) == NULL) {
-        LM_CRIT("Error allocating memory for IP address conversion.\n");
-        return -1;
-    }
-    memset(ipaddr_str, 0, ip_addr.len + 1);
-    memcpy(ipaddr_str, ip_addr.s, ip_addr.len);
-
-    int err = 0;
-
-    if((err = inet_pton(af, ipaddr_str, &result->u.addr)) != 1) {
-        if(err == 0) {
-            LM_ERR("Error converting ipsec listen IP address. Bad format %.*s\n", ip_addr.len, ip_addr.s);
-        }
-        else {
-            LM_ERR("Error converting ipsec listen IP address: %s\n", strerror(errno));
-        }
-        goto cleanup;   // return_code = -1 by default
-    }
-
-    //Set len by address family
-    if(af == AF_INET6) {
-        result->len = 16;
-    }
-    else {
-        result->len = 4;
-    }
-
-    result->af = af;
-
-    //Set success return code
-    return_code = 0;
-
-cleanup:
-    pkg_free(ipaddr_str);
-    return return_code;
-}
-
 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, unsigned short proto, ipsec_t* s)
 {
 {
     struct mnl_socket* sock = init_mnl_socket();
     struct mnl_socket* sock = init_mnl_socket();
@@ -322,10 +281,21 @@ static int create_ipsec_tunnel(const struct ip_addr *remote_addr, unsigned short
     }
     }
 
 
     //Convert ipsec address from str to struct ip_addr
     //Convert ipsec address from str to struct ip_addr
-    struct ip_addr ipsec_addr;
-    if(convert_ip_address(ipsec_listen_addr, remote_addr->af, &ipsec_addr) != 0) {
-        //there is an error msg in convert_ip_address()
-        return -1;
+	ip_addr_t ipsec_addr;
+
+    if(remote_addr->af == AF_INET){
+        if(str2ipbuf(&ipsec_listen_addr, &ipsec_addr) < 0){
+            LM_ERR("Unable to convert ipsec addr4 [%.*s]\n", ipsec_listen_addr.len, ipsec_listen_addr.s);
+            return 0;
+        }
+    } else if(remote_addr->af == AF_INET6){
+        if(str2ip6buf(&ipsec_listen_addr6, &ipsec_addr) < 0){
+            LM_ERR("Unable to convert ipsec addr6 [%.*s]\n", ipsec_listen_addr6.len, ipsec_listen_addr6.s);
+            return 0;
+        }
+    } else {
+        LM_ERR("Unsupported AF %d\n", remote_addr->af);
+        return 0;
     }
     }
 
 
     //Convert to char* for logging
     //Convert to char* for logging
@@ -336,17 +306,30 @@ static int create_ipsec_tunnel(const struct ip_addr *remote_addr, unsigned short
         return -1;
         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\n",
-            ipsec_listen_addr.len, ipsec_listen_addr.s, ipsec_client_port, ipsec_server_port,
-            remote_addr_str, s->port_uc, s->port_us);
+    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",
+            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);
 
 
-    // P-CSCF 'client' tunnel to UE 'server'
-    add_sa    (sock, proto, &ipsec_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, s->ck, s->ik);
+    // 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);
+
+    // 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);
     add_policy(sock, proto, &ipsec_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
 
 
-    // UE 'client' to P-CSCF 'server' tunnel
-    add_sa    (sock, proto, remote_addr, &ipsec_addr, s->port_uc, ipsec_server_port, s->spi_ps, s->ck, s->ik);
-    add_policy(sock, proto, remote_addr, &ipsec_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
+    // 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);
+
+    // 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);
 
 
     close_mnl_socket(sock);
     close_mnl_socket(sock);
 
 
@@ -360,23 +343,32 @@ static int destroy_ipsec_tunnel(const str remote_addr, unsigned short proto, ips
         return -1;
         return -1;
     }
     }
 
 
+    // TODO: pass ipsec listen address v4 or v6 to destroy the tunnel
+
     LM_DBG("Destroying security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d\n",
     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,
             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);
             remote_addr.len, remote_addr.s, s->port_uc, s->port_us);
 
 
-    // P-CSCF 'client' tunnel to UE 'server'
+    // 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);
+
+    // 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_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_policy(sock, proto, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
 
 
-    // UE 'client' to P-CSCF 'server' tunnel
-    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);
+    // 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);
+
+    // 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);
 
 
     // Release SPIs
     // Release SPIs
     release_spi(s->spi_uc);
     release_spi(s->spi_uc);
     release_spi(s->spi_us);
     release_spi(s->spi_us);
 
 
-
     close_mnl_socket(sock);
     close_mnl_socket(sock);
     return 0;
     return 0;
 }
 }
@@ -532,6 +524,12 @@ int ipsec_create(struct sip_msg* m, udomain_t* d)
         goto cleanup;
         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");
+    }
+
     // Destroy the tunnel, if the contact expires
     // Destroy the tunnel, if the contact expires
     if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, on_expire, NULL) != 1) {
     if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, on_expire, NULL) != 1) {
         LM_ERR("Error subscribing for contact\n");
         LM_ERR("Error subscribing for contact\n");
@@ -562,6 +560,24 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
     struct pcontact_info ci;
     struct pcontact_info ci;
     pcontact_t* pcontact = NULL;
     pcontact_t* pcontact = NULL;
     int ret = IPSEC_CMD_FAIL; // FAIL by default
     int ret = IPSEC_CMD_FAIL; // FAIL by default
+    unsigned char dst_proto = PROTO_UDP;
+    unsigned short dst_port = 0;
+    unsigned short src_port = 0;
+    ip_addr_t via_host;
+    
+    struct sip_msg* req = NULL;
+    if(m->first_line.type == SIP_REPLY) {
+        // Get request from reply
+        struct cell *t = tmb.t_gett();
+        if (!t) {
+            LM_ERR("Error getting transaction\n");
+            goto cleanup;
+        }
+
+        req = t->uas.request;
+    } else {
+        req = m;
+    }
 
 
     //
     //
     // Find the contact
     // Find the contact
@@ -578,6 +594,10 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
         goto cleanup;
         goto cleanup;
     }
     }
 
 
+    if(str2ipxbuf(&ci.via_host, &via_host) < 0){
+        LM_ERR("Error getting AF from ci.via_host\n");
+        goto cleanup;
+    }
 
 
     if(pcontact->security_temp == NULL) {
     if(pcontact->security_temp == NULL) {
         LM_ERR("No security parameters found in contact\n");
         LM_ERR("No security parameters found in contact\n");
@@ -592,7 +612,6 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
 
 
     ipsec_t* s = pcontact->security_temp->data.ipsec;
     ipsec_t* s = pcontact->security_temp->data.ipsec;
 
 
-
     // Update the destination
     // Update the destination
     //
     //
     //       from sec-agree
     //       from sec-agree
@@ -609,7 +628,27 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
     }
     }
 
 
     char buf[1024];
     char buf[1024];
-    int buf_len = snprintf(buf, sizeof(buf) - 1, "sip:%.*s:%d", ci.via_host.len, ci.via_host.s, s->port_us);
+    if(m->first_line.type == SIP_REPLY){
+        // for Reply get the dest proto from the received request
+        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;
+
+        // 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;
+    }else{
+        // for Request get the dest proto from the saved contact
+        dst_proto = pcontact->received_proto;
+
+        // for Request sends from P-CSCF client port
+        src_port = ipsec_client_port;
+        
+        // for Request sends to UE server port
+        dst_port = s->port_us;
+    }
+
+    int buf_len = snprintf(buf, sizeof(buf) - 1, "sip:%.*s:%d", ci.via_host.len, ci.via_host.s, dst_port);
 
 
     if((m->dst_uri.s = pkg_malloc(buf_len)) == NULL) {
     if((m->dst_uri.s = pkg_malloc(buf_len)) == NULL) {
         LM_ERR("Error allocating memory for dst_uri\n");
         LM_ERR("Error allocating memory for dst_uri\n");
@@ -620,9 +659,9 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
     m->dst_uri.len = buf_len;
     m->dst_uri.len = buf_len;
 
 
     // Set send socket
     // Set send socket
-    struct socket_info * client_sock = grep_sock_info(&ipsec_listen_addr, ipsec_client_port, PROTO_UDP);
+    struct socket_info * client_sock = grep_sock_info(via_host.af == AF_INET ? &ipsec_listen_addr : &ipsec_listen_addr6, src_port, dst_proto);
     if(!client_sock) {
     if(!client_sock) {
-        LM_ERR("Error calling grep_sock_info() for ipsec client port in ipsec_forward\n");
+        LM_ERR("Error calling grep_sock_info() for ipsec client port\n");
         return -1;
         return -1;
     }
     }
     m->force_send_socket = client_sock;
     m->force_send_socket = client_sock;
@@ -631,9 +670,9 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
     struct dest_info dst_info;
     struct dest_info dst_info;
     dst_info.send_sock = client_sock;
     dst_info.send_sock = client_sock;
 #ifdef USE_DNS_FAILOVER
 #ifdef USE_DNS_FAILOVER
-    if (!uri2dst(NULL, &dst_info, m, &m->dst_uri, PROTO_UDP)) {
+    if (!uri2dst(NULL, &dst_info, m, &m->dst_uri, dst_proto)) {
 #else
 #else
-    if (!uri2dst(&dst_info, m, &m->dst_uri, PROTO_UDP)) {
+    if (!uri2dst(&dst_info, m, &m->dst_uri, dst_proto)) {
 #endif
 #endif
         LM_ERR("Error converting dst_uri (%.*s) to struct dst_info\n", m->dst_uri.len, m->dst_uri.s);
         LM_ERR("Error converting dst_uri (%.*s) to struct dst_info\n", m->dst_uri.len, m->dst_uri.s);
         goto cleanup;
         goto cleanup;
@@ -649,7 +688,7 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d)
         t->uas.response.dst = dst_info;
         t->uas.response.dst = dst_info;
     }
     }
 
 
-    LM_DBG("Destination changed to %.*s\n", m->dst_uri.len, m->dst_uri.s);
+    LM_DBG("Destination changed to [%d://%.*s]\n", dst_info.proto, m->dst_uri.len, m->dst_uri.s);
 
 
     ret = IPSEC_CMD_SUCCESS; // all good, return SUCCESS
     ret = IPSEC_CMD_SUCCESS; // all good, return SUCCESS
 
 

+ 19 - 2
src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml

@@ -49,14 +49,31 @@
       <title><varname>ipsec_listen_addr</varname> (string)</title>
       <title><varname>ipsec_listen_addr</varname> (string)</title>
 
 
       <para>IP address which the Proxy-CSCF will use for incoming/outgoing SIP traffic over IPSec.</para>
       <para>IP address which the Proxy-CSCF will use for incoming/outgoing SIP traffic over IPSec.</para>
-      <para><emphasis>Default value is "127.0.0.1"</emphasis></para>
+      <para><emphasis>Default value is empty string (null) - IPv4 listen interface will not be added</emphasis></para>
 
 
       <example>
       <example>
         <title><varname>ipsec_listen_addr</varname> parameter usage</title>
         <title><varname>ipsec_listen_addr</varname> parameter usage</title>
 
 
         <programlisting format="linespecific">
         <programlisting format="linespecific">
 ...
 ...
-modparam("ims_ipsec_pcscf", "ipsec_listen_addr", "127.0.0.1")
+modparam("ims_ipsec_pcscf", "ipsec_listen_addr", "")
+...
+        </programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>ipsec_listen_addr6</varname> (string)</title>
+
+      <para>IPv6 address which the Proxy-CSCF will use for incoming/outgoing SIP traffic over IPSec.</para>
+      <para><emphasis>Default value is empty string (null) - IPv6 listen interface will not be added</emphasis></para>
+
+      <example>
+        <title><varname>ipsec_listen_addr6</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("ims_ipsec_pcscf", "ipsec_listen_addr6", "")
 ...
 ...
         </programlisting>
         </programlisting>
       </example>
       </example>

+ 65 - 28
src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c

@@ -35,7 +35,8 @@ usrloc_api_t ul;						/**!< Structure containing pointers to usrloc functions*/
 struct tm_binds tmb;					/**!< TM API structure */
 struct tm_binds tmb;					/**!< TM API structure */
 
 
 
 
-str ipsec_listen_addr = str_init("127.0.0.1");
+str ipsec_listen_addr = STR_NULL;
+str ipsec_listen_addr6 = STR_NULL;
 int ipsec_client_port =  5062;
 int ipsec_client_port =  5062;
 int ipsec_server_port =  5063;
 int ipsec_server_port =  5063;
 int spi_id_start = 100;
 int spi_id_start = 100;
@@ -68,7 +69,8 @@ static cmd_export_t cmds[] = {
  * Exported parameters
  * Exported parameters
  */
  */
 static param_export_t params[] = {
 static param_export_t params[] = {
-	{"ipsec_listen_addr",   PARAM_STR, &ipsec_listen_addr   },
+	{"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_client_port",	INT_PARAM, &ipsec_client_port   },
 	{"ipsec_server_port",	INT_PARAM, &ipsec_server_port   },
 	{"ipsec_server_port",	INT_PARAM, &ipsec_server_port   },
 	{"ipsec_spi_id_start",	INT_PARAM, &spi_id_start},
 	{"ipsec_spi_id_start",	INT_PARAM, &spi_id_start},
@@ -97,13 +99,8 @@ struct module_exports exports = {
  * Initialize parent
  * Initialize parent
  */
  */
 static int mod_init(void) {
 static int mod_init(void) {
-    char addr[128];
-    if(ipsec_listen_addr.len > sizeof(addr)-1) {
-        LM_ERR("Bad value for ipsec listen address: %.*s\n", ipsec_listen_addr.len, ipsec_listen_addr.s);
-        return -1;
-    }
-    memset(addr, 0, sizeof(addr));
-    memcpy(addr, ipsec_listen_addr.s, ipsec_listen_addr.len);
+    char addr4[128];
+	char addr6[128];
 
 
 	bind_usrloc_t bind_usrloc;
 	bind_usrloc_t bind_usrloc;
 
 
@@ -116,36 +113,76 @@ static int mod_init(void) {
 	if (bind_usrloc(&ul) < 0) {
 	if (bind_usrloc(&ul) < 0) {
 		return -1;
 		return -1;
 	}
 	}
-	LM_DBG("Successfully bound to PCSCF Usrloc module\n");
+	LM_INFO("Successfully bound to PCSCF Usrloc module\n");
 
 
 	/* load the TM API */
 	/* load the TM API */
 	if (load_tm_api(&tmb) != 0) {
 	if (load_tm_api(&tmb) != 0) {
 		LM_ERR("can't load TM API\n");
 		LM_ERR("can't load TM API\n");
 		return -1;
 		return -1;
 	}
 	}
-	LM_DBG("Successfully bound to TM module\n");
+	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;
+    	}
 
 
-    //add listen interfaces
-    if(add_listen_iface(addr, NULL, ipsec_client_port, PROTO_TCP, 0) != 0) {
-        LM_ERR("Error adding listen ipsec client interface\n");
-        return -1;
-    }
+	    memset(addr4, 0, sizeof(addr4));
+	    memcpy(addr4, ipsec_listen_addr.s, ipsec_listen_addr.len);
 
 
-    if(add_listen_iface(addr, NULL, ipsec_server_port, PROTO_TCP, 0) != 0) {
-        LM_ERR("Error adding listen ipsec server interface\n");
-        return -1;
-    }
+		//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(addr, NULL, ipsec_client_port, PROTO_UDP, 0) != 0) {
-        LM_ERR("Error adding listen ipsec client interface\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(addr, NULL, ipsec_server_port, PROTO_UDP, 0) != 0) {
-        LM_ERR("Error adding listen ipsec server interface\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) {
     if(fix_all_socket_lists() != 0) {
         LM_ERR("Error calling fix_all_socket_lists() during module initialisation\n");
         LM_ERR("Error calling fix_all_socket_lists() during module initialisation\n");

+ 12 - 2
src/modules/ims_ipsec_pcscf/ipsec.c

@@ -109,7 +109,7 @@ unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto)
     };
     };
 }
 }
 
 
-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)
+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)
 {
 {
     char l_msg_buf[MNL_SOCKET_BUFFER_SIZE];
     char l_msg_buf[MNL_SOCKET_BUFFER_SIZE];
     char l_auth_algo_buf[XFRM_TMPLS_BUF_SIZE];
     char l_auth_algo_buf[XFRM_TMPLS_BUF_SIZE];
@@ -187,7 +187,17 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_add
     // The point is to provide a continuous chunk of memory with the key in it
     // The point is to provide a continuous chunk of memory with the key in it
     l_auth_algo = (struct xfrm_algo *)l_auth_algo_buf;
     l_auth_algo = (struct xfrm_algo *)l_auth_algo_buf;
 
 
-    strcpy(l_auth_algo->alg_name,"md5");
+    // Set the proper algorithm by r_alg str
+    if(strncasecmp(r_alg.s, "hmac-md5-96", r_alg.len) == 0) {
+        strcpy(l_auth_algo->alg_name,"md5");
+    }
+    else if(strncasecmp(r_alg.s, "hmac-sha1-96", r_alg.len) == 0) {
+        strcpy(l_auth_algo->alg_name,"sha1");
+    } else {
+        // set default algorithm to sha1
+        strcpy(l_auth_algo->alg_name,"sha1");
+    }
+
     l_auth_algo->alg_key_len = ik.len * 4;
     l_auth_algo->alg_key_len = ik.len * 4;
     string_to_key(l_auth_algo->alg_key, ik);
     string_to_key(l_auth_algo->alg_key, ik);
 
 

+ 1 - 1
src/modules/ims_ipsec_pcscf/ipsec.h

@@ -42,7 +42,7 @@ 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, 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);
+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 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 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);