ims_ipsec_pcscf: IPv6 support
authorTsvetomir Dimitrov <tsv.dimitrov@gmail.com>
Wed, 13 Feb 2019 08:56:14 +0000 (08:56 +0000)
committerTsvetomir Dimitrov <tsv.dimitrov@gmail.com>
Tue, 26 Feb 2019 11:14:08 +0000 (11:14 +0000)
The parameter ipsec_listen_addr of the module can be set to either IPv4
or IPv6 address. All xfrm related code is reworked to handle both type
of addresses.

Note: At the moment it is not possible to use both IPv4 and IPv6 at the
same time for IPSec. The implementation allows it, but additional config
parameters should be added in order to make this work.

src/modules/ims_ipsec_pcscf/cmd.c
src/modules/ims_ipsec_pcscf/ipsec.c
src/modules/ims_ipsec_pcscf/ipsec.h

index 1a87b9d..317948a 100644 (file)
@@ -53,6 +53,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <arpa/inet.h>
+
 
 extern str ipsec_listen_addr;
 extern short ipsec_listen_port;
@@ -269,24 +271,82 @@ static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m)
     return 0;
 }
 
-static int create_ipsec_tunnel(const str remote_addr, unsigned short proto, ipsec_t* s)
+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)
 {
     struct mnl_socket* sock = init_mnl_socket();
     if (sock == NULL) {
         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",
+    //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;
+    }
+
+    //Convert to char* for logging
+    char remote_addr_str[128];
+    memset(remote_addr_str, 0, sizeof(remote_addr_str));
+    if(inet_ntop(remote_addr->af, remote_addr->u.addr, remote_addr_str, sizeof(remote_addr_str)) == NULL) {
+        LM_CRIT("Error converting remote IP address: %s\n", strerror(errno));
+        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.len, remote_addr.s, s->port_uc, s->port_us);
+            remote_addr_str, s->port_uc, s->port_us);
 
     // P-CSCF 'client' tunnel to UE 'server'
-    add_sa    (sock, proto, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, s->ck, s->ik);
-    add_policy(sock, proto,  ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
+    add_sa    (sock, proto, &ipsec_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, s->ck, s->ik);
+    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_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, s->ck, s->ik);
-    add_policy(sock, proto, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
+    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);
 
     close_mnl_socket(sock);
 
@@ -451,7 +511,17 @@ int ipsec_create(struct sip_msg* m, udomain_t* d)
         goto cleanup;
     }
 
-    if(create_ipsec_tunnel(ci.received_host, ci.received_proto, s) != 0) {
+    // Get request from reply
+    struct cell *t = tmb.t_gett();
+    if (!t || t == (void*) -1) {
+        LM_ERR("fill_contact(): Reply without transaction\n");
+        return -1;
+    }
+
+    struct sip_msg* req = t->uas.request;
+    ////
+
+    if(create_ipsec_tunnel(&req->rcv.src_ip, ci.received_proto, s) != 0) {
         goto cleanup;
     }
 
index 3b0c149..05c8dd8 100644 (file)
@@ -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, str src_addr_param, str 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)
 {
     char l_msg_buf[MNL_SOCKET_BUFFER_SIZE];
     char l_auth_algo_buf[XFRM_TMPLS_BUF_SIZE];
@@ -120,8 +120,6 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, str src_addr_param,
     struct xfrm_algo* l_auth_algo = NULL;
     struct xfrm_algo* l_enc_algo  = NULL;
 
-    char* src_addr = NULL;
-    char* dest_addr = NULL;
 
     memset(l_msg_buf, 0, sizeof(l_msg_buf));
     memset(l_auth_algo_buf, 0, sizeof(l_auth_algo_buf));
@@ -133,24 +131,6 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, str src_addr_param,
         return -1;
     }
 
-    // convert input IP addresses and keys to char*
-    if((src_addr = pkg_malloc(src_addr_param.len+1)) == NULL) {
-        LM_ERR("Error allocating memory for src addr during SA creation\n");
-        return -2;
-    }
-
-    if((dest_addr = pkg_malloc(dest_addr_param.len+1)) == NULL) {
-        pkg_free(src_addr);
-        LM_ERR("Error allocating memory for dest addr during SA creation\n");
-        return -3;
-    }
-
-    memset(src_addr, 0, src_addr_param.len+1);
-    memset(dest_addr, 0, dest_addr_param.len+1);
-
-    memcpy(src_addr, src_addr_param.s, src_addr_param.len);
-    memcpy(dest_addr, dest_addr_param.s, dest_addr_param.len);
-
     // nlmsghdr initialization
     l_nlh = mnl_nlmsg_put_header(l_msg_buf);
     l_nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
@@ -160,20 +140,34 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, str src_addr_param,
 
     // add Security association
     l_xsainfo = (struct xfrm_usersa_info*)mnl_nlmsg_put_extra_header(l_nlh, sizeof(struct xfrm_usersa_info));
-    l_xsainfo->sel.family       = AF_INET;
-    l_xsainfo->sel.daddr.a4     = inet_addr(dest_addr);
-    l_xsainfo->sel.saddr.a4     = inet_addr(src_addr);
+    l_xsainfo->sel.family       = dest_addr_param->af;
+    if(dest_addr_param->af == AF_INET6) {
+        memcpy(l_xsainfo->sel.daddr.a6, dest_addr_param->u.addr32, sizeof(l_xsainfo->sel.daddr.a6));
+        memcpy(l_xsainfo->sel.saddr.a6, src_addr_param->u.addr32, sizeof(l_xsainfo->sel.saddr.a6));
+        l_xsainfo->sel.prefixlen_d  = 128;
+        l_xsainfo->sel.prefixlen_s  = 128;
+    }
+    else {
+        l_xsainfo->sel.daddr.a4     = dest_addr_param->u.addr32[0];
+        l_xsainfo->sel.saddr.a4     = src_addr_param->u.addr32[0];
+        l_xsainfo->sel.prefixlen_d  = 32;
+        l_xsainfo->sel.prefixlen_s  = 32;
+    }
     l_xsainfo->sel.dport        = htons(d_port);
     l_xsainfo->sel.dport_mask   = 0xFFFF;
-    l_xsainfo->sel.prefixlen_d  = 32;
     l_xsainfo->sel.sport        = htons(s_port);
     l_xsainfo->sel.sport_mask   = 0xFFFF;
-    l_xsainfo->sel.prefixlen_s  = 32;
-    l_xsainfo->sel.proto        = sel_proto;
+    //l_xsainfo->sel.proto        = sel_proto;
     l_xsainfo->sel.user         = htonl(xfrm_user_selector);
 
-    l_xsainfo->saddr.a4         = inet_addr(src_addr);
-    l_xsainfo->id.daddr.a4      = inet_addr(dest_addr);
+    if(dest_addr_param->af == AF_INET6) {
+        memcpy(l_xsainfo->id.daddr.a6, dest_addr_param->u.addr32, sizeof(l_xsainfo->id.daddr.a6));
+        memcpy(l_xsainfo->saddr.a6, src_addr_param->u.addr32, sizeof(l_xsainfo->saddr.a6));
+    }
+    else {
+        l_xsainfo->id.daddr.a4      = dest_addr_param->u.addr32[0];
+        l_xsainfo->saddr.a4         = src_addr_param->u.addr32[0];
+    }
     l_xsainfo->id.spi           = htonl(id);
     l_xsainfo->id.proto         = IPPROTO_ESP;
 
@@ -182,14 +176,10 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, str src_addr_param,
     l_xsainfo->lft.soft_packet_limit    = XFRM_INF;
     l_xsainfo->lft.hard_packet_limit    = XFRM_INF;
     l_xsainfo->reqid                    = id;
-    l_xsainfo->family                   = AF_INET;
+    l_xsainfo->family                   = dest_addr_param->af;
     l_xsainfo->mode                     = XFRM_MODE_TRANSPORT;
     l_xsainfo->replay_window            = 32;
-    l_xsainfo->flags                    = XFRM_STATE_NOECN;
-
-    // char* ip addresses are no longer needed - free them
-    pkg_free(src_addr);
-    pkg_free(dest_addr);
+    //l_xsainfo->flags                    = XFRM_STATE_NOECN;
 
     // Add authentication algorithm for this SA
 
@@ -281,7 +271,7 @@ 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, 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, 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)
 {
     char                            l_msg_buf[MNL_SOCKET_BUFFER_SIZE];
     char                            l_tmpls_buf[XFRM_TMPLS_BUF_SIZE];
@@ -294,30 +284,9 @@ int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_addr
         return -1;
     }
 
-    char* src_addr = NULL;
-    char* dest_addr = NULL;
-
     memset(l_msg_buf, 0, sizeof(l_msg_buf));
     memset(l_tmpls_buf, 0, sizeof(l_tmpls_buf));
 
-    // convert input IP addresses to char*
-    if((src_addr = pkg_malloc(src_addr_param.len+1)) == NULL) {
-        LM_ERR("Error allocating memory for src addr during Policy creation\n");
-        return -1;
-    }
-
-    if((dest_addr = pkg_malloc(dest_addr_param.len+1)) == NULL) {
-        pkg_free(src_addr);
-        LM_ERR("Error allocating memory for dest addr during Policy creation\n");
-        return -2;
-    }
-
-    memset(src_addr, 0, src_addr_param.len+1);
-    memset(dest_addr, 0, dest_addr_param.len+1);
-
-    memcpy(src_addr, src_addr_param.s, src_addr_param.len);
-    memcpy(dest_addr, dest_addr_param.s, dest_addr_param.len);
-
     // nlmsghdr initialization
     l_nlh = mnl_nlmsg_put_header(l_msg_buf);
     l_nlh->nlmsg_flags  = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
@@ -327,16 +296,24 @@ int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_addr
 
     // add OUT policy
     l_xpinfo = (struct xfrm_userpolicy_info*)mnl_nlmsg_put_extra_header(l_nlh, sizeof(struct xfrm_userpolicy_info));
-    l_xpinfo->sel.family        = AF_INET;
-    l_xpinfo->sel.daddr.a4      = inet_addr(dest_addr);
-    l_xpinfo->sel.saddr.a4      = inet_addr(src_addr);
+    l_xpinfo->sel.family        = dest_addr_param->af;
+    if(dest_addr_param->af == AF_INET6) {
+        memcpy(l_xpinfo->sel.daddr.a6, dest_addr_param->u.addr32, sizeof(l_xpinfo->sel.daddr.a6));
+        memcpy(l_xpinfo->sel.saddr.a6, src_addr_param->u.addr32, sizeof(l_xpinfo->sel.saddr.a6));
+        l_xpinfo->sel.prefixlen_d  = 128;
+        l_xpinfo->sel.prefixlen_s  = 128;
+    }
+    else {
+        l_xpinfo->sel.daddr.a4     = dest_addr_param->u.addr32[0];
+        l_xpinfo->sel.saddr.a4     = src_addr_param->u.addr32[0];
+        l_xpinfo->sel.prefixlen_d  = 32;
+        l_xpinfo->sel.prefixlen_s  = 32;
+    }
     l_xpinfo->sel.dport         = htons(dst_port);
     l_xpinfo->sel.dport_mask    = 0xFFFF;
-    l_xpinfo->sel.prefixlen_d   = 32;
     l_xpinfo->sel.sport         = htons(src_port);
     l_xpinfo->sel.sport_mask    = 0xFFFF;
-    l_xpinfo->sel.prefixlen_s   = 32;
-    l_xpinfo->sel.proto         = sel_proto;
+    //l_xpinfo->sel.proto         = sel_proto;
     l_xpinfo->sel.user          = htonl(xfrm_user_selector);
 
     l_xpinfo->lft.soft_byte_limit   = XFRM_INF;
@@ -355,38 +332,36 @@ int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_addr
     }
     else {
         LM_ERR("Invalid direction parameter passed to add_policy: %d\n", dir);
-        pkg_free(src_addr);
-        pkg_free(dest_addr);
 
         return -3;
     }
 
-
     // xfrm_user_tmpl initialization
     struct xfrm_user_tmpl* l_tmpl = (struct xfrm_user_tmpl*)l_tmpls_buf;
     l_tmpl->id.proto    = IPPROTO_ESP;
-    l_tmpl->family      = AF_INET;
+    l_tmpl->family      = dest_addr_param->af;
+    if(dest_addr_param->af == AF_INET6) {
+        memcpy(l_tmpl->id.daddr.a6, dest_addr_param->u.addr32, sizeof(l_tmpl->id.daddr.a6));
+        memcpy(l_tmpl->saddr.a6, src_addr_param->u.addr32, sizeof(l_tmpl->saddr.a6));
+    }
+    else {
+        l_tmpl->id.daddr.a4        = dest_addr_param->u.addr32[0];
+        l_tmpl->saddr.a4           = src_addr_param->u.addr32[0];
+    }
     l_tmpl->reqid       = p_id;
     l_tmpl->mode        = XFRM_MODE_TRANSPORT;
     l_tmpl->aalgos      = (~(__u32)0);
     l_tmpl->ealgos      = (~(__u32)0);
     l_tmpl->calgos      = (~(__u32)0);
 
-
     mnl_attr_put(l_nlh, XFRMA_TMPL, sizeof(struct xfrm_user_tmpl), l_tmpl);
 
     if(mnl_socket_sendto(mnl_socket, l_nlh, l_nlh->nlmsg_len) < 0)
     {
-        pkg_free(src_addr);
-        pkg_free(dest_addr);
         LM_ERR("Failed to send Netlink message, error: %s\n", strerror(errno));
         return -4;
     }
 
-    // char* ip addresses are no longer needed - free them
-    pkg_free(src_addr);
-    pkg_free(dest_addr);
-
     return 0;
 }
 
@@ -449,8 +424,8 @@ int remove_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_a
         .xpid.sel.prefixlen_d   = 32,
         .xpid.sel.sport         = htons(src_port),
         .xpid.sel.sport_mask    = 0xFFFF,
-        .xpid.sel.prefixlen_s   = 32,
-        .xpid.sel.proto         = sel_proto
+        .xpid.sel.prefixlen_s   = 32//,
+        //.xpid.sel.proto         = sel_proto
     };
 
     if(mnl_socket_sendto(mnl_socket, &req.n, req.n.nlmsg_len) < 0)
index 71390f1..06e98c5 100644 (file)
@@ -26,6 +26,7 @@
 #define IMS_IPSEC_PCSCF_IPSEC
 
 #include "../../core/str.h"
+#include "../../core/ip_addr.h"
 
 
 struct mnl_socket;
@@ -41,10 +42,10 @@ void close_mnl_socket(struct mnl_socket* sock);
 unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto);
 
 
-int add_sa(struct mnl_socket* nl_sock,  unsigned short proto, str src_addr_param, str 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);
 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, 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, 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 clean_sa(struct mnl_socket*  mnl_socket);