res_pjsip_endpoint_identifier_ip: Add endpoint identifier transport address.

Add a new identify_by option to res_pjsip_endpoint_identifier_ip
called 'transport' this matches endpoints based on the bound
ip address (local) instead of the 'ip' option, which matches on
the source ip address (remote).
This commit is contained in:
Sperl Viktor 2024-02-12 16:50:22 +01:00 committed by Sperl Viktor
parent 6222e73cd8
commit 420be52bbd
6 changed files with 132 additions and 14 deletions

View File

@ -658,8 +658,9 @@
; identified.
; "username": Identify by the From or To username and domain
; "auth_username": Identify by the Authorization username and realm
; "ip": Identify by the source IP address
; "ip": Identify by the source (remote) IP address
; "header": Identify by a configured SIP header value.
; "transport": Identify by the bound (local) IP address
; In the username and auth_username cases, if an exact match
; on both username and domain/realm fails, the match is
; retried with just the username.

View File

@ -0,0 +1,21 @@
"""add transport attribute to identify
Revision ID: 5c9dfe2b851c
Revises: 24c12d8e9014
Create Date: 2024-02-14 18:35:38.917103
"""
# revision identifiers, used by Alembic.
revision = '5c9dfe2b851c'
down_revision = '24c12d8e9014'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('ps_endpoint_id_ips', sa.Column('transport', sa.String(8)))
def downgrade():
op.drop_column('ps_endpoint_id_ips', 'transport')

View File

@ -619,10 +619,12 @@ enum ast_sip_endpoint_identifier_type {
AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME = (1 << 0),
/*! Identify based on user name in Auth header first, then From header */
AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME = (1 << 1),
/*! Identify based on source IP address */
/*! Identify based on source (remote) IP address */
AST_SIP_ENDPOINT_IDENTIFY_BY_IP = (1 << 2),
/*! Identify based on arbitrary headers */
AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER = (1 << 3),
/*! Identify based on bound (local) IP address */
AST_SIP_ENDPOINT_IDENTIFY_BY_TRANSPORT = (1 << 4),
};
AST_VECTOR(ast_sip_identify_by_vector, enum ast_sip_endpoint_identifier_type);

View File

@ -557,6 +557,14 @@
endpoint identification.
</para>
</enum>
<enum name="transport">
<para>Matches the endpoint based on the destination IP
address.
</para>
<para>This method of identification is not configured here
but simply allowed by this configuration option.
</para>
</enum>
</enumlist>
</description>
</configOption>

View File

@ -422,6 +422,9 @@ static const char *sip_endpoint_identifier_type2str(enum ast_sip_endpoint_identi
case AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER:
str = "header";
break;
case AST_SIP_ENDPOINT_IDENTIFY_BY_TRANSPORT:
str = "transport";
break;
}
return str;
}
@ -447,6 +450,8 @@ static int sip_endpoint_identifier_str2type(const char *str)
method = AST_SIP_ENDPOINT_IDENTIFY_BY_IP;
} else if (!strcasecmp(str, "header")) {
method = AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER;
} else if (!strcasecmp(str, "transport")) {
method = AST_SIP_ENDPOINT_IDENTIFY_BY_TRANSPORT;
} else {
method = -1;
}

View File

@ -110,6 +110,16 @@
</para></note>
</description>
</configOption>
<configOption name="transport">
<synopsis>Match against a transport type.</synopsis>
<description>
<para>When using the ip or transport identifier, this option
can be used to match the transport type <literal>(udp or tcp)
</literal> as well.</para>
<para>When omitted, or left empty, which is the default, it
won't match against the transport type.</para>
</description>
</configOption>
<configOption name="type">
<synopsis>Must be of type 'identify'.</synopsis>
</configOption>
@ -135,6 +145,8 @@ struct ip_identify_match {
AST_STRING_FIELD(match_header_name);
/*! SIP header value of the match_header string */
AST_STRING_FIELD(match_header_value);
/*! The name of the transport type */
AST_STRING_FIELD(transport);
);
/*! Compiled match_header regular expression when is_regex is non-zero */
regex_t regex_buf;
@ -148,6 +160,12 @@ struct ip_identify_match {
unsigned int is_regex:1;
};
/*! \brief Structure for a socket address with transport */
struct ast_sockaddr_with_tp {
struct ast_sockaddr addr;
char tp[128];
};
/*! \brief Destructor function for a matching object */
static void ip_identify_destroy(void *obj)
{
@ -245,18 +263,29 @@ static int header_identify_match_check(void *obj, void *arg, int flags)
static int ip_identify_match_check(void *obj, void *arg, int flags)
{
struct ip_identify_match *identify = obj;
struct ast_sockaddr *addr = arg;
struct ast_sockaddr_with_tp *addr_with_tp = arg;
struct ast_sockaddr address = addr_with_tp->addr;
int sense;
sense = ast_apply_ha(identify->matches, addr);
sense = ast_apply_ha(identify->matches, &address);
if (sense != AST_SENSE_ALLOW) {
ast_debug(3, "Source address %s matches identify '%s'\n",
ast_sockaddr_stringify(addr),
ast_debug(3, "Address %s matches identify '%s'\n",
ast_sockaddr_stringify(&address),
ast_sorcery_object_get_id(identify));
return CMP_MATCH;
if (ast_strlen_zero(identify->transport) || !strcasecmp(identify->transport, addr_with_tp->tp)) {
ast_debug(3, "Transport %s matches identify '%s'\n",
addr_with_tp->tp,
ast_sorcery_object_get_id(identify));
return CMP_MATCH;
} else {
ast_debug(3, "Transport %s match not matched identify '%s'\n",
addr_with_tp->tp,
ast_sorcery_object_get_id(identify));
return 0;
}
} else {
ast_debug(3, "Source address %s does not match identify '%s'\n",
ast_sockaddr_stringify(addr),
ast_debug(3, "Address %s does not match identify '%s'\n",
ast_sockaddr_stringify(&address),
ast_sorcery_object_get_id(identify));
return 0;
}
@ -297,18 +326,54 @@ static struct ast_sip_endpoint *common_identify(ao2_callback_fn *identify_match_
static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata)
{
struct ast_sockaddr addr = { { 0, } };
struct ast_sockaddr_with_tp addr_with_tp = { { { 0, } }, };
pj_ansi_strxcpy(addr_with_tp.tp, rdata->tp_info.transport->type_name, sizeof(addr_with_tp.tp));
ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port);
ast_sockaddr_parse(&addr_with_tp.addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
ast_sockaddr_set_port(&addr_with_tp.addr, rdata->pkt_info.src_port);
return common_identify(ip_identify_match_check, &addr);
return common_identify(ip_identify_match_check, &addr_with_tp);
}
static struct ast_sip_endpoint *transport_identify(pjsip_rx_data *rdata)
{
char buffer[PJ_INET6_ADDRSTRLEN];
pj_status_t status;
struct ast_sockaddr_with_tp addr_with_tp = { { { 0, } }, };
union pj_sockaddr sock = rdata->tp_info.transport->local_addr;
pj_ansi_strxcpy(addr_with_tp.tp, rdata->tp_info.transport->type_name, sizeof(addr_with_tp.tp));
if (sock.addr.sa_family == PJ_AF_INET6) {
status = pj_inet_ntop(PJ_AF_INET6, &(sock.ipv6.sin6_addr), buffer, PJ_INET6_ADDRSTRLEN);
if (status == PJ_SUCCESS && !strcmp(buffer, "::")) {
ast_log(LOG_WARNING, "Matching against '::' may be unpredictable.\n");
}
} else {
status = pj_inet_ntop(PJ_AF_INET, &(sock.ipv4.sin_addr), buffer, PJ_INET_ADDRSTRLEN);
if (status == PJ_SUCCESS && !strcmp(buffer, "0.0.0.0")) {
ast_log(LOG_WARNING, "Matching against '0.0.0.0' may be unpredictable.\n");
}
}
if (status == PJ_SUCCESS) {
ast_sockaddr_parse(&addr_with_tp.addr, buffer, PARSE_PORT_FORBID);
ast_sockaddr_set_port(&addr_with_tp.addr, rdata->tp_info.transport->local_name.port);
return common_identify(ip_identify_match_check, &addr_with_tp);
} else {
return NULL;
}
}
static struct ast_sip_endpoint_identifier ip_identifier = {
.identify_endpoint = ip_identify,
};
static struct ast_sip_endpoint_identifier transport_identifier = {
.identify_endpoint = transport_identify,
};
static struct ast_sip_endpoint *header_identify(pjsip_rx_data *rdata)
{
return common_identify(header_identify_match_check, rdata);
@ -462,6 +527,12 @@ static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj)
return -1;
}
if (!ast_strlen_zero(identify->transport)) {
if (ast_string_field_set(identify, transport, identify->transport)) {
return -1;
}
}
if (!ast_strlen_zero(identify->match_header)) {
char *c_header;
char *c_value;
@ -785,10 +856,17 @@ static int cli_print_body(void *obj, void *arg, int flags)
addr, ast_sockaddr_cidr_bits(&match->netmask));
}
if (!ast_strlen_zero(ident->transport)) {
ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
indent,
"Transport",
ident->transport);
}
if (!ast_strlen_zero(ident->match_header)) {
ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
indent,
"Match",
"MatchHeader",
ident->match_header);
}
@ -852,10 +930,12 @@ static int load_module(void)
ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, match_to_str, match_to_var_list, 0, 0);
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_header", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_header));
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "srv_lookups", "yes", OPT_BOOL_T, 1, FLDSET(struct ip_identify_match, srv_lookups));
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, transport));
ast_sorcery_load_object(ast_sip_get_sorcery(), "identify");
ast_sip_register_endpoint_identifier_with_name(&ip_identifier, "ip");
ast_sip_register_endpoint_identifier_with_name(&header_identifier, "header");
ast_sip_register_endpoint_identifier_with_name(&transport_identifier, "transport");
ast_sip_register_endpoint_formatter(&endpoint_identify_formatter);
cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
@ -891,6 +971,7 @@ static int unload_module(void)
ast_sip_unregister_endpoint_formatter(&endpoint_identify_formatter);
ast_sip_unregister_endpoint_identifier(&header_identifier);
ast_sip_unregister_endpoint_identifier(&ip_identifier);
ast_sip_unregister_endpoint_identifier(&transport_identifier);
return 0;
}