]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_pjsip_endpoint_identifier_ip: Add endpoint identifier transport address.
authorSperl Viktor <viktike32@gmail.com>
Thu, 28 Mar 2024 15:22:24 +0000 (16:22 +0100)
committerSperl Viktor <viktike32@gmail.com>
Wed, 3 Apr 2024 17:17:49 +0000 (17:17 +0000)
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
configs/samples/pjsip.conf.sample
contrib/ast-db-manage/config/versions/d5122576cca8_add_transport_attribute_to_identify.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
res/res_pjsip/pjsip_config.xml
res/res_pjsip/pjsip_configuration.c
res/res_pjsip_endpoint_identifier_ip.c

index 6b8936b2d4c5c7e5691be7f74a07ef0bd8f9353c..d52c7afb4e371c903d7372760203be9c59fced73 100644 (file)
 ;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.
                         ; 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.
             ; (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)
 ;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 (file)
index 0000000..c75b973
--- /dev/null
@@ -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')
index d0e1e5922350cd555c8b828830b5fe78dba81792..984a38c673a9f3e7a48f1ddc332a218accaa6289 100644 (file)
@@ -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);
 
index 753037b55590ebeccbe269cc0c50fcea6fac225a..094123fff5676c1077d6f1eb88752d928f12d7bc 100644 (file)
                                                                but simply allowed by this configuration option.
                                                                </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>
index 58a50c484a2f4a8a087892f5a5863b63b367b9f6..268607a98881f3d9f8d0a29ee6cefe03b6bfa72e 100644 (file)
@@ -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;
        }
index da5c363f3aa78d99f5aeaeac1e940e10af4b3157..69e0c760649d8c01019354be47a9d16556247bed 100644 (file)
                                                </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>
@@ -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;
 }