From 44cd10a5fa5f51f079b0d47a544e8d0bf4eb43a5 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Wed, 13 Feb 2019 08:56:14 +0000 Subject: [PATCH] ims_ipsec_pcscf: IPv6 support 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 | 86 +++++++++++++++++-- src/modules/ims_ipsec_pcscf/ipsec.c | 127 +++++++++++----------------- src/modules/ims_ipsec_pcscf/ipsec.h | 5 +- 3 files changed, 132 insertions(+), 86 deletions(-) diff --git a/src/modules/ims_ipsec_pcscf/cmd.c b/src/modules/ims_ipsec_pcscf/cmd.c index 1a87b9df9ce..317948adef5 100644 --- a/src/modules/ims_ipsec_pcscf/cmd.c +++ b/src/modules/ims_ipsec_pcscf/cmd.c @@ -53,6 +53,8 @@ #include #include +#include + 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; } diff --git a/src/modules/ims_ipsec_pcscf/ipsec.c b/src/modules/ims_ipsec_pcscf/ipsec.c index 3b0c1499871..05c8dd88d52 100644 --- a/src/modules/ims_ipsec_pcscf/ipsec.c +++ b/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, 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) diff --git a/src/modules/ims_ipsec_pcscf/ipsec.h b/src/modules/ims_ipsec_pcscf/ipsec.h index 71390f1d8b1..06e98c50df8 100644 --- a/src/modules/ims_ipsec_pcscf/ipsec.h +++ b/src/modules/ims_ipsec_pcscf/ipsec.h @@ -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);