diff --git a/CHANGES b/CHANGES
index 68617a441893b7eef6371743eccb41452dd91fdf..683a164ab0b41cd8aa0b0b69bdb4d1104f9e38c3 100644
--- a/CHANGES
+++ b/CHANGES
@@ -26,6 +26,16 @@ chan_sip
    headers be retrieved from the REFER message and made accessible to the
    dialplan in the hash TRANSFER_DATA.
 
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------
+------------------------------------------------------------------------------
+
+res_pjsip
+------------------
+ * The "identify_by" on endpoints can now be set to "ip" to restrict an endpoint
+   being matched based only on IP address. To ensure no behavior change the
+   default has been changed to "username,ip".
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 15.0.0 to Asterisk 15.1.0 ------------
 ------------------------------------------------------------------------------
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index c535aaf04c4402f9e9de5138e22bb83cbd15a8b2..800ff0f444248fc8c8961d937ece17bc3f1fa0a4 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -634,9 +634,11 @@
                         ; identified.
                         ; "username": Identify by the From or To username and domain
                         ; "auth_username": Identify by the Authorization username and realm
-                        ; In all cases, if an exact match on username and domain/realm fails,
-                        ; the match will be retried with just the username.
-                        ; (default: "username")
+                        ; "ip": Identify by the source IP address
+                        ; In username and auth_username cases, if an exact match on
+                        ; username and domain/realm fails, the match will be retried
+                        ; with just the username.
+                        ; (default: "username,ip")
 ;redirect_method=user   ; How redirects received from an endpoint are handled
                         ; (default: "user")
 ;mailboxes=     ; NOTIFY the endpoint when state changes for any of the specified mailboxes.
diff --git a/contrib/ast-db-manage/config/versions/20abce6d1e3c_add_pjsip_identify_by_ip.py b/contrib/ast-db-manage/config/versions/20abce6d1e3c_add_pjsip_identify_by_ip.py
new file mode 100644
index 0000000000000000000000000000000000000000..d457c9233d6d0f3071971cc5d89554eaf6fa97e4
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/20abce6d1e3c_add_pjsip_identify_by_ip.py
@@ -0,0 +1,46 @@
+"""add pjsip identify by ip
+
+Revision ID: 20abce6d1e3c
+Revises: a1698e8bb9c5
+Create Date: 2017-10-24 15:44:06.404774
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '20abce6d1e3c'
+down_revision = 'a1698e8bb9c5'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def enum_update(table_name, column_name, enum_name, enum_values):
+    if op.get_context().bind.dialect.name != 'postgresql':
+        if op.get_context().bind.dialect.name == 'mssql':
+            op.drop_constraint('ck_ps_endpoints_identify_by_pjsip_identify_by_values', 'ps_endpoints')
+        op.alter_column(table_name, column_name,
+                        type_=sa.Enum(*enum_values, name=enum_name))
+        return
+
+    # Postgres requires a few more steps
+    tmp = enum_name + '_tmp'
+
+    op.execute('ALTER TYPE ' + enum_name + ' RENAME TO ' + tmp)
+
+    updated = sa.Enum(*enum_values, name=enum_name)
+    updated.create(op.get_bind(), checkfirst=False)
+
+    op.execute('ALTER TABLE ' + table_name + ' ALTER COLUMN ' + column_name +
+               ' TYPE ' + enum_name + ' USING identify_by::text::' + enum_name)
+
+    op.execute('DROP TYPE ' + tmp)
+
+
+def upgrade():
+    enum_update('ps_endpoints', 'identify_by', 'pjsip_identify_by_values',
+                ['username', 'auth_username', 'ip'])
+
+
+def downgrade():
+    enum_update('ps_endpoints', 'identify_by', 'pjsip_identify_by_values',
+                ['username', 'auth_username'])
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index e6ccf0a1d4f0de0a8a23640f7bf22f6face94708..e71eb98d79e74422e56a242e1025a97e6f1f541e 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -437,6 +437,8 @@ 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 */
+	AST_SIP_ENDPOINT_IDENTIFY_BY_IP = (1 << 2),
 };
 AST_VECTOR(ast_sip_identify_by_vector, enum ast_sip_endpoint_identifier_type);
 
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index f81d34ca447a3f1b7a19bf2c8400c05c78da2433..7499ded3e1ceb7769eb7c692a9618038f91cde76 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -268,15 +268,17 @@
 				<configOption name="ice_support" default="no">
 					<synopsis>Enable the ICE mechanism to help traverse NAT</synopsis>
 				</configOption>
-				<configOption name="identify_by" default="username,location">
+				<configOption name="identify_by" default="username,ip">
 					<synopsis>Way(s) for Endpoint to be identified</synopsis>
 					<description><para>
 						Endpoints and aors can be identified in multiple ways. Currently, the supported
 						options are <literal>username</literal>, which matches the endpoint or aor id based on
-						the username and domain in the From header (or To header for aors), and
+						the username and domain in the From header (or To header for aors),
 						<literal>auth_username</literal>, which matches the endpoint or aor id based on the
-						username and realm in the Authentication header.  In all cases, if an exact match
-						on both username and domain/realm fails, the match will be retried with just the username.
+						username and realm in the Authentication header, and <literal>ip</literal> which matches
+						an endpoint based on the source IP address.  In the <literal>username</literal> and
+						<literal>auth_username</literal> cases, if an exact match on both username and
+						domain/realm fails, the match will be retried with just the username.
 						</para>
 						<note><para>
 						Identification by auth_username has some security considerations because an
@@ -292,14 +294,19 @@
 						configuration object.
 						</para></note>
 						<note><para>Endpoints can also be identified by IP address; however, that method
-						of identification is not handled by this configuration option. See the documentation
-						for the <literal>identify</literal> configuration section for more details on that
-						method of endpoint identification. If this option is set and an <literal>identify</literal>
-						configuration section exists for the endpoint, then the endpoint can be identified in
-						multiple ways.</para></note>
+						of identification is not configured but simply allowed by this configuration option.
+						See the documentation for the <literal>identify</literal> configuration section for
+						more details on that method of endpoint identification.</para></note>
+						<note><para>
+						This option controls both how an endpoint is matched for incoming traffic and also how
+						an AoR is determined if a registration occurs. If <literal>ip</literal> is set alone
+						then incoming registration will not find an AoR and the registration attempt will fail.
+						If you want to allow incoming registrations to succeed you must set a second identify
+						method such as <literal>username</literal> in this case.</para></note>
 						<enumlist>
 							<enum name="username" />
 							<enum name="auth_username" />
+							<enum name="ip" />
 						</enumlist>
 					</description>
 				</configOption>
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 653cb98ac77807e7e1fb71441f4816ca9d4af57b..6db5b38985ab1c204a3f2b38d2ab49090361f3c2 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -582,8 +582,10 @@ static int ident_handler(const struct aco_option *opt, struct ast_variable *var,
 
 		if (!strcasecmp(val, "username")) {
 			method = AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME;
-		} else	if (!strcasecmp(val, "auth_username")) {
+		} else if (!strcasecmp(val, "auth_username")) {
 			method = AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME;
+		} else if (!strcasecmp(val, "ip")) {
+			method = AST_SIP_ENDPOINT_IDENTIFY_BY_IP;
 		} else {
 			ast_log(LOG_ERROR, "Unrecognized identification method %s specified for endpoint %s\n",
 					val, ast_sorcery_object_get_id(endpoint));
@@ -628,6 +630,9 @@ static int ident_to_str(const void *obj, const intptr_t *args, char **buf)
 		case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME :
 			method = "auth_username";
 			break;
+		case AST_SIP_ENDPOINT_IDENTIFY_BY_IP :
+			method = "ip";
+			break;
 		default:
 			continue;
 		}
@@ -1901,7 +1906,7 @@ int ast_res_pjsip_initialize_configuration(void)
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aors", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, aors));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.address));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bind_rtp_to_media_address", "no", OPT_BOOL_T, 1, STRFLDSET(struct ast_sip_endpoint, media.bind_rtp_to_media_address));
-	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username", ident_handler, ident_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username,ip", ident_handler, ident_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "direct_media", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.direct_media.enabled));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, direct_media_method_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "connected_line_method", "invite", connected_line_method_handler, connected_line_method_to_str, NULL, 0, 0);
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index 30bfc2618c5ca4da66d2c4f89a02b891b53d2ea8..8b92cef27e8f8aa3b5db41c4a5caa8f1546a250f 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -227,7 +227,14 @@ static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata)
 	}
 
 	endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", match->endpoint_name);
+
 	if (endpoint) {
+		if (!(endpoint->ident_method & AST_SIP_ENDPOINT_IDENTIFY_BY_IP)) {
+			ast_debug(3, "Endpoint '%s' found for '%s' but 'ip' method not supported'\n", match->endpoint_name,
+				ast_sockaddr_stringify(&addr));
+			ao2_cleanup(endpoint);
+			return NULL;
+		}
 		ast_debug(3, "Retrieved endpoint %s\n", ast_sorcery_object_get_id(endpoint));
 	} else {
 		ast_log(LOG_WARNING, "Identify section '%s' points to endpoint '%s' but endpoint could not be looked up\n",