فهرست منبع

ims_ipsec_pcscf: delete unused tunnels

- ipsec_create - added a new function parameter to delete
unused tunnels before creation of new ipsec connection.
- added new global parameters for ipsec listen addresses.
Aleksandar Yosifov 5 سال پیش
والد
کامیت
b28862bf5d

+ 21 - 21
src/modules/ims_ipsec_pcscf/cmd.c

@@ -63,6 +63,8 @@
 extern str ipsec_listen_addr;
 extern str ipsec_listen_addr6;
 extern int ipsec_reuse_server_port;
+extern ip_addr_t ipsec_listen_ip_addr;
+extern ip_addr_t ipsec_listen_ip_addr6;
 
 extern int spi_id_start;
 
@@ -78,6 +80,8 @@ extern struct tm_binds tmb;
 #define IPSEC_SEND_FORCE_SOCKET		0x01 /* if set - set send force socket for request messages */
 #define IPSEC_REVERSE_SEARCH		0x02 /* if set - start searching from the last element */
 
+#define IPSEC_CREATE_DELETE_UNUSED_TUNNELS	0x01 /* if set - delete unused tunnels before every registration */
+
 int bind_ipsec_pcscf(ipsec_pcscf_api_t* api) {
 	if(!api){
 		LM_ERR("invalid parameter value\n");
@@ -409,21 +413,13 @@ static int create_ipsec_tunnel(const struct ip_addr *remote_addr, ipsec_t* s)
         return -1;
     }
 
-    //Convert ipsec address from str to struct ip_addr
-	ip_addr_t ipsec_addr;
+    // pointer to the current listen address
+	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);
-            close_mnl_socket(sock);
-            return -1;
-        }
+        ipsec_addr = &ipsec_listen_ip_addr;
     } 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);
-            close_mnl_socket(sock);
-            return -1;
-        }
+        ipsec_addr = &ipsec_listen_ip_addr6;
     } else {
         LM_ERR("Unsupported AF %d\n", remote_addr->af);
         close_mnl_socket(sock);
@@ -446,23 +442,23 @@ static int create_ipsec_tunnel(const struct ip_addr *remote_addr, ipsec_t* s)
 
     // SA1 UE client to P-CSCF server
     //               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);
+    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, &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);
+    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, &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);
+    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, 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);
+    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);
 
@@ -650,7 +646,7 @@ int add_security_server_header(struct sip_msg* m, ipsec_t* s)
     return 0;
 }
 
-int ipsec_create(struct sip_msg* m, udomain_t* d)
+int ipsec_create(struct sip_msg* m, udomain_t* d, int _cflags)
 {
     pcontact_t* pcontact = NULL;
     struct pcontact_info ci;
@@ -662,6 +658,10 @@ int ipsec_create(struct sip_msg* m, udomain_t* d)
         return ret;
     }
 
+    if (_cflags & IPSEC_CREATE_DELETE_UNUSED_TUNNELS) {
+        delete_unused_tunnels();
+    }
+
     ul.lock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
 
     if (ul.get_pcontact(d, &ci, &pcontact, 0) != 0) {

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

@@ -62,7 +62,7 @@ typedef int (*bind_ipsec_pcscf_t)(ipsec_pcscf_api_t* api);
 struct sip_msg;
 struct udomain_t;
 
-int ipsec_create(struct sip_msg* m, udomain_t* d);
+int ipsec_create(struct sip_msg* m, udomain_t* d, int _cflags);
 int ipsec_forward(struct sip_msg* m, udomain_t* d, int _cflags);
 int ipsec_destroy(struct sip_msg* m, udomain_t* d);
 int ipsec_cleanall();

+ 7 - 0
src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml

@@ -217,6 +217,11 @@ modparam("ims_ipsec_pcscf", "ipsec_spi_id_range", 1000)
           If a database is used then this must be name of the table which
           stores the contacts.
           </para>
+          <para>
+          <emphasis>flags</emphasis> - bitwise flag:
+          0x01 - if set - delete unused tunnels before every registration.
+          This is an optional parameter, default value - 0.
+          </para>
         </listitem>
 		  </itemizedlist>
       <example>
@@ -225,6 +230,8 @@ modparam("ims_ipsec_pcscf", "ipsec_spi_id_range", 1000)
         <programlisting format="linespecific">
 ...
 ipsec_create("location");
+# or
+ipsec_create("location", "1");
 ...
         </programlisting>
       </example>

+ 22 - 1
src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c

@@ -47,6 +47,9 @@ int spi_id_start = 100;
 int spi_id_range = 1000;
 int xfrm_user_selector = 143956232;
 
+ip_addr_t ipsec_listen_ip_addr;
+ip_addr_t ipsec_listen_ip_addr6;
+
 /*! \brief Module init & destroy function */
 static int  mod_init(void);
 static int  child_init(int);
@@ -69,6 +72,7 @@ int init_flag = 0;
  */
 static cmd_export_t cmds[] = {
 	{"ipsec_create",  (cmd_function)w_create,  1, save_fixup2, 0, ONREPLY_ROUTE },
+	{"ipsec_create",  (cmd_function)w_create,  2, save_fixup2, free_uint_fixup, ONREPLY_ROUTE },
 	{"ipsec_forward", (cmd_function)w_forward, 1, save_fixup2, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
 	{"ipsec_forward", (cmd_function)w_forward, 2, save_fixup2, free_uint_fixup, REQUEST_ROUTE | ONREPLY_ROUTE },
 	{"ipsec_destroy", (cmd_function)w_destroy, 1, save_fixup2, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
@@ -176,6 +180,20 @@ static int ipsec_add_listen_ifaces()
 	char addr6[128];
 	int i;
 
+	if(ipsec_listen_addr.len) {
+		if(str2ipbuf(&ipsec_listen_addr, &ipsec_listen_ip_addr) < 0){
+			LM_ERR("Unable to convert ipsec addr4 [%.*s]\n", ipsec_listen_addr.len, ipsec_listen_addr.s);
+			return -1;
+		}
+	}
+
+	if(ipsec_listen_addr6.len) {
+		if(str2ip6buf(&ipsec_listen_addr6, &ipsec_listen_ip_addr6) < 0){
+			LM_ERR("Unable to convert ipsec addr6 [%.*s]\n", ipsec_listen_addr6.len, ipsec_listen_addr6.s);
+			return -1;
+		}
+	}
+
 	for(i = 0; i < ipsec_max_connections; ++i){
 		if(ipsec_listen_addr.len) {
 			if(ipsec_listen_addr.len > sizeof(addr4)-1) {
@@ -389,7 +407,10 @@ static int save_fixup2(void** param, int param_no)
  */
 static int w_create(struct sip_msg* _m, char* _d, char* _cflags)
 {
-	return ipsec_create(_m, (udomain_t*)_d);
+	if(_cflags){
+		return ipsec_create(_m, (udomain_t*)_d, ((int)(*_cflags)));
+	}
+	return ipsec_create(_m, (udomain_t*)_d, 0);
 }
 
 static int w_forward(struct sip_msg* _m, char* _d, char* _cflags)

+ 473 - 4
src/modules/ims_ipsec_pcscf/ipsec.c

@@ -23,11 +23,14 @@
  */
 
 #include "ipsec.h"
+#include "spi_gen.h"
+#include "port_gen.h"
 
 #include "../../core/dprint.h"
 #include "../../core/mem/pkg.h"
 #include "../../core/ip_addr.h"
 #include "../../core/resolve.h"
+#include "../ims_usrloc_pcscf/usrloc.h"
 
 #include <errno.h>
 #include <arpa/inet.h>
@@ -40,7 +43,12 @@
 #define NLMSG_BUF_SIZE 4096
 #define NLMSG_DELETEALL_BUF_SIZE 8192
 
+extern usrloc_api_t ul;
 
+extern str ipsec_listen_addr;
+extern str ipsec_listen_addr6;
+extern ip_addr_t ipsec_listen_ip_addr;
+extern ip_addr_t ipsec_listen_ip_addr6;
 extern int xfrm_user_selector;
 
 struct xfrm_buffer {
@@ -48,6 +56,10 @@ struct xfrm_buffer {
     int offset;
 };
 
+struct del_tunnels {
+    void *contacts;
+    struct xfrm_buffer delmsg_buf;
+};
 
 //
 // This file contains all Linux specific IPSec code.
@@ -533,7 +545,7 @@ int clean_sa(struct mnl_socket*  mnl_socket)
 {
     struct {
         struct nlmsghdr n;
-        //char buf[NLMSG_BUF_SIZE];
+        //char buf[NLMSG_DELETEALL_BUF_SIZE];
     } req = {
         .n.nlmsg_len = NLMSG_HDRLEN,
         .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
@@ -546,7 +558,7 @@ int clean_sa(struct mnl_socket*  mnl_socket)
         return 1;
     }
 
-    char buf[NLMSG_BUF_SIZE];
+    char buf[NLMSG_DELETEALL_BUF_SIZE];
     memset(&buf, 0, sizeof(buf));
 
     struct xfrm_buffer delmsg_buf;
@@ -575,7 +587,7 @@ int clean_policy(struct mnl_socket*  mnl_socket)
 {
     struct {
         struct nlmsghdr n;
-        //char buf[NLMSG_BUF_SIZE];
+        //char buf[NLMSG_DELETEALL_BUF_SIZE];
     } req = {
         .n.nlmsg_len = NLMSG_HDRLEN,
         .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
@@ -588,7 +600,7 @@ int clean_policy(struct mnl_socket*  mnl_socket)
         return 1;
     }
 
-    char buf[NLMSG_BUF_SIZE];
+    char buf[NLMSG_DELETEALL_BUF_SIZE];
     memset(&buf, 0, sizeof(buf));
 
     struct xfrm_buffer delmsg_buf;
@@ -612,3 +624,460 @@ int clean_policy(struct mnl_socket*  mnl_socket)
 
     return 0;
 }
+
+static int delete_unused_sa_cb(const struct nlmsghdr *nlh, void *data)
+{
+    struct xfrm_usersa_info *xsinfo = NLMSG_DATA(nlh);
+
+    // Check if user id is different from Kamailio's
+    if (ntohl(xsinfo->sel.user) != xfrm_user_selector) {
+        return MNL_CB_OK;
+    }
+
+    ip_addr_t sa_src_addr, sa_dst_addr;
+    unsigned int sa_spi;
+    unsigned short sa_sport, sa_dport;
+
+    ip_addr_t *proxy_ip_addr = NULL;
+
+    sa_src_addr.af = sa_dst_addr.af = xsinfo->sel.family;
+    if(xsinfo->sel.family == AF_INET6) {
+        memcpy(sa_src_addr.u.addr32, xsinfo->sel.saddr.a6, sizeof(xsinfo->sel.saddr.a6));
+        memcpy(sa_dst_addr.u.addr32, xsinfo->sel.daddr.a6, sizeof(xsinfo->sel.daddr.a6));
+        sa_src_addr.len = sa_dst_addr.len = 16;
+
+        proxy_ip_addr = &ipsec_listen_ip_addr6;
+    } else if(xsinfo->sel.family == AF_INET) {
+        sa_src_addr.u.addr32[0] = xsinfo->sel.saddr.a4;
+        sa_dst_addr.u.addr32[0] = xsinfo->sel.daddr.a4;
+        sa_src_addr.len = sa_dst_addr.len = 4;
+
+        proxy_ip_addr = &ipsec_listen_ip_addr;
+    } else {
+        LM_CRIT("Unknown AF %d\n", xsinfo->sel.family);
+        return MNL_CB_OK;
+    }
+
+    sa_spi = htonl(xsinfo->id.spi);
+    sa_sport = htons(xsinfo->sel.sport);
+    sa_dport = htons(xsinfo->sel.dport);
+
+    char saddr_buf[IP_ADDR_MAX_STRZ_SIZE];
+    char daddr_buf[IP_ADDR_MAX_STRZ_SIZE];
+    memset(saddr_buf, 0, IP_ADDR_MAX_STRZ_SIZE);
+    memset(daddr_buf, 0, IP_ADDR_MAX_STRZ_SIZE);
+    ip_addr2sbufz(&sa_src_addr, saddr_buf, IP_ADDR_MAX_STRZ_SIZE);
+    ip_addr2sbufz(&sa_dst_addr, daddr_buf, IP_ADDR_MAX_STRZ_SIZE);
+
+//  * +-----------------+---------------+------+------+------+------+-------+-------+-------+-------+
+//  * |received host.len|received host.s|spi_uc|spi_us|spi_pc|spi_ps|port_uc|port_us|port_pc|port_ps|
+//  * +-----------------+---------------+------+------+------+------+-------+-------+-------+-------+
+//  * |received host.len|received host.s|spi_uc|spi_us|spi_pc|spi_ps|port_uc|port_us|port_pc|port_ps|
+//  * +-----------------+---------------+------+------+------+------+-------+-------+-------+-------+
+//  * |.............................................................................................|
+//  * +-----------------+---------------+------+------+------+------+-------+-------+-------+-------+
+//  * |0000|
+//  * +----+
+    void  *cp;
+    str received_host;
+    ip_addr_t received_host_addr;
+    ipsec_t ipsec;
+    
+    cp = ((struct del_tunnels*)data)->contacts;
+    while (1) {
+        memcpy(&(received_host.len), cp, sizeof(received_host.len));
+        if (received_host.len == 0) {
+            break; // no more records
+        }
+
+        received_host.s = (char*)cp + sizeof(received_host.len);
+        cp = (char*)cp + sizeof(received_host.len) + received_host.len;
+
+        memcpy(&ipsec.spi_uc, cp, sizeof(ipsec.spi_uc));
+        cp = (char*)cp + sizeof(ipsec.spi_uc);
+
+        memcpy(&ipsec.spi_us, cp, sizeof(ipsec.spi_us));
+        cp = (char*)cp + sizeof(ipsec.spi_us);
+
+        memcpy(&ipsec.spi_pc, cp, sizeof(ipsec.spi_pc));
+        cp = (char*)cp + sizeof(ipsec.spi_pc);
+
+        memcpy(&ipsec.spi_ps, cp, sizeof(ipsec.spi_ps));
+        cp = (char*)cp + sizeof(ipsec.spi_ps);
+
+        memcpy(&ipsec.port_uc, cp, sizeof(ipsec.port_uc));
+        cp = (char*)cp + sizeof(ipsec.port_uc);
+
+        memcpy(&ipsec.port_us, cp, sizeof(ipsec.port_us));
+        cp = (char*)cp + sizeof(ipsec.port_us);
+
+        memcpy(&ipsec.port_pc, cp, sizeof(ipsec.port_pc));
+        cp = (char*)cp + sizeof(ipsec.port_pc);
+
+        memcpy(&ipsec.port_ps, cp, sizeof(ipsec.port_ps));
+        cp = (char*)cp + sizeof(ipsec.port_ps);
+
+        // convert 'received host' ip string to ip_addr_t
+        if (str2ipxbuf(&received_host, &received_host_addr) < 0){
+            LM_ERR("Unable to convert received host [%.*s]\n", received_host.len, received_host.s);
+            return 1;
+        }
+
+        // SA  Src address  Dst address Src port    Dst port        SPI
+        // SA1 UE-IP        Proxy-IP    UE-Server   Proxy-Client    Proxy-Client
+        // SA2 UE-IP        Proxy-IP    UE-Client   Proxy-Server    Proxy-Server
+        // SA3 Proxy-IP     UE-IP       P-Server    UE-Client       UE-Client
+        // SA4 Proxy-IP     UE-IP       P-Client    UE-Server       UE-Server
+
+        // Check for SA1 or SA2
+        if(ip_addr_cmp(&sa_src_addr, &received_host_addr) && ip_addr_cmp(&sa_dst_addr, proxy_ip_addr)) {
+            // check for SA1
+            if(sa_sport == ipsec.port_us && sa_dport == ipsec.port_pc && sa_spi == ipsec.spi_pc){
+                return MNL_CB_OK;
+            }
+
+            // check for SA2
+            if(sa_sport == ipsec.port_uc && sa_dport == ipsec.port_ps && sa_spi == ipsec.spi_ps){
+                return MNL_CB_OK;
+            }
+        }
+
+        // Check for SA3 or SA4
+        if(ip_addr_cmp(&sa_src_addr, proxy_ip_addr) && ip_addr_cmp(&sa_dst_addr, &received_host_addr)) {
+            // check for SA3
+            if(sa_sport == ipsec.port_ps && sa_dport == ipsec.port_uc && sa_spi == ipsec.spi_uc){
+                return MNL_CB_OK;
+            }
+
+            // check for SA4
+            if(sa_sport == ipsec.port_pc && sa_dport == ipsec.port_us && sa_spi == ipsec.spi_us){
+                return MNL_CB_OK;
+            }
+        }
+    }
+
+    LM_DBG("The SA is not used and will be deleted: spi:%u | saddr:%s:%u | daddr:%s:%u\n", sa_spi, saddr_buf, sa_sport, daddr_buf, sa_dport);
+
+    struct xfrm_buffer* delmsg_buf = &((struct del_tunnels*)data)->delmsg_buf;
+    uint32_t new_delmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id));
+
+    if(delmsg_buf->offset + new_delmsg_len > sizeof(delmsg_buf->buf)/sizeof(delmsg_buf->buf[0])) {
+        LM_ERR("Not enough memory allocated for delete SAs netlink command\n");
+        return MNL_CB_ERROR;
+    }
+
+    struct nlmsghdr *new_delmsg = (struct nlmsghdr *)&delmsg_buf->buf[delmsg_buf->offset];
+    new_delmsg->nlmsg_len = new_delmsg_len;
+    new_delmsg->nlmsg_flags = NLM_F_REQUEST;
+    new_delmsg->nlmsg_type = XFRM_MSG_DELSA;
+    new_delmsg->nlmsg_seq = time(NULL);
+
+    struct xfrm_usersa_id *xsid = NLMSG_DATA(new_delmsg);
+    xsid->family = xsinfo->family;
+    memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr));
+    xsid->spi = xsinfo->id.spi;
+    xsid->proto = xsinfo->id.proto;
+
+    mnl_attr_put(new_delmsg, XFRMA_SRCADDR, sizeof(xsid->daddr), &xsinfo->saddr);
+
+    delmsg_buf->offset += new_delmsg->nlmsg_len;
+
+    // NOTE: Release the Proxy SPIs and Ports only here. Do not release the same SPIs and ports in delete unsused policy callback.
+    // Release SPIs
+    release_spi(ipsec.spi_pc);
+    release_spi(ipsec.spi_ps);
+
+    // Release the client and the server ports
+    release_cport(ipsec.port_pc);
+    release_sport(ipsec.port_ps);
+
+    return MNL_CB_OK;
+}
+
+static int delete_unused_sa(struct mnl_socket *mnl_socket, void *contacts)
+{
+    struct {
+        struct nlmsghdr n;
+        //char buf[NLMSG_DELETEALL_BUF_SIZE];
+    } req = {
+        .n.nlmsg_len = NLMSG_HDRLEN,
+        .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+        .n.nlmsg_type = XFRM_MSG_GETSA,
+        .n.nlmsg_seq = time(NULL),
+    };
+
+    if(mnl_socket_sendto(mnl_socket, &req, req.n.nlmsg_len) == -1) {
+        LM_ERR("Error sending get all SAs command via netlink socket: %s\n", strerror(errno));
+        return 1;
+    }
+
+    char buf[NLMSG_DELETEALL_BUF_SIZE];
+    memset(&buf, 0, sizeof(buf));
+
+    struct del_tunnels del_data;
+    del_data.contacts = contacts;
+    memset(&del_data.delmsg_buf, 0, sizeof(del_data.delmsg_buf));
+
+    int ret = mnl_socket_recvfrom(mnl_socket, buf, sizeof(buf));
+    while (ret > 0) {
+        ret = mnl_cb_run(buf, ret, req.n.nlmsg_seq, mnl_socket_get_portid(mnl_socket), delete_unused_sa_cb, &del_data);
+        if (ret <= MNL_CB_STOP) {
+            break;
+        }
+
+        ret = mnl_socket_recvfrom(mnl_socket, buf, sizeof(buf));
+    }
+
+    // DELETE unused SAs
+    if(mnl_socket_sendto(mnl_socket, &del_data.delmsg_buf.buf, del_data.delmsg_buf.offset) == -1) {
+        LM_ERR("Error sending delete unused SAs command via netlink socket: %s\n", strerror(errno));
+        return 1;
+    }
+
+    return 0;
+}
+
+static int delete_unused_policy_cb(const struct nlmsghdr *nlh, void *data)
+{
+    struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(nlh);
+
+    //Check if user id is different from Kamailio's
+    if (ntohl(xpinfo->sel.user) != xfrm_user_selector) {
+        return MNL_CB_OK;
+    }
+
+    ip_addr_t sa_src_addr, sa_dst_addr;
+    unsigned short sa_sport, sa_dport;
+
+    ip_addr_t *proxy_ip_addr = NULL;
+
+    sa_src_addr.af = sa_dst_addr.af = xpinfo->sel.family;
+    if(xpinfo->sel.family == AF_INET6) {
+        memcpy(sa_src_addr.u.addr32, xpinfo->sel.saddr.a6, sizeof(xpinfo->sel.saddr.a6));
+        memcpy(sa_dst_addr.u.addr32, xpinfo->sel.daddr.a6, sizeof(xpinfo->sel.daddr.a6));
+        sa_src_addr.len = sa_dst_addr.len = 16;
+
+        proxy_ip_addr = &ipsec_listen_ip_addr6;
+    } else if(xpinfo->sel.family == AF_INET) {
+        sa_src_addr.u.addr32[0] = xpinfo->sel.saddr.a4;
+        sa_dst_addr.u.addr32[0] = xpinfo->sel.daddr.a4;
+        sa_src_addr.len = sa_dst_addr.len = 4;
+
+        proxy_ip_addr = &ipsec_listen_ip_addr;
+    } else {
+        LM_CRIT("Unknown AF %d\n", xpinfo->sel.family);
+        return MNL_CB_OK;
+    }
+
+    sa_sport = htons(xpinfo->sel.sport);
+    sa_dport = htons(xpinfo->sel.dport);
+
+    char saddr_buf[IP_ADDR_MAX_STRZ_SIZE];
+    char daddr_buf[IP_ADDR_MAX_STRZ_SIZE];
+    memset(saddr_buf, 0, IP_ADDR_MAX_STRZ_SIZE);
+    memset(daddr_buf, 0, IP_ADDR_MAX_STRZ_SIZE);
+    ip_addr2sbufz(&sa_src_addr, saddr_buf, IP_ADDR_MAX_STRZ_SIZE);
+    ip_addr2sbufz(&sa_dst_addr, daddr_buf, IP_ADDR_MAX_STRZ_SIZE);
+
+//  * +-----------------+---------------+------+------+------+------+-------+-------+-------+-------+
+//  * |received host.len|received host.s|spi_uc|spi_us|spi_pc|spi_ps|port_uc|port_us|port_pc|port_ps|
+//  * +-----------------+---------------+------+------+------+------+-------+-------+-------+-------+
+//  * |received host.len|received host.s|spi_uc|spi_us|spi_pc|spi_ps|port_uc|port_us|port_pc|port_ps|
+//  * +-----------------+---------------+------+------+------+------+-------+-------+-------+-------+
+//  * |.............................................................................................|
+//  * +-----------------+---------------+------+------+------+------+-------+-------+-------+-------+
+//  * |0000|
+//  * +----+
+    void  *cp;
+    str received_host;
+    ip_addr_t received_host_addr;
+    ipsec_t ipsec;
+
+    cp = ((struct del_tunnels*)data)->contacts;
+    while (1) {
+        memcpy(&(received_host.len), cp, sizeof(received_host.len));
+        if (received_host.len == 0) {
+            break; // no more records
+        }
+
+        received_host.s = (char*)cp + sizeof(received_host.len);
+        cp = (char*)cp + sizeof(received_host.len) + received_host.len;
+
+        memcpy(&ipsec.spi_uc, cp, sizeof(ipsec.spi_uc));
+        cp = (char*)cp + sizeof(ipsec.spi_uc);
+
+        memcpy(&ipsec.spi_us, cp, sizeof(ipsec.spi_us));
+        cp = (char*)cp + sizeof(ipsec.spi_us);
+
+        memcpy(&ipsec.spi_pc, cp, sizeof(ipsec.spi_pc));
+        cp = (char*)cp + sizeof(ipsec.spi_pc);
+
+        memcpy(&ipsec.spi_ps, cp, sizeof(ipsec.spi_ps));
+        cp = (char*)cp + sizeof(ipsec.spi_ps);
+
+        memcpy(&ipsec.port_uc, cp, sizeof(ipsec.port_uc));
+        cp = (char*)cp + sizeof(ipsec.port_uc);
+
+        memcpy(&ipsec.port_us, cp, sizeof(ipsec.port_us));
+        cp = (char*)cp + sizeof(ipsec.port_us);
+
+        memcpy(&ipsec.port_pc, cp, sizeof(ipsec.port_pc));
+        cp = (char*)cp + sizeof(ipsec.port_pc);
+
+        memcpy(&ipsec.port_ps, cp, sizeof(ipsec.port_ps));
+        cp = (char*)cp + sizeof(ipsec.port_ps);
+
+        // convert 'received host' ip string to ip_addr_t
+        if (str2ipxbuf(&received_host, &received_host_addr) < 0){
+            LM_ERR("Unable to convert received host [%.*s]\n", received_host.len, received_host.s);
+            return 1;
+        }
+
+        // Policy  Src address  Dst address Src port    Dst port
+        // Policy1 UE-IP        Proxy-IP    UE-Server   Proxy-Client
+        // Policy2 UE-IP        Proxy-IP    UE-Client   Proxy-Server
+        // Policy3 Proxy-IP     UE-IP       P-Server    UE-Client
+        // Policy4 Proxy-IP     UE-IP       P-Client    UE-Server
+
+        // Check for Policy1 or Policy2
+        if(ip_addr_cmp(&sa_src_addr, &received_host_addr) && ip_addr_cmp(&sa_dst_addr, proxy_ip_addr)) {
+            // check for SA1
+            if(sa_sport == ipsec.port_us && sa_dport == ipsec.port_pc){
+                return MNL_CB_OK;
+            }
+
+            // check for SA2
+            if(sa_sport == ipsec.port_uc && sa_dport == ipsec.port_ps){
+                return MNL_CB_OK;
+            }
+        }
+
+        // Check for Policy3 or Policy4
+        if(ip_addr_cmp(&sa_src_addr, proxy_ip_addr) && ip_addr_cmp(&sa_dst_addr, &received_host_addr)) {
+            // check for SA3
+            if(sa_sport == ipsec.port_ps && sa_dport == ipsec.port_uc){
+                return MNL_CB_OK;
+            }
+
+            // check for SA4
+            if(sa_sport == ipsec.port_pc && sa_dport == ipsec.port_us){
+                return MNL_CB_OK;
+            }
+        }
+    }
+
+    LM_DBG("The POLICY is not used and will be deleted: saddr:%s:%u | daddr:%s:%u\n", saddr_buf, sa_sport, daddr_buf, sa_dport);
+
+    struct xfrm_buffer* delmsg_buf = &((struct del_tunnels*)data)->delmsg_buf;
+    uint32_t new_delmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id));
+
+    if(delmsg_buf->offset + new_delmsg_len > sizeof(delmsg_buf->buf)/sizeof(delmsg_buf->buf[0])) {
+        LM_ERR("Not enough memory allocated for delete policies netlink command\n");
+        return MNL_CB_ERROR;
+    }
+
+    struct nlmsghdr *new_delmsg = (struct nlmsghdr *)&delmsg_buf->buf[delmsg_buf->offset];
+    new_delmsg->nlmsg_len = new_delmsg_len;
+    new_delmsg->nlmsg_flags = NLM_F_REQUEST;
+    new_delmsg->nlmsg_type = XFRM_MSG_DELPOLICY;
+    new_delmsg->nlmsg_seq = time(NULL);
+
+    struct xfrm_userpolicy_id *xpid = NLMSG_DATA(new_delmsg);
+    memcpy(&xpid->sel, &xpinfo->sel, sizeof(xpid->sel));
+    xpid->dir = xpinfo->dir;
+    xpid->index = xpinfo->index;
+
+    delmsg_buf->offset += new_delmsg->nlmsg_len;
+
+    return MNL_CB_OK;
+}
+
+static int delete_unused_policy(struct mnl_socket *mnl_socket, void *contacts)
+{
+    struct {
+        struct nlmsghdr n;
+        //char buf[NLMSG_DELETEALL_BUF_SIZE];
+    } req = {
+        .n.nlmsg_len = NLMSG_HDRLEN,
+        .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+        .n.nlmsg_type = XFRM_MSG_GETPOLICY,
+        .n.nlmsg_seq = time(NULL),
+    };
+
+    if(mnl_socket_sendto(mnl_socket, &req, req.n.nlmsg_len) == -1) {
+        LM_ERR("Error sending get all POLICY command via netlink socket: %s\n", strerror(errno));
+        return 1;
+    }
+
+    char buf[NLMSG_DELETEALL_BUF_SIZE];
+    memset(&buf, 0, sizeof(buf));
+
+    struct del_tunnels del_data;
+    del_data.contacts = contacts;
+    memset(&del_data.delmsg_buf, 0, sizeof(del_data.delmsg_buf));
+
+    int ret = mnl_socket_recvfrom(mnl_socket, buf, sizeof(buf));
+    while (ret > 0) {
+        ret = mnl_cb_run(buf, ret, req.n.nlmsg_seq, mnl_socket_get_portid(mnl_socket), delete_unused_policy_cb, &del_data);
+        if (ret <= MNL_CB_STOP) {
+            break;
+        }
+
+        ret = mnl_socket_recvfrom(mnl_socket, buf, sizeof(buf));
+    }
+
+    // DELETE unused POLICIES
+    if(mnl_socket_sendto(mnl_socket, &del_data.delmsg_buf.buf, del_data.delmsg_buf.offset) == -1) {
+        LM_ERR("Error sending delete unused policies command via netlink socket: %s\n", strerror(errno));
+        return 1;
+    }
+
+    return 0;
+}
+
+int delete_unused_tunnels()
+{
+    int rval, len = 0;
+    void *buf = NULL;
+
+    // first try to fetch required size for all contacts
+    rval = ul.get_all_ucontacts(buf, len, 0, 0, 1);
+    LM_DBG("Minimum required size %d\n", rval);
+    if (rval < 0) {
+        LM_ERR("Failed to fetch contacts\n");
+        return 1;
+    }
+
+    if (rval > 0) {
+        len = rval * 2;
+        buf = malloc(len);
+        if (buf == NULL) {
+            LM_ERR("Out of memory\n");
+            return 1;
+        }
+
+        rval = ul.get_all_ucontacts(buf, len, 0, 0, 1);
+        if (rval != 0) {
+            free(buf);
+            return 1;
+        }
+    }
+    if (buf == NULL) {
+        return 1;
+    }
+
+    struct mnl_socket* sock = init_mnl_socket();
+    if (sock == NULL) {
+        LM_ERR("Can't init mnl socket\n");
+        free(buf);
+        return 1;
+    }
+
+    delete_unused_sa(sock, buf);
+    delete_unused_policy(sock, buf);
+
+    close_mnl_socket(sock);
+
+    free(buf);
+    return 0;
+}

+ 2 - 0
src/modules/ims_ipsec_pcscf/ipsec.h

@@ -49,4 +49,6 @@ int remove_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_ad
 int clean_sa(struct mnl_socket*  mnl_socket);
 int clean_policy(struct mnl_socket*  mnl_socket);
 
+int delete_unused_tunnels();
+
 #endif /* IMS_IPSEC_PCSCF_IPSEC */