Compare commits
3 Commits
ce602d5264
...
1e09653a76
Author | SHA1 | Date |
---|---|---|
Andreas Eversberg | 1e09653a76 | |
Andreas Eversberg | 8bb1499f1f | |
Andreas Eversberg | 302754c049 |
|
@ -582,6 +582,7 @@ AST_EXT_LIB_SETUP([BEANSTALK], [Beanstalk Job Queue], [beanstalk])
|
|||
#FIXME
|
||||
AST_EXT_LIB_SETUP([VEVS], [Vocal EVS Audio Decoder/Encoder], [bluetooth])
|
||||
#AST_EXT_LIB_SETUP([VEVS], [Vocal EVS Audio Decoder/Encoder], [vocal-evs])
|
||||
AST_EXT_LIB_SETUP([LIBMNL], [MNL library], [mnl])
|
||||
|
||||
if test "x${PBX_PJPROJECT}" != "x1" ; then
|
||||
AST_EXT_LIB_SETUP([PJPROJECT], [PJPROJECT], [pjproject])
|
||||
|
@ -2414,6 +2415,7 @@ AST_EXT_LIB_CHECK([BEANSTALK], [beanstalk], [bs_version], [beanstalk.h])
|
|||
|
||||
#FIXME
|
||||
AST_EXT_LIB_CHECK([VEVS], [bluetooth], [ba2str], [bluetooth/bluetooth.h])
|
||||
AST_EXT_LIB_CHECK([LIBMNL], [mnl], [mnl_socket_open], [libmnl/libmnl.h])
|
||||
#AST_EXT_LIB_CHECK([VEVS], [vocal-evs], [vevs_enc], [vocal-evs/evs.h])
|
||||
|
||||
PG_CONFIG=":"
|
||||
|
|
|
@ -563,7 +563,9 @@ enum ast_sip_auth_type {
|
|||
/*! Google Oauth */
|
||||
AST_SIP_AUTH_TYPE_GOOGLE_OAUTH,
|
||||
/*! Credentials not stored this is a fake auth */
|
||||
AST_SIP_AUTH_TYPE_ARTIFICIAL
|
||||
AST_SIP_AUTH_TYPE_ARTIFICIAL,
|
||||
/*! Credentials stored as a username and RES combination */
|
||||
AST_SIP_AUTH_TYPE_IMS_AKA
|
||||
};
|
||||
|
||||
#define SIP_SORCERY_AUTH_TYPE "auth"
|
||||
|
@ -586,7 +588,14 @@ struct ast_sip_auth {
|
|||
AST_STRING_FIELD(oauth_clientid);
|
||||
/*! Secret to use for OAuth authentication */
|
||||
AST_STRING_FIELD(oauth_secret);
|
||||
/*! Use USIM emulation with these parameters */
|
||||
AST_STRING_FIELD(usim_opc);
|
||||
AST_STRING_FIELD(usim_k);
|
||||
AST_STRING_FIELD(usim_amf);
|
||||
AST_STRING_FIELD(usim_sqn);
|
||||
);
|
||||
/*! Use AMI interface for communication with USIM (instead of emulation) */
|
||||
unsigned int usim_ami;
|
||||
/*! The time period (in seconds) that a nonce may be reused */
|
||||
unsigned int nonce_lifetime;
|
||||
/*! Used to determine what to use when authenticating */
|
||||
|
|
|
@ -354,4 +354,7 @@ BEANSTALK_LIB=@BEANSTALK_LIB@
|
|||
VEVS_INCLUDE=@VEVS_INCLUDE@
|
||||
VEVS_LIB=@VEVS_LIB@
|
||||
|
||||
LIBMNL_INCLUDE=@LIBMNL_INCLUDE@
|
||||
LIBMNL_LIB=@LIBMNL_LIB@
|
||||
|
||||
HAVE_SBIN_LAUNCHD=@PBX_LAUNCHD@
|
||||
|
|
|
@ -63,6 +63,8 @@ static int auth_type_handler(const struct aco_option *opt, struct ast_variable *
|
|||
ast_log(LOG_WARNING, "OAuth support is not available in the version of PJSIP in use\n");
|
||||
return -1;
|
||||
#endif
|
||||
} else if (!strcasecmp(var->value, "ims_aka")) {
|
||||
auth->type = AST_SIP_AUTH_TYPE_IMS_AKA;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Unknown authentication storage type '%s' specified for %s\n",
|
||||
var->value, var->name);
|
||||
|
@ -74,7 +76,8 @@ static int auth_type_handler(const struct aco_option *opt, struct ast_variable *
|
|||
static const char *auth_types_map[] = {
|
||||
[AST_SIP_AUTH_TYPE_USER_PASS] = "userpass",
|
||||
[AST_SIP_AUTH_TYPE_MD5] = "md5",
|
||||
[AST_SIP_AUTH_TYPE_GOOGLE_OAUTH] = "google_oauth"
|
||||
[AST_SIP_AUTH_TYPE_GOOGLE_OAUTH] = "google_oauth",
|
||||
[AST_SIP_AUTH_TYPE_IMS_AKA] = "ims_aka"
|
||||
};
|
||||
|
||||
const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type)
|
||||
|
@ -126,6 +129,7 @@ static int auth_apply(const struct ast_sorcery *sorcery, void *obj)
|
|||
break;
|
||||
case AST_SIP_AUTH_TYPE_USER_PASS:
|
||||
case AST_SIP_AUTH_TYPE_ARTIFICIAL:
|
||||
case AST_SIP_AUTH_TYPE_IMS_AKA:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -395,6 +399,16 @@ int ast_sip_initialize_sorcery_auth(void)
|
|||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, realm));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime",
|
||||
"32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "usim_ami",
|
||||
"no", OPT_BOOL_T, 0, FLDSET(struct ast_sip_auth, usim_ami));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "usim_opc",
|
||||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, usim_opc));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "usim_k",
|
||||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, usim_k));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "usim_amf",
|
||||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, usim_amf));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "usim_sqn",
|
||||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, usim_sqn));
|
||||
ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type",
|
||||
"userpass", auth_type_handler, auth_type_to_str, NULL, 0, 0);
|
||||
|
||||
|
|
|
@ -313,12 +313,8 @@ static pj_status_t set_outbound_authentication_credentials(pjsip_auth_clt_sess *
|
|||
pj_cstr(&auth_cred.scheme, "digest");
|
||||
switch (auth->type) {
|
||||
case AST_SIP_AUTH_TYPE_USER_PASS:
|
||||
if (volte_auth == (void *)0x1)
|
||||
pj_cstr(&auth_cred.data, "");
|
||||
else if (volte_auth)
|
||||
pj_strset(&auth_cred.data, (char *)volte_auth, 8);
|
||||
else
|
||||
pj_cstr(&auth_cred.data, auth->auth_pass);
|
||||
case AST_SIP_AUTH_TYPE_IMS_AKA:
|
||||
pj_cstr(&auth_cred.data, auth->auth_pass);
|
||||
auth_cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
|
||||
break;
|
||||
case AST_SIP_AUTH_TYPE_MD5:
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>libmnl</depend>
|
||||
<depend>pjproject</depend>
|
||||
<depend>res_pjsip</depend>
|
||||
<use type="module">res_statsd</use>
|
||||
|
@ -213,7 +214,7 @@
|
|||
<configOption name="user_agent">
|
||||
<synopsis>Overrides the User-Agent header that should be used for outbound REGISTER requests.</synopsis>
|
||||
</configOption>
|
||||
<configOption name="volte">
|
||||
<configOption name="ims_aka">
|
||||
<synopsis>Perform Voice over LTE SIP registration process.</synopsis>
|
||||
</configOption>
|
||||
</configObject>
|
||||
|
@ -272,6 +273,8 @@
|
|||
/* forward declarations */
|
||||
static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,
|
||||
const struct ast_sip_auth_vector *auth_vector);
|
||||
static int add_outbound_initial_authorization(pjsip_tx_data *tdata, const char *fromdomain,
|
||||
const struct ast_sip_auth_vector *auth_vector);
|
||||
|
||||
/*! \brief Some thread local storage used to determine if the running thread invoked the callback */
|
||||
AST_THREADSTORAGE(register_callback_invoked);
|
||||
|
@ -378,7 +381,7 @@ struct sip_outbound_registration {
|
|||
/*! \brief Whether Outbound support is enabled */
|
||||
unsigned int support_outbound;
|
||||
/*! \brief VoLTE support */
|
||||
unsigned int volte;
|
||||
unsigned int ims_aka;
|
||||
};
|
||||
|
||||
/*! \brief Outbound registration client state information (persists for lifetime of regc) */
|
||||
|
@ -447,7 +450,7 @@ struct sip_outbound_registration_client_state {
|
|||
/*! \brief The value for the User-Agent header sent in requests */
|
||||
char *user_agent;
|
||||
/*! \brief VoLTE support */
|
||||
unsigned int volte;
|
||||
unsigned int ims_aka;
|
||||
};
|
||||
|
||||
/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
|
||||
|
@ -896,6 +899,22 @@ static int handle_client_registration(void *data)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Create initial authorization header. */
|
||||
if (client_state->ims_aka) {
|
||||
struct sip_outbound_registration *reg = NULL;
|
||||
struct ast_sip_endpoint *endpt = NULL;
|
||||
reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration",client_state->registration_name);
|
||||
endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint);
|
||||
if ((reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration",
|
||||
client_state->registration_name)) &&
|
||||
reg->endpoint &&
|
||||
(endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint)) &&
|
||||
add_outbound_initial_authorization(tdata, endpt->fromdomain, &client_state->outbound_auths)) {
|
||||
ast_log(LOG_WARNING, "Failed to add initial authorization header.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
registration_client_send(client_state, tdata);
|
||||
|
||||
return 0;
|
||||
|
@ -1292,6 +1311,11 @@ static int handle_registration_response(void *data)
|
|||
pjsip_cseq_hdr *cseq_hdr;
|
||||
pjsip_tx_data *tdata;
|
||||
|
||||
/* Remove initial autorization header. */
|
||||
if (response->client_state->ims_aka) {
|
||||
volte_del_authorization(response->old_request);
|
||||
}
|
||||
|
||||
if (response->client_state->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) {
|
||||
struct sip_outbound_registration *reg = NULL;
|
||||
struct ast_sip_endpoint *endpt = NULL;
|
||||
|
@ -1586,7 +1610,7 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
|
|||
state->client_state->registration_name =
|
||||
ast_strdup(ast_sorcery_object_get_id(registration));
|
||||
state->client_state->user_agent = ast_strdup(registration->user_agent);
|
||||
state->client_state->volte = registration->volte;
|
||||
state->client_state->ims_aka = registration->ims_aka;
|
||||
|
||||
ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
|
||||
ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
|
||||
|
@ -1854,6 +1878,44 @@ cleanup:
|
|||
return res;
|
||||
}
|
||||
|
||||
/* Add intial authorization header for IMS AKA */
|
||||
static int add_outbound_initial_authorization(pjsip_tx_data *tdata, const char *fromdomain,
|
||||
const struct ast_sip_auth_vector *auth_vector)
|
||||
{
|
||||
size_t auth_size = AST_VECTOR_SIZE(auth_vector);
|
||||
struct ast_sip_auth *auths[auth_size];
|
||||
int res = 0;
|
||||
int idx;
|
||||
const char *username;
|
||||
|
||||
puts("1");
|
||||
memset(auths, 0, sizeof(auths));
|
||||
if (ast_sip_retrieve_auths(auth_vector, auths)) {
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
puts("2");
|
||||
for (idx = 0; idx < auth_size; ++idx) {
|
||||
if (auths[idx]->type == AST_SIP_AUTH_TYPE_IMS_AKA)
|
||||
break;
|
||||
}
|
||||
|
||||
if (idx == auth_size) {
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
puts("3");
|
||||
username = auths[idx]->auth_user;
|
||||
|
||||
volte_init_authorization(tdata, fromdomain, username);
|
||||
|
||||
cleanup:
|
||||
ast_sip_cleanup_auths(auths, auth_size);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*! \brief Helper function that allocates a pjsip registration client and configures it */
|
||||
static int sip_outbound_registration_regc_alloc(void *data)
|
||||
{
|
||||
|
@ -2027,8 +2089,12 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
|
|||
ast_log(LOG_ERROR, "Line support has been enabled on outbound registration '%s' without providing an endpoint\n",
|
||||
ast_sorcery_object_get_id(applied));
|
||||
return -1;
|
||||
} else if (!ast_strlen_zero(applied->endpoint) && !applied->line) {
|
||||
ast_log(LOG_ERROR, "An endpoint has been specified on outbound registration '%s' without enabling line support\n",
|
||||
} else if (applied->ims_aka && ast_strlen_zero(applied->endpoint)) {
|
||||
ast_log(LOG_ERROR, "IMS AKA support has been enabled on outbound registration '%s' without providing an endpoint\n",
|
||||
ast_sorcery_object_get_id(applied));
|
||||
return -1;
|
||||
} else if (!ast_strlen_zero(applied->endpoint) && !applied->line && !applied->ims_aka) {
|
||||
ast_log(LOG_ERROR, "An endpoint has been specified on outbound registration '%s' without enabling line or IMS AKA support\n",
|
||||
ast_sorcery_object_get_id(applied));
|
||||
return -1;
|
||||
}
|
||||
|
@ -2845,7 +2911,7 @@ static int load_module(void)
|
|||
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));
|
||||
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));
|
||||
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, user_agent));
|
||||
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "volte", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, volte));
|
||||
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "ims_aka", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, ims_aka));
|
||||
|
||||
/*
|
||||
* Register sorcery observers.
|
||||
|
|
|
@ -0,0 +1,436 @@
|
|||
/* Linux kernel IPsec interfacing via netlink XFRM
|
||||
*
|
||||
* Copyright (C) 2021 Harald Welte <laforge@osmocom.org>
|
||||
*
|
||||
* DOUBANGO 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.
|
||||
*
|
||||
* DOUBANGO 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 DOUBANGO.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <linux/xfrm.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "netlink_xfrm.h"
|
||||
|
||||
#define XFRM_USER_ID 0x240299 /* some random number; let's use TS 24.299 */
|
||||
|
||||
struct mnl_socket *xfrm_init_mnl_socket(void)
|
||||
{
|
||||
struct mnl_socket *mnl_socket = mnl_socket_open(NETLINK_XFRM);
|
||||
if (!mnl_socket) {
|
||||
fprintf(stderr, "ERR: Could not open XFRM netlink socket: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mnl_socket_bind(mnl_socket, 0, MNL_SOCKET_AUTOPID) < 0) {
|
||||
fprintf(stderr, "ERR: Could not open XFRM netlink socket: %s", strerror(errno));
|
||||
mnl_socket_close(mnl_socket);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mnl_socket;
|
||||
}
|
||||
|
||||
void xfrm_exit_mnl_socket(struct mnl_socket *mnl_socket)
|
||||
{
|
||||
mnl_socket_close(mnl_socket);
|
||||
}
|
||||
|
||||
static unsigned int get_next_nlmsg_seq(void)
|
||||
{
|
||||
static unsigned int next_seq;
|
||||
return next_seq++;
|
||||
}
|
||||
|
||||
|
||||
/* this is just a simple call-back which returns the nlmsghdr via 'data' */
|
||||
static int data_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
const struct nlmsghdr **rx = data;
|
||||
|
||||
*rx = nlh;
|
||||
|
||||
/* FIXME: is there a situation in which we'd want to return OK and not STOP? */
|
||||
return MNL_CB_STOP;
|
||||
}
|
||||
|
||||
/* send 'tx' via 'mnl_sock' and receive messages from kernel, using caller-provided
|
||||
* rx_buf/rx_buf_size as temporary storage buffer; return response nlmsghdr in 'rx' */
|
||||
static int transceive_mnl(struct mnl_socket *mnl_sock, const struct nlmsghdr *tx,
|
||||
uint8_t *rx_buf, size_t rx_buf_size, struct nlmsghdr **rx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = mnl_socket_sendto(mnl_sock, tx, tx->nlmsg_len);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERR: cannot create IPsec SA: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
puts("sent");
|
||||
|
||||
/* iterate until it is our answer, handing to mnl_cb_run, ... */
|
||||
while (1) {
|
||||
rc = mnl_socket_recvfrom(mnl_sock, rx_buf, rx_buf_size);
|
||||
if (rc == -1) {
|
||||
perror("mnl_socket_recvfrom");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rc = mnl_cb_run(rx_buf, rc, tx->nlmsg_seq, mnl_socket_get_portid(mnl_sock), data_cb, rx);
|
||||
if (rc == -1) {
|
||||
perror("mnl_cb_run");
|
||||
return -EIO;
|
||||
} else if (rc <= MNL_CB_STOP)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sockaddrs2xfrm_sel(struct xfrm_selector *sel, const struct sockaddr *src,
|
||||
const struct sockaddr *dst)
|
||||
{
|
||||
const struct sockaddr_in *sin;
|
||||
const struct sockaddr_in6 *sin6;
|
||||
|
||||
switch (src->sa_family) {
|
||||
case AF_INET:
|
||||
sin = (const struct sockaddr_in *) src;
|
||||
sel->saddr.a4 = sin->sin_addr.s_addr;
|
||||
sel->prefixlen_s = 32;
|
||||
sel->sport = sin->sin_port;
|
||||
sin = (const struct sockaddr_in *) dst;
|
||||
sel->daddr.a4 = sin->sin_addr.s_addr;
|
||||
sel->prefixlen_d = 32;
|
||||
sel->dport = sin->sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
sin6 = (const struct sockaddr_in6 *) src;
|
||||
memcpy(sel->saddr.a6, &sin6->sin6_addr, sizeof(sel->saddr.a6));
|
||||
sel->prefixlen_s = 128;
|
||||
sel->sport = sin6->sin6_port;
|
||||
sin6 = (const struct sockaddr_in6 *) dst;
|
||||
memcpy(sel->daddr.a6, &sin6->sin6_addr, sizeof(sel->daddr.a6));
|
||||
sel->prefixlen_d = 128;
|
||||
sel->dport = sin6->sin6_port;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
sel->dport_mask = 0xffff;
|
||||
sel->sport_mask = 0xffff;
|
||||
sel->family = src->sa_family;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SPI Allocation
|
||||
***********************************************************************/
|
||||
|
||||
/* allocate a local SPI for ESP between given src+dst address */
|
||||
int xfrm_spi_alloc(struct mnl_socket *mnl_sock, uint32_t reqid, uint32_t *spi_out,
|
||||
const struct sockaddr *src, const struct sockaddr *dst)
|
||||
{
|
||||
uint8_t msg_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
uint8_t rx_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct xfrm_userspi_info *xui, *rx_xui;
|
||||
struct nlmsghdr *nlh, *rx_nlh = NULL;
|
||||
const struct sockaddr_in *sin;
|
||||
const struct sockaddr_in6 *sin6;
|
||||
int rc;
|
||||
|
||||
memset(msg_buf, 0, sizeof(msg_buf));
|
||||
|
||||
if (src->sa_family != dst->sa_family)
|
||||
return -EINVAL;
|
||||
|
||||
nlh = mnl_nlmsg_put_header(msg_buf);
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST,
|
||||
nlh->nlmsg_type = XFRM_MSG_ALLOCSPI,
|
||||
nlh->nlmsg_seq = get_next_nlmsg_seq();
|
||||
//nlh->nlmsg_pid = reqid; //FIXME
|
||||
|
||||
xui = (struct xfrm_userspi_info *) mnl_nlmsg_put_extra_header(nlh, sizeof(*xui));
|
||||
|
||||
xui->info.family = src->sa_family;
|
||||
|
||||
/* RFC4303 reserves 0..255 */
|
||||
xui->min = 0x100;
|
||||
xui->max = 0xffffffff;
|
||||
|
||||
/* ID src, dst, proto */
|
||||
switch (src->sa_family) {
|
||||
case AF_INET:
|
||||
sin = (const struct sockaddr_in *) src;
|
||||
xui->info.saddr.a4 = sin->sin_addr.s_addr;
|
||||
sin = (const struct sockaddr_in *) dst;
|
||||
xui->info.id.daddr.a4 = sin->sin_addr.s_addr;
|
||||
//xui->info.sel.prefixlen_d = 32;
|
||||
break;
|
||||
case AF_INET6:
|
||||
sin6 = (const struct sockaddr_in6 *) src;
|
||||
memcpy(xui->info.saddr.a6, &sin6->sin6_addr, sizeof(xui->info.saddr.a6));
|
||||
//xui->info.sel.prefixlen_s = 128;
|
||||
sin6 = (const struct sockaddr_in6 *) dst;
|
||||
memcpy(xui->info.id.daddr.a6, &sin6->sin6_addr, sizeof(xui->info.id.daddr.a6));
|
||||
//xui->info.sel.prefixlen_d = 128;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "ERR: unsupported address family %u\n", src->sa_family);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xui->info.id.proto = IPPROTO_ESP;
|
||||
xui->info.reqid = reqid;
|
||||
xui->info.mode = XFRM_MODE_TRANSPORT;
|
||||
//xui->info.replay_window = 32; // TODO: check spec
|
||||
|
||||
rc = transceive_mnl(mnl_sock, nlh, rx_buf, MNL_SOCKET_BUFFER_SIZE, &rx_nlh);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERR: cannot create IPsec SA: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parse response */
|
||||
rx_xui = (void *)rx_nlh + sizeof(*rx_nlh);
|
||||
//printf("Allocated SPI=0x%08x\n", ntohl(xui->info.id.spi));
|
||||
*spi_out = ntohl(rx_xui->info.id.spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* SA (Security Association)
|
||||
***********************************************************************/
|
||||
|
||||
int xfrm_sa_del(struct mnl_socket *mnl_sock,
|
||||
const struct sockaddr *src, const struct sockaddr *dst, uint32_t spi)
|
||||
{
|
||||
uint8_t msg_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
uint8_t rx_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct xfrm_usersa_id *said;
|
||||
struct nlmsghdr *nlh, *rx_nlh;
|
||||
const struct sockaddr_in *sin;
|
||||
const struct sockaddr_in6 *sin6;
|
||||
xfrm_address_t saddr;
|
||||
int rc;
|
||||
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
|
||||
nlh = mnl_nlmsg_put_header(msg_buf);
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
nlh->nlmsg_type = XFRM_MSG_DELSA;
|
||||
nlh->nlmsg_seq = get_next_nlmsg_seq();
|
||||
//nlh->nlmsg_pid = reqid; //FIXME
|
||||
|
||||
said = (struct xfrm_usersa_id *) mnl_nlmsg_put_extra_header(nlh, sizeof(*said));
|
||||
said->spi = htonl(spi);
|
||||
said->proto = IPPROTO_ESP;
|
||||
|
||||
said->family = src->sa_family;
|
||||
switch (src->sa_family) {
|
||||
case AF_INET:
|
||||
sin = (const struct sockaddr_in *) src;
|
||||
saddr.a4 = sin->sin_addr.s_addr;
|
||||
sin = (const struct sockaddr_in *) dst;
|
||||
said->daddr.a4 = sin->sin_addr.s_addr;
|
||||
break;
|
||||
case AF_INET6:
|
||||
sin6 = (const struct sockaddr_in6 *) src;
|
||||
memcpy(saddr.a6, &sin6->sin6_addr, sizeof(saddr.a6));
|
||||
sin6 = (const struct sockaddr_in6 *) dst;
|
||||
memcpy(said->daddr.a6, &sin6->sin6_addr, sizeof(said->daddr.a6));
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "ERR: unsupported address family %u\n", src->sa_family);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mnl_attr_put(nlh, XFRMA_SRCADDR, sizeof(saddr), (void *)&saddr);
|
||||
|
||||
rc = transceive_mnl(mnl_sock, nlh, rx_buf, MNL_SOCKET_BUFFER_SIZE, &rx_nlh);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERR: cannot delete IPsec SA: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FIXME: parse response */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xfrm_sa_add(struct mnl_socket *mnl_sock, uint32_t reqid,
|
||||
const struct sockaddr *src, const struct sockaddr *dst, uint32_t spi,
|
||||
const struct xfrm_algo *auth_algo, const struct xfrm_algo *ciph_algo)
|
||||
{
|
||||
uint8_t msg_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
uint8_t rx_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct xfrm_usersa_info *sainfo;
|
||||
struct nlmsghdr *nlh, *rx_nlh;
|
||||
int rc;
|
||||
|
||||
nlh = mnl_nlmsg_put_header(msg_buf);
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
|
||||
nlh->nlmsg_type = XFRM_MSG_NEWSA;
|
||||
nlh->nlmsg_seq = get_next_nlmsg_seq();
|
||||
//nlh->nlmsg_pid = reqid; //FIXME
|
||||
|
||||
sainfo = (struct xfrm_usersa_info *) mnl_nlmsg_put_extra_header(nlh, sizeof(*sainfo));
|
||||
sainfo->sel.family = src->sa_family;
|
||||
rc = sockaddrs2xfrm_sel(&sainfo->sel, src, dst);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
sainfo->sel.user = htonl(XFRM_USER_ID);
|
||||
|
||||
sainfo->saddr = sainfo->sel.saddr;
|
||||
sainfo->id.daddr = sainfo->sel.daddr;
|
||||
|
||||
sainfo->id.spi = htonl(spi);
|
||||
sainfo->id.proto = IPPROTO_ESP;
|
||||
|
||||
sainfo->lft.soft_byte_limit = XFRM_INF;
|
||||
sainfo->lft.hard_byte_limit = XFRM_INF;
|
||||
sainfo->lft.soft_packet_limit = XFRM_INF;
|
||||
sainfo->lft.hard_packet_limit = XFRM_INF;
|
||||
sainfo->reqid = reqid;
|
||||
sainfo->family = src->sa_family;
|
||||
sainfo->mode = XFRM_MODE_TRANSPORT;
|
||||
sainfo->replay_window = 32;
|
||||
|
||||
mnl_attr_put(nlh, XFRMA_ALG_AUTH, sizeof(struct xfrm_algo) + auth_algo->alg_key_len, auth_algo);
|
||||
|
||||
mnl_attr_put(nlh, XFRMA_ALG_CRYPT, sizeof(struct xfrm_algo) + ciph_algo->alg_key_len, ciph_algo);
|
||||
|
||||
rc = transceive_mnl(mnl_sock, nlh, rx_buf, MNL_SOCKET_BUFFER_SIZE, &rx_nlh);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERR: cannot create IPsec SA: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FIXME: parse response */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Security Policy
|
||||
***********************************************************************/
|
||||
|
||||
int xfrm_policy_add(struct mnl_socket *mnl_sock,
|
||||
const struct sockaddr *src, const struct sockaddr *dst, uint32_t spi, bool dir_in)
|
||||
{
|
||||
uint8_t msg_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
uint8_t rx_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct xfrm_userpolicy_info *pinfo;
|
||||
struct xfrm_user_tmpl tmpl;
|
||||
struct nlmsghdr *nlh, *rx_nlh;
|
||||
int rc;
|
||||
|
||||
memset(&tmpl, 0, sizeof(tmpl));
|
||||
|
||||
nlh = mnl_nlmsg_put_header(msg_buf);
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
|
||||
nlh->nlmsg_type = XFRM_MSG_NEWPOLICY;
|
||||
nlh->nlmsg_seq = get_next_nlmsg_seq();
|
||||
//nlh->nlmsg_pid = reqid; //FIXME
|
||||
|
||||
pinfo = (struct xfrm_userpolicy_info *) mnl_nlmsg_put_extra_header(nlh, sizeof(*pinfo));
|
||||
|
||||
rc = sockaddrs2xfrm_sel(&pinfo->sel, src, dst);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pinfo->sel.user = htonl(XFRM_USER_ID);
|
||||
|
||||
pinfo->lft.soft_byte_limit = XFRM_INF;
|
||||
pinfo->lft.hard_byte_limit = XFRM_INF;
|
||||
pinfo->lft.soft_packet_limit = XFRM_INF;
|
||||
pinfo->lft.hard_packet_limit = XFRM_INF;
|
||||
pinfo->priority = 2342; // FIXME
|
||||
pinfo->action = XFRM_POLICY_ALLOW;
|
||||
pinfo->share = XFRM_SHARE_ANY;
|
||||
|
||||
if (dir_in)
|
||||
pinfo->dir = XFRM_POLICY_IN;
|
||||
else
|
||||
pinfo->dir = XFRM_POLICY_OUT;
|
||||
|
||||
tmpl.id.proto = IPPROTO_ESP;
|
||||
tmpl.id.daddr = pinfo->sel.daddr;
|
||||
tmpl.saddr = pinfo->sel.saddr;
|
||||
tmpl.family = pinfo->sel.family;
|
||||
tmpl.reqid = spi;
|
||||
tmpl.mode = XFRM_MODE_TRANSPORT;
|
||||
tmpl.aalgos = 0xffffffff;
|
||||
tmpl.ealgos = 0xffffffff;
|
||||
tmpl.calgos = 0xffffffff;
|
||||
mnl_attr_put(nlh, XFRMA_TMPL, sizeof(tmpl), &tmpl);
|
||||
|
||||
rc = transceive_mnl(mnl_sock, nlh, rx_buf, MNL_SOCKET_BUFFER_SIZE, &rx_nlh);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERR: cannot create IPsec policy: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FIXME: parse response */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int xfrm_policy_del(struct mnl_socket *mnl_sock,
|
||||
const struct sockaddr *src, const struct sockaddr *dst, bool dir_in)
|
||||
{
|
||||
uint8_t msg_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
uint8_t rx_buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct xfrm_userpolicy_id *pid;
|
||||
struct nlmsghdr *nlh, *rx_nlh;
|
||||
int rc;
|
||||
|
||||
nlh = mnl_nlmsg_put_header(msg_buf);
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
nlh->nlmsg_type = XFRM_MSG_DELPOLICY;
|
||||
nlh->nlmsg_seq = get_next_nlmsg_seq();
|
||||
//nlh->nlmsg_pid = reqid; //FIXME
|
||||
|
||||
pid = (struct xfrm_userpolicy_id *) mnl_nlmsg_put_extra_header(nlh, sizeof(*pid));
|
||||
|
||||
rc = sockaddrs2xfrm_sel(&pid->sel, src, dst);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pid->sel.user = htonl(XFRM_USER_ID);
|
||||
|
||||
if (dir_in)
|
||||
pid->dir = XFRM_POLICY_IN;
|
||||
else
|
||||
pid->dir = XFRM_POLICY_OUT;
|
||||
|
||||
rc = transceive_mnl(mnl_sock, nlh, rx_buf, MNL_SOCKET_BUFFER_SIZE, &rx_nlh);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERR: cannot delete IPsec policy: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FIXME: parse response */
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/xfrm.h>
|
||||
|
||||
struct mnl_socket;
|
||||
|
||||
struct xfrm_algobuf {
|
||||
struct xfrm_algo algo;
|
||||
uint8_t buf[128];
|
||||
};
|
||||
|
||||
struct mnl_socket *xfrm_init_mnl_socket(void);
|
||||
|
||||
void xfrm_exit_mnl_socket(struct mnl_socket *mnl_socket);
|
||||
|
||||
int xfrm_spi_alloc(struct mnl_socket *mnl_sock, uint32_t reqid, uint32_t *spi_out,
|
||||
const struct sockaddr *src, const struct sockaddr *dst);
|
||||
|
||||
int xfrm_sa_add(struct mnl_socket *mnl_sock, uint32_t reqid,
|
||||
const struct sockaddr *src, const struct sockaddr *dst, uint32_t spi,
|
||||
const struct xfrm_algo *auth_algo, const struct xfrm_algo *ciph_algo);
|
||||
|
||||
int xfrm_sa_del(struct mnl_socket *mnl_sock,
|
||||
const struct sockaddr *src, const struct sockaddr *dst, uint32_t spi);
|
||||
|
||||
int xfrm_policy_add(struct mnl_socket *mnl_sock,
|
||||
const struct sockaddr *src, const struct sockaddr *dst, uint32_t spi, bool dir_in);
|
||||
|
||||
int xfrm_policy_del(struct mnl_socket *mnl_sock,
|
||||
const struct sockaddr *src, const struct sockaddr *dst, bool dir_in);
|
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* Author: Andreas Eversberg
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
#include "asterisk/utils.h"
|
||||
|
||||
#include <pjsip.h>
|
||||
|
||||
#include "volte.h"
|
||||
|
||||
/* Socket for transform configuration */
|
||||
static struct mnl_socket *g_mnl_socket = NULL;
|
||||
|
||||
/* Security address and SPI settings */
|
||||
static pj_sockaddr g_local_addr_c, g_remote_addr_s;
|
||||
static pj_sockaddr g_remote_addr_c, g_local_addr_s;
|
||||
static uint32_t g_local_spi_c, g_remote_spi_s;
|
||||
static uint32_t g_remote_spi_c, g_local_spi_s;
|
||||
static pj_bool_t g_local_sa_c_set = PJ_FALSE, g_remote_sa_s_set = PJ_FALSE;
|
||||
static pj_bool_t g_remote_sa_c_set = PJ_FALSE, g_local_sa_s_set = PJ_FALSE;
|
||||
static pj_bool_t g_local_sp_c_set = PJ_FALSE, g_remote_sp_s_set = PJ_FALSE;
|
||||
static pj_bool_t g_remote_sp_c_set = PJ_FALSE, g_local_sp_s_set = PJ_FALSE;
|
||||
|
||||
/* Supported authentication and encryption algorithms. */
|
||||
struct ipsec_alg {
|
||||
const char *sip_name;
|
||||
const char *kernel_name;
|
||||
};
|
||||
|
||||
const struct ipsec_alg ipsec_alg[] = {
|
||||
{ "hmac-md5-96", "md5" },
|
||||
{ "hmac-sha-1-96", "sha1" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
const struct ipsec_alg ipsec_ealg[] = {
|
||||
{ "null", "cipher_null" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* Convert from pj_sockaddr to sockaddr_storage. */
|
||||
static void copy_pj_sockaddr_to_sockaddr_storage(const pj_sockaddr *src, struct sockaddr_storage *dst)
|
||||
{
|
||||
memset(dst, 0, sizeof(struct sockaddr_storage));
|
||||
|
||||
dst->ss_family = (src->addr.sa_family == PJ_AF_INET) ? AF_INET : AF_INET6;
|
||||
|
||||
if (dst->ss_family == AF_INET) {
|
||||
struct sockaddr_in *dst_in = (struct sockaddr_in *)dst;
|
||||
const pj_sockaddr_in *src_in = (const pj_sockaddr_in *)src;
|
||||
dst_in->sin_port = src_in->sin_port;
|
||||
memcpy(&dst_in->sin_addr, &src_in->sin_addr, sizeof(struct in_addr));
|
||||
} else if (dst->ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *dst_in6 = (struct sockaddr_in6 *)dst;
|
||||
const pj_sockaddr_in6 *src_in6 = (const pj_sockaddr_in6 *)src;
|
||||
dst_in6->sin6_port = src_in6->sin6_port;
|
||||
memcpy(&dst_in6->sin6_addr, &src_in6->sin6_addr, sizeof(struct in6_addr));
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert sockaddr_storage IP to string. */
|
||||
static char *sockaddr_storage_to_string(const struct sockaddr_storage *addr, char *ip_string, size_t ip_string_len)
|
||||
{
|
||||
if (addr->ss_family == AF_INET) {
|
||||
const struct sockaddr_in *addr_in = (const struct sockaddr_in *)addr;
|
||||
inet_ntop(AF_INET, &addr_in->sin_addr, ip_string, ip_string_len);
|
||||
} else if (addr->ss_family == AF_INET6) {
|
||||
const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)addr;
|
||||
inet_ntop(AF_INET6, &addr_in6->sin6_addr, ip_string, ip_string_len);
|
||||
} else {
|
||||
snprintf(ip_string, ip_string_len, "<Unknown AF>");
|
||||
}
|
||||
|
||||
return ip_string;
|
||||
}
|
||||
|
||||
/* Delete old SA and SP entries upon new registration or module exit. */
|
||||
void volte_cleanup_xfrm(void)
|
||||
{
|
||||
struct sockaddr_storage local_addr_c, local_addr_s;
|
||||
struct sockaddr_storage remote_addr_c, remote_addr_s;
|
||||
|
||||
/* Convert from pj_sockaddr to sockaddr_storage. */
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_local_addr_c, &local_addr_c);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_remote_addr_c, &remote_addr_c);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_local_addr_s, &local_addr_s);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_remote_addr_s, &remote_addr_s);
|
||||
|
||||
if (g_local_sa_c_set || g_local_sa_s_set || g_remote_sa_c_set || g_remote_sa_s_set ||
|
||||
g_local_sp_c_set || g_local_sp_s_set || g_remote_sp_c_set || g_remote_sp_s_set)
|
||||
ast_log(LOG_DEBUG, "Remove old security associations/policies\n");
|
||||
|
||||
/* Remove current security associations and policies. */
|
||||
if (g_local_sa_c_set) {
|
||||
xfrm_sa_del(g_mnl_socket, (const struct sockaddr *)&local_addr_c,
|
||||
(const struct sockaddr *)&remote_addr_s, g_remote_spi_s);
|
||||
g_local_sa_c_set = PJ_FALSE;
|
||||
}
|
||||
if (g_local_sa_s_set) {
|
||||
xfrm_sa_del(g_mnl_socket, (const struct sockaddr *)&local_addr_s,
|
||||
(const struct sockaddr *)&remote_addr_c, g_remote_spi_c);
|
||||
g_local_sa_s_set = PJ_FALSE;
|
||||
}
|
||||
if (g_remote_sa_c_set) {
|
||||
xfrm_sa_del(g_mnl_socket, (const struct sockaddr *)&remote_addr_c,
|
||||
(const struct sockaddr *)&local_addr_s, g_local_spi_s);
|
||||
g_remote_sa_c_set = PJ_FALSE;
|
||||
}
|
||||
if (g_remote_sa_s_set) {
|
||||
xfrm_sa_del(g_mnl_socket, (const struct sockaddr *)&remote_addr_s,
|
||||
(const struct sockaddr *)&local_addr_c, g_local_spi_c);
|
||||
g_remote_sa_s_set = PJ_FALSE;
|
||||
}
|
||||
if (g_local_sp_c_set) {
|
||||
xfrm_policy_del(g_mnl_socket, (const struct sockaddr *)&local_addr_c,
|
||||
(const struct sockaddr *)&remote_addr_s, false);
|
||||
g_local_sp_c_set = PJ_FALSE;
|
||||
}
|
||||
if (g_local_sp_s_set) {
|
||||
xfrm_policy_del(g_mnl_socket, (const struct sockaddr *)&local_addr_s,
|
||||
(const struct sockaddr *)&remote_addr_c, false);
|
||||
g_local_sp_s_set = PJ_FALSE;
|
||||
}
|
||||
if (g_remote_sp_c_set) {
|
||||
xfrm_policy_del(g_mnl_socket, (const struct sockaddr *)&remote_addr_c,
|
||||
(const struct sockaddr *)&local_addr_s, true);
|
||||
g_remote_sp_c_set = PJ_FALSE;
|
||||
}
|
||||
if (g_remote_sp_s_set) {
|
||||
xfrm_policy_del(g_mnl_socket, (const struct sockaddr *)&remote_addr_s,
|
||||
(const struct sockaddr *)&local_addr_c, true);
|
||||
g_remote_sp_s_set = PJ_FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pj_status_t volte_alloc_spi(void)
|
||||
{
|
||||
struct sockaddr_storage local_addr_c, local_addr_s;
|
||||
struct sockaddr_storage remote_addr_c, remote_addr_s;
|
||||
char src_str[64], dst_str[64];
|
||||
pj_status_t status;
|
||||
|
||||
/* Convert from pj_sockaddr to sockaddr_storage. */
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_local_addr_c, &local_addr_c);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_remote_addr_c, &remote_addr_c);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_local_addr_s, &local_addr_s);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_remote_addr_s, &remote_addr_s);
|
||||
|
||||
ast_log(LOG_DEBUG, "SPI allocation: local client: %s:%d remote server: %s:%d\n",
|
||||
sockaddr_storage_to_string(&local_addr_c, src_str, sizeof(src_str)),
|
||||
pj_sockaddr_get_port(&g_local_addr_c),
|
||||
sockaddr_storage_to_string(&remote_addr_s, dst_str, sizeof(dst_str)),
|
||||
pj_sockaddr_get_port(&g_remote_addr_s));
|
||||
ast_log(LOG_DEBUG, "SPI allocation: remote client: %s:%d local server: %s:%d\n",
|
||||
sockaddr_storage_to_string(&remote_addr_c, src_str, sizeof(src_str)),
|
||||
pj_sockaddr_get_port(&g_remote_addr_s),
|
||||
sockaddr_storage_to_string(&local_addr_c, dst_str, sizeof(dst_str)),
|
||||
pj_sockaddr_get_port(&g_local_addr_s));
|
||||
|
||||
/* Allocate SPI-C and SPI-S towards remote peer. */
|
||||
status = xfrm_spi_alloc(g_mnl_socket, 2342, &g_local_spi_c, (const struct sockaddr *)&local_addr_c,
|
||||
(const struct sockaddr *)&remote_addr_c);
|
||||
if (status) {
|
||||
spi_alloc_failed:
|
||||
ast_log(LOG_ERROR, "Failed to allocate SPI.\n");
|
||||
return status;
|
||||
}
|
||||
g_local_sa_s_set = PJ_TRUE;
|
||||
status = xfrm_spi_alloc(g_mnl_socket, 2342, &g_local_spi_s, (const struct sockaddr *)&local_addr_s,
|
||||
(const struct sockaddr *)&remote_addr_s);
|
||||
if (status)
|
||||
goto spi_alloc_failed;
|
||||
g_local_sa_c_set = PJ_TRUE;
|
||||
ast_log(LOG_DEBUG, "SPI allocation: SPI-C=0x%08x SPI-S=0x%08x\n", g_local_spi_s, g_local_spi_c);
|
||||
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
/* Set new SA and SP entries upon secuirty handshake. */
|
||||
pj_status_t volte_set_xfrm(const pj_str_t *alg, const pj_str_t *ealg, uint8_t *ik)
|
||||
{
|
||||
struct xfrm_algobuf auth_algo, ciph_algo;
|
||||
int i, j;
|
||||
struct sockaddr_storage local_addr_c, local_addr_s;
|
||||
struct sockaddr_storage remote_addr_c, remote_addr_s;
|
||||
char src_str[64], dst_str[64];
|
||||
pj_status_t sa_add_failed = PJ_SUCCESS, sp_add_failed = PJ_SUCCESS;
|
||||
pj_status_t status;
|
||||
|
||||
/* Set authentication and encryption algorithms and key. */
|
||||
for (i = 0; ipsec_alg[i].sip_name; i++) {
|
||||
if (!pj_strncmp2(alg, ipsec_alg[i].sip_name, strlen(ipsec_alg[i].sip_name)))
|
||||
break;
|
||||
}
|
||||
if (!ipsec_alg[i].kernel_name) {
|
||||
ast_log(LOG_ERROR, "Given 'alg' not supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (j = 0; ipsec_ealg[j].sip_name; j++) {
|
||||
if (!pj_strncmp2(ealg, ipsec_ealg[j].sip_name, strlen(ipsec_ealg[j].sip_name)))
|
||||
break;
|
||||
}
|
||||
if (!ipsec_ealg[i].kernel_name) {
|
||||
ast_log(LOG_ERROR, "Given 'ealg' not supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
strcpy(auth_algo.algo.alg_name, ipsec_alg[i].kernel_name);
|
||||
switch (i) {
|
||||
case 0:
|
||||
memcpy(auth_algo.algo.alg_key, ik, 16);
|
||||
auth_algo.algo.alg_key_len = 128;
|
||||
break;
|
||||
case 1:
|
||||
memcpy(auth_algo.algo.alg_key, ik, 16);
|
||||
memset(auth_algo.algo.alg_key + 16, 0x00, 4);
|
||||
auth_algo.algo.alg_key_len = 160;
|
||||
break;
|
||||
}
|
||||
strcpy(ciph_algo.algo.alg_name, ipsec_ealg[j].kernel_name);
|
||||
switch (j) {
|
||||
case 0:
|
||||
ciph_algo.algo.alg_key_len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_local_addr_c, &local_addr_c);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_remote_addr_c, &remote_addr_c);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_local_addr_s, &local_addr_s);
|
||||
copy_pj_sockaddr_to_sockaddr_storage(&g_remote_addr_s, &remote_addr_s);
|
||||
|
||||
ast_log(LOG_DEBUG, "xfrm: local client: %s:%d (SPI=0x%08x) remote server: %s:%d (SPI=0x%08x)\n",
|
||||
sockaddr_storage_to_string(&local_addr_c, src_str, sizeof(src_str)),
|
||||
pj_sockaddr_get_port(&g_local_addr_c), g_local_spi_c,
|
||||
sockaddr_storage_to_string(&remote_addr_s, dst_str, sizeof(dst_str)),
|
||||
pj_sockaddr_get_port(&g_remote_addr_s), g_remote_spi_s);
|
||||
ast_log(LOG_DEBUG, "xfrm: remote client: %s:%d (SPI=0x%08x) local server: %s:%d (SPI=0x%08x)\n",
|
||||
sockaddr_storage_to_string(&remote_addr_c, src_str, sizeof(src_str)),
|
||||
pj_sockaddr_get_port(&g_remote_addr_s), g_remote_spi_c,
|
||||
sockaddr_storage_to_string(&local_addr_c, dst_str, sizeof(dst_str)),
|
||||
pj_sockaddr_get_port(&g_local_addr_s), g_local_spi_s);
|
||||
ast_log(LOG_DEBUG, "xfrm: alg: %s ealg: %s\n", auth_algo.algo.alg_name, ciph_algo.algo.alg_name);
|
||||
|
||||
status = xfrm_sa_add(g_mnl_socket, g_remote_spi_s, (const struct sockaddr *)&local_addr_c,
|
||||
(const struct sockaddr *)&remote_addr_s, g_remote_spi_s, &auth_algo.algo, &ciph_algo.algo);
|
||||
if (status)
|
||||
sa_add_failed = status;
|
||||
else
|
||||
g_local_sa_c_set = PJ_TRUE;
|
||||
status = xfrm_sa_add(g_mnl_socket, g_remote_spi_c, (const struct sockaddr *)&local_addr_s,
|
||||
(const struct sockaddr *)&remote_addr_c, g_remote_spi_c, &auth_algo.algo, &ciph_algo.algo);
|
||||
if (status)
|
||||
sa_add_failed = status;
|
||||
else
|
||||
g_local_sa_s_set = PJ_TRUE;
|
||||
status = xfrm_sa_add(g_mnl_socket, g_local_spi_s, (const struct sockaddr *)&remote_addr_c,
|
||||
(const struct sockaddr *)&local_addr_s, g_local_spi_s, &auth_algo.algo, &ciph_algo.algo);
|
||||
if (status)
|
||||
sa_add_failed = status;
|
||||
else
|
||||
g_remote_sa_c_set = PJ_TRUE;
|
||||
|
||||
status = xfrm_sa_add(g_mnl_socket, g_local_spi_c, (const struct sockaddr *)&remote_addr_s,
|
||||
(const struct sockaddr *)&local_addr_c, g_local_spi_c, &auth_algo.algo, &ciph_algo.algo);
|
||||
if (status)
|
||||
sa_add_failed = status;
|
||||
else
|
||||
g_remote_sa_s_set = PJ_TRUE;
|
||||
status = xfrm_policy_add(g_mnl_socket, (const struct sockaddr *)&local_addr_c,
|
||||
(const struct sockaddr *)&remote_addr_s, g_remote_spi_s, false);
|
||||
if (status)
|
||||
sp_add_failed = status;
|
||||
else
|
||||
g_local_sp_c_set = PJ_TRUE;
|
||||
status = xfrm_policy_add(g_mnl_socket, (const struct sockaddr *)&local_addr_s,
|
||||
(const struct sockaddr *)&remote_addr_c, g_remote_spi_c, false);
|
||||
if (status)
|
||||
sp_add_failed = status;
|
||||
else
|
||||
g_local_sp_s_set = PJ_TRUE;
|
||||
status = xfrm_policy_add(g_mnl_socket, (const struct sockaddr *)&remote_addr_c,
|
||||
(const struct sockaddr *)&local_addr_s, g_local_spi_s, true);
|
||||
if (status)
|
||||
sp_add_failed = status;
|
||||
else
|
||||
g_remote_sp_c_set = PJ_TRUE;
|
||||
status = xfrm_policy_add(g_mnl_socket, (const struct sockaddr *)&remote_addr_s,
|
||||
(const struct sockaddr *)&local_addr_c, g_local_spi_c, true);
|
||||
if (status)
|
||||
sp_add_failed = status;
|
||||
else
|
||||
g_remote_sp_s_set = PJ_TRUE;
|
||||
|
||||
if (sa_add_failed)
|
||||
ast_log(LOG_ERROR, "Failed to add IPSec SA.\n");
|
||||
if (sp_add_failed)
|
||||
ast_log(LOG_ERROR, "Failed to add IPSec SP.\n");
|
||||
|
||||
return (sa_add_failed) ? sa_add_failed : sp_add_failed;
|
||||
}
|
||||
|
||||
/* Header field names. */
|
||||
const pj_str_t STR_SUPPORTED = { "Supported", 9 };
|
||||
const pj_str_t STR_REQUIRE = { "Require", 7 };
|
||||
const pj_str_t STR_PROXY_REQUIRE = { "Proxy-Require", 13 };
|
||||
const pj_str_t STR_PATH = { "path", 4 };
|
||||
const pj_str_t STR_SEC_AGREE = { "sec-agree", 9 };
|
||||
const pj_str_t STR_AUTHORIZATION = { "Authorization", 13 };
|
||||
const pj_str_t STR_AUTS = { "auts", 4 };
|
||||
|
||||
/* Create string header and add given value. */
|
||||
static pj_status_t add_value_string_hdr(pjsip_tx_data *tdata, const pj_str_t *name, const pj_str_t *value)
|
||||
{
|
||||
pjsip_generic_string_hdr *hdr;
|
||||
|
||||
/* Add Proxy-Require header. */
|
||||
hdr = pjsip_generic_string_hdr_create(tdata->pool, name, value);
|
||||
if (!hdr) {
|
||||
ast_log(LOG_ERROR, "Failed to create string header.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Append header */
|
||||
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Add a value to an array header. If the header does not exist, it is created. */
|
||||
static pj_status_t add_value_array_hdr(pjsip_tx_data *tdata, const pj_str_t *name, const pj_str_t *value)
|
||||
{
|
||||
pjsip_generic_array_hdr *hdr;
|
||||
pj_bool_t created = PJ_FALSE;
|
||||
|
||||
/* Create header, if not yet created. */
|
||||
hdr = pjsip_msg_find_hdr_by_name(tdata->msg, name, NULL);
|
||||
if (!hdr) {
|
||||
hdr = pjsip_generic_array_hdr_create(tdata->pool, name);
|
||||
created = PJ_TRUE;
|
||||
}
|
||||
if (!hdr) {
|
||||
ast_log(LOG_ERROR, "Failed to create array header.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (hdr->count == PJSIP_GENERIC_ARRAY_MAX_COUNT) {
|
||||
ast_log(LOG_ERROR, "Too many evalue in array, skipping %s.", value->ptr);
|
||||
return -E2BIG;
|
||||
}
|
||||
pj_strdup(tdata->pool, &hdr->values[hdr->count++], value);
|
||||
|
||||
/* Append header, if created. */
|
||||
if (created)
|
||||
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Add Sec-Agree to header. */
|
||||
pj_status_t volte_add_sec_agree(pjsip_tx_data *tdata)
|
||||
{
|
||||
pj_status_t status;
|
||||
|
||||
/* "Require: sec-agree" */
|
||||
status = add_value_array_hdr(tdata, &STR_REQUIRE, &STR_SEC_AGREE);
|
||||
if (status)
|
||||
return status;
|
||||
/* "Proxy-Require: sec-agree" */
|
||||
status = add_value_string_hdr(tdata, &STR_PROXY_REQUIRE, &STR_SEC_AGREE);
|
||||
if (status)
|
||||
return status;
|
||||
/* "Supported: path,sec-agree" */
|
||||
status = add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_PATH);
|
||||
if (status)
|
||||
return status;
|
||||
status = add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_SEC_AGREE);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Add initial Authorization header. */
|
||||
pj_status_t volte_init_authorization(pjsip_tx_data *tdata, const char *fromdomain, const char *username)
|
||||
{
|
||||
char authorization[1024];
|
||||
pj_status_t status;
|
||||
|
||||
snprintf(authorization, sizeof(authorization), "Digest uri=\"sip:%s\",usernmame=\"%s@%s\",response=\"\",realm=\"%s\",nonce=\"\"", fromdomain, username, fromdomain, fromdomain);
|
||||
|
||||
const pj_str_t authorization_str = {authorization, strlen(authorization)};
|
||||
status = add_value_string_hdr(tdata, &STR_AUTHORIZATION, &authorization_str);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Remove initial Authorization header. */
|
||||
pj_status_t volte_del_authorization(pjsip_tx_data *tdata)
|
||||
{
|
||||
pjsip_authorization_hdr *auth_hdr;
|
||||
|
||||
/* remove double authentication header */
|
||||
auth_hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &STR_AUTHORIZATION, NULL);
|
||||
if (auth_hdr) {
|
||||
pj_list_erase(auth_hdr);
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "netlink_xfrm.h"
|
||||
|
||||
void volte_cleanup_xfrm(void);
|
||||
pj_status_t volte_alloc_spi(void);
|
||||
pj_status_t volte_set_xfrm(const pj_str_t *alg, const pj_str_t *ealg, uint8_t *ik);
|
||||
|
||||
pj_status_t volte_add_sec_agree(pjsip_tx_data *tdata);
|
||||
pj_status_t volte_init_authorization(pjsip_tx_data *tdata, const char *fromdomain, const char *username);
|
||||
pj_status_t volte_del_authorization(pjsip_tx_data *tdata);
|
Loading…
Reference in New Issue