diff --git a/src/modules/ims_ipsec_pcscf/cmd.c b/src/modules/ims_ipsec_pcscf/cmd.c index f696ac9e715..db7261830e9 100644 --- a/src/modules/ims_ipsec_pcscf/cmd.c +++ b/src/modules/ims_ipsec_pcscf/cmd.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com + * Copyright (C) 2019 Aleksandar Yosifov * * The initial version of this code was written by Dragos Vingarzan * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the @@ -49,6 +50,9 @@ #include "ipsec.h" #include "spi_gen.h" +#include "port_gen.h" +#include "cmd.h" +#include "sec_agree.h" #include #include @@ -58,9 +62,12 @@ extern str ipsec_listen_addr; extern str ipsec_listen_addr6; -extern short ipsec_listen_port; -extern short ipsec_server_port; -extern short ipsec_client_port; +extern int ipsec_server_port; +extern int ipsec_client_port; + +extern int spi_id_start; + +extern unsigned int init_flag; // check http://www.asipto.com/pub/kamailio-devel-guide/#c16return_values const int IPSEC_CMD_FAIL = -1; @@ -69,6 +76,20 @@ const int IPSEC_CMD_SUCCESS = 1; extern usrloc_api_t ul; extern struct tm_binds tmb; +int bind_ipsec_pcscf(ipsec_pcscf_api_t* api) { + if(!api){ + LM_ERR("invalid parameter value\n"); + return -1; + } + if(init_flag == 0){ + LM_ERR("configuration error - trying to bind to ipsec pscscf module before being initialized\n"); + return -1; + } + + api->ipsec_on_expire = ipsec_on_expire; + + return 0; +} static str get_www_auth_param(const char* param_name, str www_auth) { @@ -139,6 +160,7 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m) ci->via_port = uri.port_no ? uri.port_no : 5060; ci->via_prot = 0; ci->aor = m->first_line.u.request.uri; + ci->searchflag = SEARCH_NORMAL; req = m; } @@ -168,6 +190,7 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m) ci->via_port = vb->port; ci->via_prot = vb->proto; ci->aor = cb->contacts->uri; + ci->searchflag = SEARCH_RECEIVED; } else { LM_ERR("Unknown first line type: %d\n", m->first_line.type); @@ -270,10 +293,23 @@ static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m) return -1; } + s->port_pc = acquire_cport(); + s->port_ps = acquire_sport(); + + if(s->port_pc == 0){ + LM_ERR("No free client port for IPSEC tunnel creation\n"); + return -1; + } + + if(s->port_ps == 0){ + LM_ERR("No free server port for IPSEC tunnel creation\n"); + return -1; + } + return 0; } -static int create_ipsec_tunnel(const struct ip_addr *remote_addr, unsigned short proto, ipsec_t* s) +static int create_ipsec_tunnel(const struct ip_addr *remote_addr, ipsec_t* s) { struct mnl_socket* sock = init_mnl_socket(); if (sock == NULL) { @@ -306,74 +342,91 @@ static int create_ipsec_tunnel(const struct ip_addr *remote_addr, unsigned short return -1; } - LM_DBG("Creating security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %s; client port %d server port %d; spi_ps %u, spi_pc %u, spi_us %u, spi_uc %u\n", + LM_DBG("Creating security associations: Local IP: %.*s port_pc: %d port_ps: %d; UE IP: %s; port_uc %d port_us %d; spi_pc %u, spi_ps %u, spi_uc %u, spi_us %u\n", remote_addr->af == AF_INET ? ipsec_listen_addr.len : ipsec_listen_addr6.len, remote_addr->af == AF_INET ? ipsec_listen_addr.s : ipsec_listen_addr6.s, - ipsec_client_port, ipsec_server_port, remote_addr_str, s->port_uc, s->port_us, s->spi_ps, s->spi_pc, s->spi_us, s->spi_uc); + s->port_pc, s->port_ps, remote_addr_str, s->port_uc, s->port_us, s->spi_pc, s->spi_ps, s->spi_uc, s->spi_us); // SA1 UE client to P-CSCF server - // src adrr dst addr src port dst port - add_sa (sock, proto, remote_addr, &ipsec_addr, s->port_uc, ipsec_server_port, s->spi_ps, s->ck, s->ik, s->r_alg); - add_policy(sock, proto, remote_addr, &ipsec_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN); + // src adrr dst addr src port dst port + add_sa (sock, remote_addr, &ipsec_addr, s->port_uc, s->port_ps, s->spi_ps, s->ck, s->ik, s->r_alg); + add_policy(sock, remote_addr, &ipsec_addr, s->port_uc, s->port_ps, s->spi_ps, IPSEC_POLICY_DIRECTION_IN); // SA2 P-CSCF client to UE server - // src adrr dst addr src port dst port - add_sa (sock, proto, &ipsec_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, s->ck, s->ik, s->r_alg); - add_policy(sock, proto, &ipsec_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT); + // src adrr dst addr src port dst port + add_sa (sock, &ipsec_addr, remote_addr, s->port_pc, s->port_us, s->spi_us, s->ck, s->ik, s->r_alg); + add_policy(sock, &ipsec_addr, remote_addr, s->port_pc, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT); // SA3 P-CSCF server to UE client - // src adrr dst addr src port dst port - add_sa (sock, proto, &ipsec_addr, remote_addr, ipsec_server_port, s->port_uc, s->spi_uc, s->ck, s->ik, s->r_alg); - add_policy(sock, proto, &ipsec_addr, remote_addr, ipsec_server_port, s->port_uc, s->spi_uc, IPSEC_POLICY_DIRECTION_OUT); + // src adrr dst addr src port dst port + add_sa (sock, &ipsec_addr, remote_addr, s->port_ps, s->port_uc, s->spi_uc, s->ck, s->ik, s->r_alg); + add_policy(sock, &ipsec_addr, remote_addr, s->port_ps, s->port_uc, s->spi_uc, IPSEC_POLICY_DIRECTION_OUT); // SA4 UE server to P-CSCF client - // src adrr dst addr src port dst port - add_sa (sock, proto, remote_addr, &ipsec_addr, s->port_us, ipsec_client_port, s->spi_pc, s->ck, s->ik, s->r_alg); - add_policy(sock, proto, remote_addr, &ipsec_addr, s->port_us, ipsec_client_port, s->spi_pc, IPSEC_POLICY_DIRECTION_IN); + // src adrr dst addr src port dst port + add_sa (sock, remote_addr, &ipsec_addr, s->port_us, s->port_pc, s->spi_pc, s->ck, s->ik, s->r_alg); + add_policy(sock, remote_addr, &ipsec_addr, s->port_us, s->port_pc, s->spi_pc, IPSEC_POLICY_DIRECTION_IN); close_mnl_socket(sock); return 0; } -static int destroy_ipsec_tunnel(const str remote_addr, unsigned short proto, ipsec_t* s) +static int destroy_ipsec_tunnel(str remote_addr, ipsec_t* s, unsigned short received_port) { struct mnl_socket* sock = init_mnl_socket(); if (sock == NULL) { return -1; } - // TODO: pass ipsec listen address v4 or v6 to destroy the tunnel + ip_addr_t ip_addr; + str ipsec_addr; + + // convert 'remote_addr' ip string to ip_addr_t + if(str2ipxbuf(&remote_addr, &ip_addr) < 0){ + LM_ERR("Unable to convert remote address [%.*s]\n", remote_addr.len, remote_addr.s); + return -1; + } + + if(ip_addr.af == AF_INET6){ + ipsec_addr = ipsec_listen_addr6; + }else{ + ipsec_addr = ipsec_listen_addr; + } - LM_DBG("Destroying security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d\n", - ipsec_listen_addr.len, ipsec_listen_addr.s, ipsec_client_port, ipsec_server_port, - remote_addr.len, remote_addr.s, s->port_uc, s->port_us); + LM_DBG("Destroying security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d; spi_ps %u, spi_pc %u, spi_us %u, spi_uc %u\n", + ipsec_addr.len, ipsec_addr.s, s->port_pc, s->port_ps, + remote_addr.len, remote_addr.s, s->port_uc, s->port_us, s->spi_ps, s->spi_pc, s->spi_us, s->spi_uc); // SA1 UE client to P-CSCF server - remove_sa (sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps); - remove_policy(sock, proto, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN); + remove_sa (sock, remote_addr, ipsec_addr, s->port_uc, s->port_ps, s->spi_ps, ip_addr.af); + remove_policy(sock, remote_addr, ipsec_addr, s->port_uc, s->port_ps, s->spi_ps, ip_addr.af, IPSEC_POLICY_DIRECTION_IN); // SA2 P-CSCF client to UE server - remove_sa (sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us); - remove_policy(sock, proto, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT); + remove_sa (sock, ipsec_addr, remote_addr, s->port_pc, s->port_us, s->spi_us, ip_addr.af); + remove_policy(sock, ipsec_addr, remote_addr, s->port_pc, s->port_us, s->spi_us, ip_addr.af, IPSEC_POLICY_DIRECTION_OUT); // SA3 P-CSCF server to UE client - remove_sa (sock, ipsec_listen_addr, remote_addr, ipsec_server_port, s->port_uc, s->spi_uc); - remove_policy(sock, proto, ipsec_listen_addr, remote_addr, ipsec_server_port, s->port_uc, s->spi_uc, IPSEC_POLICY_DIRECTION_OUT); + remove_sa (sock, ipsec_addr, remote_addr, s->port_ps, s->port_uc, s->spi_uc, ip_addr.af); + remove_policy(sock, ipsec_addr, remote_addr, s->port_ps, s->port_uc, s->spi_uc, ip_addr.af, IPSEC_POLICY_DIRECTION_OUT); // SA4 UE server to P-CSCF client - remove_sa (sock, remote_addr, ipsec_listen_addr, s->port_us, ipsec_client_port, s->spi_pc); - remove_policy(sock, proto, remote_addr, ipsec_listen_addr, s->port_us, ipsec_client_port, s->spi_pc, IPSEC_POLICY_DIRECTION_IN); + remove_sa (sock, remote_addr, ipsec_addr, s->port_us, s->port_pc, s->spi_pc, ip_addr.af); + remove_policy(sock, remote_addr, ipsec_addr, s->port_us, s->port_pc, s->spi_pc, ip_addr.af, IPSEC_POLICY_DIRECTION_IN); // Release SPIs - release_spi(s->spi_uc); - release_spi(s->spi_us); + release_spi(s->spi_pc); + release_spi(s->spi_ps); + + // Release the client and the server ports + release_cport(s->port_pc); + release_sport(s->port_ps); close_mnl_socket(sock); return 0; } -static void on_expire(struct pcontact *c, int type, void *param) +void ipsec_on_expire(struct pcontact *c, int type, void *param) { if(type != PCSCF_CONTACT_EXPIRE && type != PCSCF_CONTACT_DELETE) { LM_ERR("Unexpected event type %d\n", type); @@ -392,7 +445,7 @@ static void on_expire(struct pcontact *c, int type, void *param) return; } - destroy_ipsec_tunnel(c->received_host, c->received_proto, c->security_temp->data.ipsec); + destroy_ipsec_tunnel(c->received_host, c->security_temp->data.ipsec, c->contact_port); } int add_supported_secagree_header(struct sip_msg* m) @@ -441,7 +494,7 @@ int add_security_server_header(struct sip_msg* m, ipsec_t* s) memset(sec_hdr_buf, 0, sizeof(sec_hdr_buf)); sec_header->len = snprintf(sec_hdr_buf, sizeof(sec_hdr_buf) - 1, "Security-Server: ipsec-3gpp;prot=esp;mod=trans;spi-c=%d;spi-s=%d;port-c=%d;port-s=%d;alg=%.*s;ealg=%.*s\r\n", - s->spi_pc, s->spi_ps, ipsec_client_port, ipsec_server_port, + s->spi_pc, s->spi_ps, s->port_pc, s->port_ps, s->r_alg.len, s->r_alg.s, s->r_ealg.len, s->r_ealg.s ); @@ -497,12 +550,6 @@ int ipsec_create(struct sip_msg* m, udomain_t* d) goto cleanup; } - ipsec_t* s = pcontact->security_temp->data.ipsec; - - if(update_contact_ipsec_params(s, m) != 0) { - goto cleanup; - } - // Get request from reply struct cell *t = tmb.t_gett(); if (!t || t == (void*) -1) { @@ -511,38 +558,83 @@ int ipsec_create(struct sip_msg* m, udomain_t* d) } struct sip_msg* req = t->uas.request; - //// - if(create_ipsec_tunnel(&req->rcv.src_ip, ci.received_proto, s) != 0) { - goto cleanup; - } + // Update contacts only for initial registration, for re-registration the existing contacts shouldn't be updated. + if(ci.via_port == SIP_PORT){ + LM_DBG("Registration for contact with AOR [%.*s], VIA [%d://%.*s:%d], received_host [%d://%.*s:%d]\n", + ci.aor.len, ci.aor.s, ci.via_prot, ci.via_host.len, ci.via_host.s, ci.via_port, + ci.received_proto, ci.received_host.len, ci.received_host.s, ci.received_port); - // TODO: Save security_tmp to security!!!!! + ipsec_t* s = pcontact->security_temp->data.ipsec; - if (ul.update_pcontact(d, &ci, pcontact) != 0) { - LM_ERR("Error updating contact\n"); - goto cleanup; - } + if(update_contact_ipsec_params(s, m) != 0) { + goto cleanup; + } - // Update temp security parameters - if(ul.update_temp_security(d, pcontact->security_temp->type, pcontact->security_temp, pcontact) != 0) - { - LM_ERR("Error updating temp security\n"); - } + if(create_ipsec_tunnel(&req->rcv.src_ip, s) != 0){ + goto cleanup; + } - // Destroy the tunnel, if the contact expires - if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, on_expire, NULL) != 1) { - LM_ERR("Error subscribing for contact\n"); - goto cleanup; - } + if (ul.update_pcontact(d, &ci, pcontact) != 0){ + LM_ERR("Error updating contact\n"); + goto cleanup; + } + // Update temp security parameters + if(ul.update_temp_security(d, pcontact->security_temp->type, pcontact->security_temp, pcontact) != 0){ + LM_ERR("Error updating temp security\n"); + } - if(add_supported_secagree_header(m) != 0) { - goto cleanup; - } + if(add_supported_secagree_header(m) != 0) { + goto cleanup; + } - if(add_security_server_header(m, s) != 0) { - goto cleanup; + if(add_security_server_header(m, s) != 0) { + goto cleanup; + } + + if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, ipsec_on_expire, (void*)&pcontact->received_port) != 1) { + LM_ERR("Error subscribing for contact\n"); + goto cleanup; + } + }else{ + LM_DBG("RE-Registration for contact with AOR [%.*s], VIA [%d://%.*s:%d], received_host [%d://%.*s:%d]\n", + ci.aor.len, ci.aor.s, ci.via_prot, ci.via_host.len, ci.via_host.s, ci.via_port, + ci.received_proto, ci.received_host.len, ci.received_host.s, ci.received_port); + + security_t* req_sec_params = NULL; + + // Parse security parameters from the REGISTER request and get some data for the new tunnels + if((req_sec_params = cscf_get_security(req)) == NULL) { + LM_CRIT("No security parameters in REGISTER request\n"); + goto cleanup; + } + + if(update_contact_ipsec_params(req_sec_params->data.ipsec, m) != 0) { + goto cleanup; + } + + if(create_ipsec_tunnel(&req->rcv.src_ip, req_sec_params->data.ipsec) != 0){ + goto cleanup; + } + + // if (ul.update_pcontact(d, &ci, pcontact) != 0){ + // LM_ERR("Error updating contact\n"); + // goto cleanup; + // } + + // // Update temp security parameters + // if(ul.update_temp_security(d, req_sec_params->type, req_sec_params, pcontact) != 0){ + // LM_ERR("Error updating temp security\n"); + // } + + if(add_supported_secagree_header(m) != 0) { + goto cleanup; + } + + if(add_security_server_header(m, req_sec_params->data.ipsec) != 0) { + goto cleanup; + } } ret = IPSEC_CMD_SUCCESS; // all good, set ret to SUCCESS, and exit @@ -633,7 +725,7 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d) dst_proto = req->rcv.proto; // for Reply and TCP sends from P-CSCF server port, for Reply and UDP sends from P-CSCF client port - src_port = dst_proto == PROTO_TCP ? ipsec_server_port : ipsec_client_port; + src_port = dst_proto == PROTO_TCP ? s->port_ps : s->port_pc; // for Reply and TCP sends to UE client port, for Reply and UDP sends to UE server port dst_port = dst_proto == PROTO_TCP ? s->port_uc : s->port_us; @@ -642,7 +734,7 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d) dst_proto = pcontact->received_proto; // for Request sends from P-CSCF client port - src_port = ipsec_client_port; + src_port = s->port_pc; // for Request sends to UE server port dst_port = s->port_us; @@ -742,7 +834,7 @@ int ipsec_destroy(struct sip_msg* m, udomain_t* d) goto cleanup; } - destroy_ipsec_tunnel(ci.received_host, ci.received_proto, pcontact->security_temp->data.ipsec); + destroy_ipsec_tunnel(ci.received_host, pcontact->security_temp->data.ipsec, pcontact->contact_port); ret = IPSEC_CMD_SUCCESS; // all good, set ret to SUCCESS, and exit diff --git a/src/modules/ims_ipsec_pcscf/cmd.h b/src/modules/ims_ipsec_pcscf/cmd.h index d3bbae31cb8..497f97a6285 100644 --- a/src/modules/ims_ipsec_pcscf/cmd.h +++ b/src/modules/ims_ipsec_pcscf/cmd.h @@ -3,6 +3,7 @@ * * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com + * * The initial version of this code was written by Dragos Vingarzan * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the @@ -46,6 +47,16 @@ #ifndef IPSEC_CMD_H #define IPSEC_CMD_H +typedef void (*contact_expired_t)(pcontact_t* c, int type, void* param); + +/*! ipsec pcscf API export structure */ +typedef struct ipsec_pcscf_api { + contact_expired_t ipsec_on_expire; +} ipsec_pcscf_api_t; + +/*! ipsec pcscf API export bind function */ +typedef int (*bind_ipsec_pcscf_t)(ipsec_pcscf_api_t* api); + struct sip_msg; struct udomain_t; @@ -53,5 +64,6 @@ int ipsec_create(struct sip_msg* m, udomain_t* d); int ipsec_forward(struct sip_msg* m, udomain_t* d); int ipsec_destroy(struct sip_msg* m, udomain_t* d); int ipsec_cleanall(); +void ipsec_on_expire(pcontact_t* c, int type, void* param); #endif /* IPSEC_CMD_H */ diff --git a/src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml b/src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml index a73bf0ba1ef..6ef506723bf 100644 --- a/src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml +++ b/src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml @@ -82,9 +82,9 @@ modparam("ims_ipsec_pcscf", "ipsec_listen_addr6", "")
<varname>ipsec_client_port</varname> (int) - Port number which will be bound for incoming (server) IPSec traffic. + Start port number which will be bound for incoming (server) IPSec traffic. - Default value is 5963. + Default value is 5062. <varname>ipsec_client_port</varname> parameter usage @@ -100,7 +100,7 @@ modparam("ims_ipsec_pcscf", "ipsec_client_port", 5062)
<varname>ipsec_server_port</varname> (int) - Port number which will be bound for incoming (server) IPSec traffic. + Start port number which will be bound for incoming (server) IPSec traffic. Default value is 5063. @@ -115,6 +115,26 @@ modparam("ims_ipsec_pcscf", "ipsec_server_port", 5063)
+
+ <varname>ipsec_max_connections</varname> (int) + + Maximum IPSec connections for the process. E.g. if ipsec_client_port=5100, ipsec_server_port=6100 and + ipsec_max_connections=10, all client ports between 5100 and 5109 and all server ports between 6100 and 6109 + will be used for maximum to 10 IPSec connections. + + Default value is 2. + + + <varname>ipsec_max_connections</varname> parameter usage + + +... +modparam("ims_ipsec_pcscf", "ipsec_max_connections", 10) +... + + +
+
<varname>ipsec_spi_id_start</varname> (int) @@ -227,7 +247,7 @@ ipsec_forward("location"); - ipsec_forward + ipsec_destroy ... diff --git a/src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c b/src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c index 4b1d5ec4462..c59552b540f 100644 --- a/src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c +++ b/src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c @@ -2,6 +2,7 @@ * IMS IPSEC PCSCF module * * Copyright (C) 2018 Tsvetomir Dimitrov + * Copyright (C) 2019 Aleksandar Yosifov * * This file is part of Kamailio, a free SIP server. * @@ -27,6 +28,7 @@ #include "cmd.h" #include "spi_gen.h" +#include "port_gen.h" MODULE_VERSION @@ -39,6 +41,7 @@ str ipsec_listen_addr = STR_NULL; str ipsec_listen_addr6 = STR_NULL; int ipsec_client_port = 5062; int ipsec_server_port = 5063; +int ipsec_max_connections = 2; int spi_id_start = 100; int spi_id_range = 1000; int xfrm_user_selector = 143956232; @@ -55,6 +58,10 @@ static int w_destroy(struct sip_msg* _m, char* _d, char* _cflags); static int domain_fixup(void** param, int param_no); static int save_fixup2(void** param, int param_no); +extern int bind_ipsec_pcscf(usrloc_api_t* api); + +int init_flag = 0; + /*! \brief * Exported functions */ @@ -62,6 +69,7 @@ static cmd_export_t cmds[] = { {"ipsec_create", (cmd_function)w_create, 1, save_fixup2, 0, ONREPLY_ROUTE }, {"ipsec_forward", (cmd_function)w_forward, 1, save_fixup2, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"ipsec_destroy", (cmd_function)w_destroy, 1, save_fixup2, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, + {"bind_ims_ipsec_pcscf", (cmd_function)bind_ipsec_pcscf, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; @@ -69,12 +77,13 @@ static cmd_export_t cmds[] = { * Exported parameters */ static param_export_t params[] = { - {"ipsec_listen_addr", PARAM_STR, &ipsec_listen_addr }, - {"ipsec_listen_addr6", PARAM_STR, &ipsec_listen_addr6 }, - {"ipsec_client_port", INT_PARAM, &ipsec_client_port }, - {"ipsec_server_port", INT_PARAM, &ipsec_server_port }, - {"ipsec_spi_id_start", INT_PARAM, &spi_id_start}, - {"ipsec_spi_id_range", INT_PARAM, &spi_id_range}, + {"ipsec_listen_addr", PARAM_STR, &ipsec_listen_addr }, + {"ipsec_listen_addr6", PARAM_STR, &ipsec_listen_addr6 }, + {"ipsec_client_port", INT_PARAM, &ipsec_client_port }, + {"ipsec_server_port", INT_PARAM, &ipsec_server_port }, + {"ipsec_max_connections", INT_PARAM, &ipsec_max_connections }, + {"ipsec_spi_id_start", INT_PARAM, &spi_id_start }, + {"ipsec_spi_id_range", INT_PARAM, &spi_id_range }, {0, 0, 0} }; @@ -95,13 +104,152 @@ struct module_exports exports = { }; +static void ipsec_print_all_socket_lists() +{ + struct socket_info *si; + struct socket_info** list; + struct addr_info* ai; + unsigned short proto; + + LM_INFO("Listening on:\n"); + + proto=PROTO_UDP; + do{ + list=get_sock_info_list(proto); + for(si=list?*list:0; si; si=si->next){ + char buf[1024]; + int cnt=0; + + memset(buf, 0, sizeof(buf)); + + if(si->addr_info_lst){ + sprintf(buf, "%s: (%s", get_valid_proto_name(proto), si->address_str.s); + cnt = strlen(buf); + + for(ai=si->addr_info_lst; ai; ai=ai->next){ + sprintf(buf + cnt, ", %s", ai->address_str.s); + cnt = strlen(buf); + } + + if(si->port_no_str.s){ + sprintf(buf + cnt, "):%s%s%s", si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : ""); + }else{ + sprintf(buf + cnt, "):%u%s%s", si->port_no, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : ""); + } + cnt = strlen(buf); + }else{ + sprintf(buf, "%s: %s", get_valid_proto_name(proto), si->name.s); + cnt = strlen(buf); + + if(!(si->flags & SI_IS_IP)){ + if(si->address_str.s){ + sprintf(buf + cnt, " [%s]", si->address_str.s); + cnt = strlen(buf); + } + } + + if(si->port_no_str.s){ + sprintf(buf + cnt, ":%s%s%s", si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : ""); + }else{ + sprintf(buf + cnt, ":%u%s%s", si->port_no, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : ""); + } + cnt = strlen(buf); + + if(si->useinfo.name.s){ + printf(buf + cnt, " advertise %s:%d", si->useinfo.name.s, si->useinfo.port_no); + cnt = strlen(buf); + } + } + + LM_INFO("%s\n", buf); + } + }while((proto=next_proto(proto))); +} + +static int ipsec_add_listen_ifaces() +{ + char addr4[128]; + char addr6[128]; + int i; + + for(i = 0; i < ipsec_max_connections; ++i){ + if(ipsec_listen_addr.len) { + if(ipsec_listen_addr.len > sizeof(addr4)-1) { + LM_ERR("Bad value for ipsec listen address IPv4: %.*s\n", ipsec_listen_addr.len, ipsec_listen_addr.s); + return -1; + } + + memset(addr4, 0, sizeof(addr4)); + memcpy(addr4, ipsec_listen_addr.s, ipsec_listen_addr.len); + + //add listen interfaces for IPv4 + if(add_listen_iface(addr4, NULL, ipsec_client_port + i, PROTO_TCP, 0) != 0) { + LM_ERR("Error adding listen ipsec client TCP interface for IPv4\n"); + return -1; + } + + if(add_listen_iface(addr4, NULL, ipsec_server_port + i, PROTO_TCP, 0) != 0) { + LM_ERR("Error adding listen ipsec server TCP interface for IPv4\n"); + return -1; + } + + if(add_listen_iface(addr4, NULL, ipsec_client_port + i, PROTO_UDP, 0) != 0) { + LM_ERR("Error adding listen ipsec client UDP interface for IPv4\n"); + return -1; + } + + if(add_listen_iface(addr4, NULL, ipsec_server_port + i, PROTO_UDP, 0) != 0) { + LM_ERR("Error adding listen ipsec server UDP interface for IPv4\n"); + return -1; + } + } + + if(ipsec_listen_addr6.len) { + if(ipsec_listen_addr6.len > sizeof(addr6)-1) { + LM_ERR("Bad value for ipsec listen address IPv6: %.*s\n", ipsec_listen_addr6.len, ipsec_listen_addr6.s); + return -1; + } + + memset(addr6, 0, sizeof(addr6)); + memcpy(addr6, ipsec_listen_addr6.s, ipsec_listen_addr6.len); + + //add listen interfaces for IPv6 + if(add_listen_iface(addr6, NULL, ipsec_client_port + i, PROTO_TCP, 0) != 0) { + LM_ERR("Error adding listen ipsec client TCP interface for IPv6\n"); + return -1; + } + + if(add_listen_iface(addr6, NULL, ipsec_server_port + i, PROTO_TCP, 0) != 0) { + LM_ERR("Error adding listen ipsec server TCP interface for IPv6\n"); + return -1; + } + + if(add_listen_iface(addr6, NULL, ipsec_client_port + i, PROTO_UDP, 0) != 0) { + LM_ERR("Error adding listen ipsec client UDP interface for IPv6\n"); + return -1; + } + + if(add_listen_iface(addr6, NULL, ipsec_server_port + i, PROTO_UDP, 0) != 0) { + LM_ERR("Error adding listen ipsec server UDP interface for IPv6\n"); + return -1; + } + } + } + + if(fix_all_socket_lists() != 0) { + LM_ERR("Error calling fix_all_socket_lists()\n"); + return -1; + } + + ipsec_print_all_socket_lists(); + + return 0; +} + /*! \brief * Initialize parent */ static int mod_init(void) { - char addr4[128]; - char addr6[128]; - bind_usrloc_t bind_usrloc; bind_usrloc = (bind_usrloc_t) find_export("ul_bind_ims_usrloc_pcscf", 1, 0); @@ -122,70 +270,7 @@ static int mod_init(void) { } LM_INFO("Successfully bound to TM module\n"); - if(ipsec_listen_addr.len) { - if(ipsec_listen_addr.len > sizeof(addr4)-1) { - LM_ERR("Bad value for ipsec listen address IPv4: %.*s\n", ipsec_listen_addr.len, ipsec_listen_addr.s); - return -1; - } - - memset(addr4, 0, sizeof(addr4)); - memcpy(addr4, ipsec_listen_addr.s, ipsec_listen_addr.len); - - //add listen interfaces for IPv4 - if(add_listen_iface(addr4, NULL, ipsec_client_port, PROTO_TCP, 0) != 0) { - LM_ERR("Error adding listen ipsec client TCP interface for IPv4\n"); - return -1; - } - - if(add_listen_iface(addr4, NULL, ipsec_server_port, PROTO_TCP, 0) != 0) { - LM_ERR("Error adding listen ipsec server TCP interface for IPv4\n"); - return -1; - } - - if(add_listen_iface(addr4, NULL, ipsec_client_port, PROTO_UDP, 0) != 0) { - LM_ERR("Error adding listen ipsec client UDP interface for IPv4\n"); - return -1; - } - - if(add_listen_iface(addr4, NULL, ipsec_server_port, PROTO_UDP, 0) != 0) { - LM_ERR("Error adding listen ipsec server UDP interface for IPv4\n"); - return -1; - } - } - - if(ipsec_listen_addr6.len) { - if(ipsec_listen_addr6.len > sizeof(addr6)-1) { - LM_ERR("Bad value for ipsec listen address IPv6: %.*s\n", ipsec_listen_addr6.len, ipsec_listen_addr6.s); - return -1; - } - - memset(addr6, 0, sizeof(addr6)); - memcpy(addr6, ipsec_listen_addr6.s, ipsec_listen_addr6.len); - - //add listen interfaces for IPv6 - if(add_listen_iface(addr6, NULL, ipsec_client_port, PROTO_TCP, 0) != 0) { - LM_ERR("Error adding listen ipsec client TCP interface for IPv6\n"); - return -1; - } - - if(add_listen_iface(addr6, NULL, ipsec_server_port, PROTO_TCP, 0) != 0) { - LM_ERR("Error adding listen ipsec server TCP interface for IPv6\n"); - return -1; - } - - if(add_listen_iface(addr6, NULL, ipsec_client_port, PROTO_UDP, 0) != 0) { - LM_ERR("Error adding listen ipsec client UDP interface for IPv6\n"); - return -1; - } - - if(add_listen_iface(addr6, NULL, ipsec_server_port, PROTO_UDP, 0) != 0) { - LM_ERR("Error adding listen ipsec server UDP interface for IPv6\n"); - return -1; - } - } - - if(fix_all_socket_lists() != 0) { - LM_ERR("Error calling fix_all_socket_lists() during module initialisation\n"); + if(ipsec_add_listen_ifaces() != 0){ return -1; } @@ -195,11 +280,18 @@ static int mod_init(void) { } int res = 0; - if((res = init_spi_gen(spi_id_start, spi_id_start + spi_id_range)) != 0) { + if((res = init_spi_gen(spi_id_start, spi_id_range)) != 0) { LM_ERR("Error initialising spi generator. Error: %d\n", res); return -1; } + if((res = init_port_gen(ipsec_server_port, ipsec_client_port, ipsec_max_connections)) != 0) { + LM_ERR("Error initialising port generator. Error: %d\n", res); + return -1; + } + + init_flag = 1; + return 0; } @@ -212,6 +304,10 @@ static void mod_destroy(void) if(destroy_spi_gen() != 0) { LM_ERR("Error destroying spi generator\n"); } + + if(destroy_port_gen() != 0){ + LM_ERR("Error destroying port generator\n"); + } } static int child_init(int rank) diff --git a/src/modules/ims_ipsec_pcscf/ipsec.c b/src/modules/ims_ipsec_pcscf/ipsec.c index da756e7faff..9d53b5c9484 100644 --- a/src/modules/ims_ipsec_pcscf/ipsec.c +++ b/src/modules/ims_ipsec_pcscf/ipsec.c @@ -27,14 +27,13 @@ #include "../../core/dprint.h" #include "../../core/mem/pkg.h" #include "../../core/ip_addr.h" +#include "../../core/resolve.h" #include #include #include #include #include -//#include -//#include #define XFRM_TMPLS_BUF_SIZE 1024 @@ -43,8 +42,6 @@ extern int xfrm_user_selector; -extern int spi_id_start; -extern int spi_id_range; struct xfrm_buffer { char buf[NLMSG_DELETEALL_BUF_SIZE]; @@ -91,25 +88,25 @@ static void string_to_key(char* dst, const str key_string) } // Converts the protocol enum used in Kamailio to the constants used in Linux -unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto) -{ - switch(kamailio_proto) { - case PROTO_UDP: - return IPPROTO_UDP; - case PROTO_TCP: - return IPPROTO_TCP; - case PROTO_NONE: - case PROTO_TLS: - case PROTO_SCTP: - case PROTO_WS: - case PROTO_WSS: - case PROTO_OTHER: - default: - return IPPROTO_MAX; - }; -} - -int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int s_port, int d_port, int long id, str ck, str ik, str r_alg) +// unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto) +// { +// switch(kamailio_proto) { +// case PROTO_UDP: +// return IPPROTO_UDP; +// case PROTO_TCP: +// return IPPROTO_TCP; +// case PROTO_NONE: +// case PROTO_TLS: +// case PROTO_SCTP: +// case PROTO_WS: +// case PROTO_WSS: +// case PROTO_OTHER: +// default: +// return IPPROTO_MAX; +// }; +// } + +int add_sa(struct mnl_socket* nl_sock, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int s_port, int d_port, int long id, str ck, str ik, str r_alg) { char l_msg_buf[MNL_SOCKET_BUFFER_SIZE]; char l_auth_algo_buf[XFRM_TMPLS_BUF_SIZE]; @@ -125,11 +122,11 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_add memset(l_auth_algo_buf, 0, sizeof(l_auth_algo_buf)); memset(l_enc_algo_buf, 0, sizeof(l_enc_algo_buf)); - unsigned sel_proto = 0; - if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) { - LM_ERR("Invalid port was passed to the function: %d\n", proto); - return -1; - } + // unsigned sel_proto = 0; + // if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) { + // LM_ERR("Invalid port was passed to the function: %d\n", proto); + // return -1; + // } // nlmsghdr initialization l_nlh = mnl_nlmsg_put_header(l_msg_buf); @@ -220,7 +217,7 @@ int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_add } -int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id) +int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id, unsigned int af) { char* src_addr = NULL; char* dest_addr = NULL; @@ -243,26 +240,47 @@ int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_para memcpy(src_addr, src_addr_param.s, src_addr_param.len); memcpy(dest_addr, dest_addr_param.s, dest_addr_param.len); - struct { struct nlmsghdr n; struct xfrm_usersa_id xsid; char buf[XFRM_TMPLS_BUF_SIZE]; } req = { - .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)), - .n.nlmsg_flags = NLM_F_REQUEST, - .n.nlmsg_type = XFRM_MSG_DELSA, - .xsid.spi = htonl(id), - .xsid.family = AF_INET, - .xsid.proto = IPPROTO_ESP, - .xsid.daddr.a4 = inet_addr(dest_addr) + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_DELSA, + .n.nlmsg_pid = id, + .xsid.spi = htonl(id), + .xsid.family = af, + .xsid.proto = IPPROTO_ESP }; - // SADDR xfrm_address_t saddr; memset(&saddr, 0, sizeof(saddr)); - saddr.a4 = inet_addr(src_addr); + + if(af == AF_INET6){ + ip_addr_t ip_addr; + + if(str2ipxbuf(&dest_addr_param, &ip_addr) < 0){ + LM_ERR("Unable to convert dest address [%.*s]\n", dest_addr_param.len, dest_addr_param.s); + pkg_free(src_addr); + pkg_free(dest_addr); + return -1; + } + memcpy(req.xsid.daddr.a6, ip_addr.u.addr32, sizeof(req.xsid.daddr.a6)); + + memset(&ip_addr, 0, sizeof(ip_addr_t)); + if(str2ipxbuf(&src_addr_param, &ip_addr) < 0){ + LM_ERR("Unable to convert src address [%.*s]\n", src_addr_param.len, src_addr_param.s); + pkg_free(src_addr); + pkg_free(dest_addr); + return -1; + } + memcpy(saddr.a6, ip_addr.u.addr32, sizeof(saddr.a6)); + }else{ + req.xsid.daddr.a4 = inet_addr(dest_addr); + saddr.a4 = inet_addr(src_addr); + } mnl_attr_put(&req.n, XFRMA_SRCADDR, sizeof(saddr), (void *)&saddr); @@ -281,18 +299,18 @@ int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_para } -int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir) +int add_policy(struct mnl_socket* mnl_socket, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir) { char l_msg_buf[MNL_SOCKET_BUFFER_SIZE]; char l_tmpls_buf[XFRM_TMPLS_BUF_SIZE]; struct nlmsghdr* l_nlh; struct xfrm_userpolicy_info* l_xpinfo; - unsigned sel_proto = 0; - if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) { - LM_ERR("Invalid port was passed to the function: %d\n", proto); - return -1; - } + // unsigned sel_proto = 0; + // if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) { + // LM_ERR("Invalid port was passed to the function: %d\n", proto); + // return -1; + // } memset(l_msg_buf, 0, sizeof(l_msg_buf)); memset(l_tmpls_buf, 0, sizeof(l_tmpls_buf)); @@ -375,14 +393,13 @@ int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, const struct return 0; } -int remove_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir) +int remove_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, unsigned int af, enum ipsec_policy_direction dir) { - unsigned sel_proto = 0; - if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) { - LM_ERR("Invalid port was passed to the function: %d\n", proto); - return -1; - } - + // unsigned sel_proto = 0; + // if((sel_proto = kamailio_to_linux_proto(proto)) == IPPROTO_MAX) { + // LM_ERR("Invalid port was passed to the function: %d\n", proto); + // return -1; + // } unsigned char policy_dir = 0; if(dir == IPSEC_POLICY_DIRECTION_IN) { @@ -392,7 +409,7 @@ int remove_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_a policy_dir = XFRM_POLICY_OUT; } else { - LM_ERR("Invalid direction parameter passed to add_policy: %d\n", dir); + LM_ERR("Invalid direction parameter passed to remove_policy: %d\n", dir); return -1; } @@ -422,22 +439,49 @@ int remove_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_a struct xfrm_userpolicy_id xpid; char buf[XFRM_TMPLS_BUF_SIZE]; } req = { - .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)), - .n.nlmsg_flags = NLM_F_REQUEST, - .n.nlmsg_type = XFRM_MSG_DELPOLICY, + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_DELPOLICY, + .n.nlmsg_pid = p_id, .xpid.dir = policy_dir, - .xpid.sel.family = AF_INET, - .xpid.sel.daddr.a4 = inet_addr(dest_addr), - .xpid.sel.saddr.a4 = inet_addr(src_addr), + .xpid.sel.family = af, .xpid.sel.dport = htons(dst_port), .xpid.sel.dport_mask = 0xFFFF, - .xpid.sel.prefixlen_d = 32, .xpid.sel.sport = htons(src_port), .xpid.sel.sport_mask = 0xFFFF, - .xpid.sel.prefixlen_s = 32//, + .xpid.sel.user = htonl(xfrm_user_selector) //.xpid.sel.proto = sel_proto }; + if(af == AF_INET6){ + ip_addr_t ip_addr; + + if(str2ipxbuf(&dest_addr_param, &ip_addr) < 0){ + LM_ERR("Unable to convert dest address [%.*s]\n", dest_addr_param.len, dest_addr_param.s); + pkg_free(src_addr); + pkg_free(dest_addr); + return -1; + } + memcpy(req.xpid.sel.daddr.a6, ip_addr.u.addr32, sizeof(req.xpid.sel.daddr.a6)); + + if(str2ipxbuf(&src_addr_param, &ip_addr) < 0){ + LM_ERR("Unable to convert src address [%.*s]\n", src_addr_param.len, src_addr_param.s); + pkg_free(src_addr); + pkg_free(dest_addr); + return -1; + } + memcpy(req.xpid.sel.saddr.a6, ip_addr.u.addr32, sizeof(req.xpid.sel.saddr.a6)); + + req.xpid.sel.prefixlen_d = 128; + req.xpid.sel.prefixlen_s = 128; + }else{ + req.xpid.sel.daddr.a4 = inet_addr(dest_addr); + req.xpid.sel.saddr.a4 = inet_addr(src_addr); + + req.xpid.sel.prefixlen_d = 32; + req.xpid.sel.prefixlen_s = 32; + } + if(mnl_socket_sendto(mnl_socket, &req.n, req.n.nlmsg_len) < 0) { LM_ERR("Failed to send Netlink message, error: %s\n", strerror(errno)); diff --git a/src/modules/ims_ipsec_pcscf/ipsec.h b/src/modules/ims_ipsec_pcscf/ipsec.h index d334bbad789..68ff7edea52 100644 --- a/src/modules/ims_ipsec_pcscf/ipsec.h +++ b/src/modules/ims_ipsec_pcscf/ipsec.h @@ -39,14 +39,13 @@ enum ipsec_policy_direction { struct mnl_socket* init_mnl_socket(); void close_mnl_socket(struct mnl_socket* sock); -unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto); +//unsigned short kamailio_to_linux_proto(const unsigned short kamailio_proto); +int add_sa(struct mnl_socket* nl_sock, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int s_port, int d_port, int long id, str ck, str ik, str r_alg); +int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id, unsigned int af); -int add_sa(struct mnl_socket* nl_sock, unsigned short proto, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int s_port, int d_port, int long id, str ck, str ik, str r_alg); -int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id); - -int add_policy(struct mnl_socket* mnl_socket, unsigned short proto, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir); -int remove_policy(struct mnl_socket* mnl_socket, unsigned short proto, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir); +int add_policy(struct mnl_socket* mnl_socket, const struct ip_addr *src_addr_param, const struct ip_addr *dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir); +int remove_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, unsigned int af, enum ipsec_policy_direction dir); int clean_sa(struct mnl_socket* mnl_socket); int clean_policy(struct mnl_socket* mnl_socket); diff --git a/src/modules/ims_ipsec_pcscf/port_gen.c b/src/modules/ims_ipsec_pcscf/port_gen.c new file mode 100644 index 00000000000..590f26cfd27 --- /dev/null +++ b/src/modules/ims_ipsec_pcscf/port_gen.c @@ -0,0 +1,145 @@ +/* + * IMS IPSEC PCSCF module + * + * Copyright (C) 2018 Tsvetomir Dimitrov + * Copyright (C) 2019 Aleksandar Yosifov + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "spi_gen.h" +#include "spi_list.h" +#include + +pthread_mutex_t sport_mut; // server port mutex +pthread_mutex_t cport_mut; // client port mutex +spi_list_t used_sports; // list with used server ports +spi_list_t used_cports; // list with used client ports +uint32_t sport_val; // the last acquired server port +uint32_t cport_val; // the last acquired client port +uint32_t min_sport; +uint32_t min_cport; +uint32_t max_sport; +uint32_t max_cport; + +int init_port_gen(uint32_t sport_start_val, uint32_t cport_start_val, uint32_t range) +{ + if(sport_start_val < 1 || cport_start_val < 1){ + return 1; + } + + if((UINT32_MAX - range < sport_start_val) || (UINT32_MAX - range < cport_start_val)){ + return 2; + } + + if(pthread_mutex_init(&sport_mut, NULL) || pthread_mutex_init(&cport_mut, NULL)){ + return 3; + } + + used_sports = create_list(); + used_cports = create_list(); + + sport_val = min_sport = sport_start_val; + cport_val = min_cport = cport_start_val; + max_sport = sport_start_val + range; + max_cport = cport_start_val + range; + + return 0; +} + +uint32_t acquire_port(spi_list_t* used_ports, pthread_mutex_t* port_mut, uint32_t* port_val, uint32_t min_port, uint32_t max_port) +{ + //save the initial value for the highly unlikely case where there are no free server PORTs + uint32_t initial_val = *port_val; + uint32_t ret = 0; // by default return invalid port + + if(pthread_mutex_lock(port_mut) != 0) { + return ret; + } + + while(1){ + if(spi_in_list(used_ports, *port_val) == 0) { + ret = *port_val; + (*port_val)++; + break; + } + + (*port_val)++; //the current server port is not available - increment + + if(*port_val >= max_port) { //reached the top of the range - reset + *port_val = min_port; + } + + if(*port_val == initial_val) { //there are no free server ports + pthread_mutex_unlock(port_mut); + return ret; + } + } + + // found unused server port - add it to the used list + if(spi_add(used_ports, ret) != 0) { + ret = 0; + } + + pthread_mutex_unlock(port_mut); + return ret; +} + +uint32_t acquire_sport() +{ + return acquire_port(&used_sports, &sport_mut, &sport_val, min_sport, max_sport); +} + +uint32_t acquire_cport() +{ + return acquire_port(&used_cports, &cport_mut, &cport_val, min_cport, max_cport); +} + +int release_sport(uint32_t port) +{ + if(pthread_mutex_lock(&sport_mut) != 0){ + return 1; + } + + spi_remove(&used_sports, port); + + pthread_mutex_unlock(&sport_mut); + return 0; +} + +int release_cport(uint32_t port) +{ + if(pthread_mutex_lock(&cport_mut) != 0){ + return 1; + } + + spi_remove(&used_cports, port); + + pthread_mutex_unlock(&cport_mut); + return 0; +} + +int destroy_port_gen() +{ + int ret = pthread_mutex_destroy(&sport_mut); + if(ret != 0){ + return ret; + } + + return pthread_mutex_destroy(&cport_mut); +} diff --git a/src/modules/ims_ipsec_pcscf/port_gen.h b/src/modules/ims_ipsec_pcscf/port_gen.h new file mode 100644 index 00000000000..1978f64196d --- /dev/null +++ b/src/modules/ims_ipsec_pcscf/port_gen.h @@ -0,0 +1,40 @@ +/* + * IMS IPSEC PCSCF module + * + * Copyright (C) 2018 Tsvetomir Dimitrov + * Copyright (C) 2019 Aleksandar Yosifov + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _SPI_GEN_H_ + +#include + +// +// PORT GEN is based on SPI list, because the logics of the SPI gen and PORT gen are basically the same. +// It is used as an unique port generator for the TCP client and server ports. + +int init_port_gen(uint32_t sport_start_val, uint32_t cport_start_val, uint32_t range); +int destroy_port_gen(); +uint32_t acquire_sport(); // acquare server port +uint32_t acquire_cport(); // acquare client port +int release_sport(uint32_t port); // release server port +int release_cport(uint32_t port); // release client port + +#endif /* _SPI_GEN_H_ */ diff --git a/src/modules/ims_ipsec_pcscf/sec_agree.c b/src/modules/ims_ipsec_pcscf/sec_agree.c new file mode 100644 index 00000000000..089e0377348 --- /dev/null +++ b/src/modules/ims_ipsec_pcscf/sec_agree.c @@ -0,0 +1,264 @@ +/** + * Copyright (C) 2017 kamailio.org + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "sec_agree.h" + +#include "../../core/str.h" +#include "../../core/parser/msg_parser.h" +#include "../../core/mem/mem.h" + +static uint32_t parse_digits(str value) +{ + uint32_t ret = 0; + + int buf_len = value.len+1; + char* buf = (char*)malloc(buf_len); + + if(!buf) { + return ret; + } + + memset(buf, 0, buf_len); + memcpy(buf, value.s, value.len); + + ret = atoll(buf); + + free(buf); + + return ret; +} + +static void trim_whitespaces(str* string) { + // skip leading whitespace + while(string->len && (string->s[0]==' ' || string->s[0]=='\t' || string->s[0]=='<')){ + string->s = string->s + 1; + string->len --; + } + + // skip trailing whitespace + while(string->len && (string->s[string->len-1]==' ' || string->s[string->len-1]=='\t')){ + string->len--; + } +} + +#define SEC_COPY_STR_PARAM(DST, SRC)\ + DST.s = shm_malloc(SRC.len);\ + if(DST.s == NULL) {\ + return -1;\ + }\ + memcpy(DST.s, SRC.s, SRC.len);\ + DST.len = SRC.len; + + +static int process_sec_agree_param(str name, str value, ipsec_t *ret) +{ + trim_whitespaces(&name); + trim_whitespaces(&value); + + if(strncasecmp(name.s, "alg", name.len) == 0) { + SEC_COPY_STR_PARAM(ret->r_alg, value); + } + else if(strncasecmp(name.s, "prot", name.len) == 0) { + SEC_COPY_STR_PARAM(ret->prot, value); + } + else if(strncasecmp(name.s, "mod", name.len) == 0) { + SEC_COPY_STR_PARAM(ret->mod, value); + } + else if(strncasecmp(name.s, "ealg", name.len) == 0) { + SEC_COPY_STR_PARAM(ret->r_ealg, value); + } + else if(strncasecmp(name.s, "spi-c", name.len) == 0) { + ret->spi_uc = parse_digits(value); + } + else if(strncasecmp(name.s, "spi-s", name.len) == 0) { + ret->spi_us = parse_digits(value); + } + else if(strncasecmp(name.s, "port-c", name.len) == 0) { + ret->port_uc = parse_digits(value); + } + else if(strncasecmp(name.s, "port-s", name.len) == 0) { + ret->port_us = parse_digits(value); + } + else { + //unknown parameter + } + + return 0; +} + +static security_t* parse_sec_agree(struct hdr_field* h) +{ + int i = 0; + + str name = {0,0}; + str value = {0,0}; + str mechanism_name = {0,0}; + security_t* params = NULL; + str body = h->body; + + trim_whitespaces(&body); + + // find mechanism name end + for(i = 0; body.s[i] != ';' && i < body.len; i++); + + mechanism_name.s = body.s; + mechanism_name.len = i; + + if(strncasecmp(mechanism_name.s, "ipsec-3gpp", 10) != 0) { + // unsupported mechanism + LM_ERR("Unsupported mechanism: %.*s\n", STR_FMT(&mechanism_name)); + goto cleanup; + } + + // allocate shm memory for security_t (it will be saved in contact) + if ((params = shm_malloc(sizeof(security_t))) == NULL) { + LM_ERR("Error allocating shm memory for security_t parameters during sec-agree parsing\n"); + return NULL; + } + memset(params, 0, sizeof(security_t)); + + if((params->sec_header.s = shm_malloc(h->name.len)) == NULL) { + LM_ERR("Error allocating shm memory for security_t sec_header parameter during sec-agree parsing\n"); + goto cleanup; + } + memcpy(params->sec_header.s, h->name.s, h->name.len); + params->sec_header.len = h->name.len; + + // allocate memory for ipsec_t in security_t + params->data.ipsec = shm_malloc(sizeof(ipsec_t)); + if(!params->data.ipsec) { + LM_ERR("Error allocating memory for ipsec parameters during sec-agree parsing\n"); + goto cleanup; + } + memset(params->data.ipsec, 0, sizeof(ipsec_t)); + + + // set security type to IPSEC + params->type = SECURITY_IPSEC; + + body.s=body.s+i+1; + body.len=body.len-i-1; + + // get the rest of the parameters + i = 0; + while(i <= body.len) { + //look for end of buffer or parameter separator + if(i == body.len || body.s[i] == ';' ) { + if(name.len) { + // if(name.len) => a param name is parsed + // and now i points to the end of its value + value.s = body.s; + value.len = i; + } + //else - name is not read but there is a value + //so there is some error - skip ahead + body.s=body.s+i+1; + body.len=body.len-i-1; + + i=0; + + if(name.len && value.len) { + if(process_sec_agree_param(name, value, params->data.ipsec)) { + goto cleanup; + } + } + //else - something's wrong. Ignore! + + //processing is done - reset + name.len=0; + value.len=0; + } + //look for param=value separator + else if(body.s[i] == '=') { + name.s = body.s; + name.len = i; + + //position saved - skip ahead + body.s=body.s+i+1; + body.len=body.len-i-1; + + i=0; + } + //nothing interesting - move on + else { + i++; + } + } + + return params; + +cleanup: + // The same piece of code also lives in modules/ims_usrloc_pcscf/pcontact.c + // Function - free_security() + // Keep them in sync! + if (params) { + shm_free(params->sec_header.s); + + if(params->type == SECURITY_IPSEC && params->data.ipsec) { + shm_free(params->data.ipsec->ealg.s); + shm_free(params->data.ipsec->r_ealg.s); + shm_free(params->data.ipsec->ck.s); + shm_free(params->data.ipsec->alg.s); + shm_free(params->data.ipsec->r_alg.s); + shm_free(params->data.ipsec->ik.s); + shm_free(params->data.ipsec->prot.s); + shm_free(params->data.ipsec->mod.s); + shm_free(params->data.ipsec); + } + + shm_free(params); + } + + return NULL; +} + +static str s_security_client={"Security-Client",15}; +/** + * Looks for the Security-Client header + * @param msg - the sip message + * @param params - ptr to struct sec_agree_params, where parsed values will be saved + * @returns 0 on success, error code on failure + */ +security_t* cscf_get_security(struct sip_msg *msg) +{ + struct hdr_field *h = NULL; + + if (!msg) return NULL; + + if (parse_headers(msg, HDR_EOH_F, 0)<0) { + return NULL; + } + + h = msg->headers; + while(h) + { + if (h->name.len == s_security_client.len && strncasecmp(h->name.s, s_security_client.s, s_security_client.len)==0) + { + return parse_sec_agree(h); + } + + h = h->next; + } + + LM_INFO("No security parameters found\n"); + + return NULL; +} diff --git a/src/modules/ims_ipsec_pcscf/sec_agree.h b/src/modules/ims_ipsec_pcscf/sec_agree.h new file mode 100644 index 00000000000..958a79ca3b7 --- /dev/null +++ b/src/modules/ims_ipsec_pcscf/sec_agree.h @@ -0,0 +1,35 @@ +/** + * Copyright (C) 2017 kamailio.org + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef SEC_AGREE_H +#define SEC_AGREE_H + +#include "../ims_usrloc_pcscf/usrloc.h" + +/** + * Looks for the Security-Client header + * @param msg - the sip message + * @param params - ptr to struct sec_agree_params, where parsed values will be saved + * @returns 0 on success, error code on failure + */ +security_t* cscf_get_security(struct sip_msg *msg); + +#endif // SEC_AGREE_H diff --git a/src/modules/ims_ipsec_pcscf/spi_gen.c b/src/modules/ims_ipsec_pcscf/spi_gen.c index fa7a3e77599..ebd28203fb7 100644 --- a/src/modules/ims_ipsec_pcscf/spi_gen.c +++ b/src/modules/ims_ipsec_pcscf/spi_gen.c @@ -76,7 +76,8 @@ uint32_t acquire_spi() } if(spi_val == initial_val) { //there are no free SPIs - break; + pthread_mutex_unlock(&spis_mut); + return ret; } } diff --git a/src/modules/ims_ipsec_pcscf/spi_list.c b/src/modules/ims_ipsec_pcscf/spi_list.c index ca687fafe1f..6d780e0bd13 100644 --- a/src/modules/ims_ipsec_pcscf/spi_list.c +++ b/src/modules/ims_ipsec_pcscf/spi_list.c @@ -150,6 +150,8 @@ int spi_in_list(spi_list_t* list, uint32_t id) while(n) { if (n->id == id) return 1; + + n = n->next; } return 0; diff --git a/src/modules/ims_ipsec_pcscf/spi_list_tests.c b/src/modules/ims_ipsec_pcscf/spi_list_tests.c index c8afcd3548b..7c731ddeff5 100644 --- a/src/modules/ims_ipsec_pcscf/spi_list_tests.c +++ b/src/modules/ims_ipsec_pcscf/spi_list_tests.c @@ -300,16 +300,38 @@ void case13() //No duplicates void case14() { spi_list_t list = create_list(); - spi_add(&list, 1); spi_add(&list, 2); + spi_add(&list, 3); + spi_add(&list, 5); + spi_add(&list, 6); + + if(spi_in_list(&list, 1) != 0) { + printf("%s: failed. 1 is not in list, but spi_in_list() returns true.\n", __func__); + return; + } + + if(spi_in_list(&list, 4) != 0) { + printf("%s: failed. 4 is not in list, but spi_in_list() returns true.\n", __func__); + return; + } + + if(spi_in_list(&list, 7) != 0) { + printf("%s: failed. 7 is not in list, but spi_in_list() returns true.\n", __func__); + return; + } + + if(spi_in_list(&list, 2) != 1) { + printf("%s: failed. 2 is in list, but spi_in_list() returns false.\n", __func__); + return; + } - if(spi_in_list(&list, 1) != 1) { - printf("%s: failed. 1 is in list, but spi_in_list() returns false.\n", __func__); + if(spi_in_list(&list, 3) != 1) { + printf("%s: failed. 3 is in list, but spi_in_list() returns false.\n", __func__); return; } - if(spi_in_list(&list, 3) != 0) { - printf("%s: failed. 3 is not in list, but spi_in_list() returns true.\n", __func__); + if(spi_in_list(&list, 6) != 1) { + printf("%s: failed. 6 is in list, but spi_in_list() returns false.\n", __func__); return; }