From: Sperl Viktor Date: Thu, 28 Mar 2024 15:22:24 +0000 (+0100) Subject: res_pjsip_endpoint_identifier_ip: Add endpoint identifier transport address. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c8769f3d5a8b365d919030713982209516bac3b1;p=thirdparty%2Fasterisk.git 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). UserNote: set identify_by=transport for the pjsip endpoint. Then use the existing 'match' option and the new 'transport' option of the identify. Fixes: #672 --- diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index 6b8936b2d4..d52c7afb4e 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -197,6 +197,47 @@ ;tcp_keepalive_interval_time=10 ; The time in seconds between individual keepalive probes ;tcp_keepalive_probe_count=5 ; The maximum number of keepalive probes TCP should send before dropping the connection +;===============ENDPOINT IDENTIFIER TRANSPORT EXAMPLE========================== +; +; When Asterisk has multiple bound IP addresses, and endpoints don't use any +; other means of identification (e.g.: username), the transports' bind addresses +; can be used to identify them. Can be useful in case you're connecting to the +; same ITSP multiple times on different IPs / NICs. +; +;[transport-eth0] +;type=transport +;protocol=tcp +;bind=192.168.1.1:5060 +; +;[transport-eth1] +;type=transport +;protocol=udp +;bind=192.168.2.1:5060 +; +; +;[myprovider-a] +;type=endpoint +;transport=transport-eth0 +;identify_by=transport +; +;[myprovider-b] +;type=endpoint +;transport=transport-eth1 +;identify_by=transport +; +; +;[identify-a] +;type=identify +;endpoint=myprovider-a +;match=192.168.1.1:5060 ; This is the bind address of [transport-eth0] +;;transport=tcp ; Optionally, this is the transport protocol of [transport-eth0] +; +;[identify-b] +;type=identify +;endpoint=myprovider-b +;match=192.168.2.1:5060 ; This is the bind address of [transport-eth1] +;;transport=udp ; Optionally, This is the transport protocol of [transport-eth1] + ;===============OUTBOUND REGISTRATION WITH OUTBOUND AUTHENTICATION============ ; ; This is a simple registration that works with some SIP trunking providers. @@ -684,9 +725,10 @@ ; 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. ; "request_uri": Identify by the configured SIP request URI. + ; "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. @@ -1271,9 +1313,10 @@ ; (default: "no") ;endpoint_identifier_order=ip,username,anonymous ; The order by which endpoint identifiers are given priority. - ; Currently, "ip", "header", "username", "auth_username" and "anonymous" - ; are valid identifiers as registered by the res_pjsip_endpoint_identifier_* - ; modules. Some modules like res_pjsip_endpoint_identifier_user register + ; Currently, "ip", "header", "request_uri", "transport", "username", + ; "auth_username" and "anonymous" are valid identifiers as registered by + ; the res_pjsip_endpoint_identifier_* modules. + ; Some modules like res_pjsip_endpoint_identifier_user register ; more than one identifier. Use the CLI command "pjsip show identifiers" ; to see the identifiers currently available. ; (default: ip,username,anonymous) @@ -1461,6 +1504,8 @@ ;match= ; Comma separated list of IP addresses, networks, or hostnames to match ; against (default: "") ;match_header= ; SIP header with specified value to match against (default: "") +;match_request_uri= ; SIP request URI to match against (default: "") +;transport= ; Match ageinst the transport protocol (tcp or udp) (default: "") ;type= ; Must be of type identify (default: "") diff --git a/contrib/ast-db-manage/config/versions/d5122576cca8_add_transport_attribute_to_identify.py b/contrib/ast-db-manage/config/versions/d5122576cca8_add_transport_attribute_to_identify.py new file mode 100644 index 0000000000..c75b97359f --- /dev/null +++ b/contrib/ast-db-manage/config/versions/d5122576cca8_add_transport_attribute_to_identify.py @@ -0,0 +1,20 @@ +"""add transport attribute to identify + +Revision ID: d5122576cca8 +Revises: cf150a175fd3 +Create Date: 2024-03-28 14:29:43.372496 + +""" + +# revision identifiers, used by Alembic. +revision = 'd5122576cca8' +down_revision = 'cf150a175fd3' + +from alembic import op +import sqlalchemy as sa + +def upgrade(): + op.add_column('ps_endpoint_id_ips', sa.Column('transport', sa.String(128))) + +def downgrade(): + op.drop_column('ps_endpoint_id_ips', 'transport') diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index d0e1e59223..984a38c673 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -617,6 +617,8 @@ enum ast_sip_endpoint_identifier_type { AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER = (1 << 3), /*! Identify based on request uri */ AST_SIP_ENDPOINT_IDENTIFY_BY_REQUEST_URI = (1 << 4), + /*! Identify based on bound (local) IP address */ + AST_SIP_ENDPOINT_IDENTIFY_BY_TRANSPORT = (1 << 5), }; AST_VECTOR(ast_sip_identify_by_vector, enum ast_sip_endpoint_identifier_type); diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml index 753037b555..094123fff5 100644 --- a/res/res_pjsip/pjsip_config.xml +++ b/res/res_pjsip/pjsip_config.xml @@ -565,6 +565,14 @@ but simply allowed by this configuration option. + + Matches the endpoint based on the destination IP + address. + + This method of identification is not configured here + but simply allowed by this configuration option. + + diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 58a50c484a..268607a988 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -426,6 +426,9 @@ static const char *sip_endpoint_identifier_type2str(enum ast_sip_endpoint_identi case AST_SIP_ENDPOINT_IDENTIFY_BY_REQUEST_URI: str = "request_uri"; break; + case AST_SIP_ENDPOINT_IDENTIFY_BY_TRANSPORT: + str = "transport"; + break; } return str; } @@ -453,6 +456,8 @@ static int sip_endpoint_identifier_str2type(const char *str) method = AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER; } else if (!strcasecmp(str, "request_uri")) { method = AST_SIP_ENDPOINT_IDENTIFY_BY_REQUEST_URI; + } else if (!strcasecmp(str, "transport")) { + method = AST_SIP_ENDPOINT_IDENTIFY_BY_TRANSPORT; } else { method = -1; } diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c index da5c363f3a..69e0c76064 100644 --- a/res/res_pjsip_endpoint_identifier_ip.c +++ b/res/res_pjsip_endpoint_identifier_ip.c @@ -124,6 +124,16 @@ + + Match against a transport type. + + When using the ip or transport identifier, this option + can be used to match the transport type (udp or tcp) + as well. + When omitted, or left empty, which is the default, it + won't match against the transport type. + + Must be of type 'identify'. @@ -151,6 +161,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_header_regex is non-zero */ regex_t regex_header_buf; @@ -168,6 +180,12 @@ struct ip_identify_match { unsigned int is_request_uri_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) { @@ -303,18 +321,29 @@ static int request_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)); + 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 CMP_MATCH; + 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; } @@ -355,18 +384,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_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_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"); + } + } - ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID); - ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port); + 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); + 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); @@ -530,6 +595,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; @@ -879,6 +950,13 @@ 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, @@ -954,11 +1032,13 @@ static int load_module(void) 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", "match_request_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_request_uri)); 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(&request_identifier, "request_uri"); + 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); @@ -995,6 +1075,7 @@ static int unload_module(void) ast_sip_unregister_endpoint_identifier(&header_identifier); ast_sip_unregister_endpoint_identifier(&request_identifier); ast_sip_unregister_endpoint_identifier(&ip_identifier); + ast_sip_unregister_endpoint_identifier(&transport_identifier); return 0; }