diff --git a/CHANGES b/CHANGES
index 91e170ffbb20c7519239106cb0ab30670c7e38a2..78a464f44430a75810c636b5e42fa327030ab1c4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -232,6 +232,36 @@ app_confbridge
    conference state and made the locked column a yes/no value instead of a
    locked/unlocked value.
 
+REDIRECTING(reason)
+------------------
+ * The REDIRECTING(reason) value is now treated consistently between
+   chan_sip and chan_pjsip.
+
+   Both channel drivers match incoming reason values with values documented
+   by REDIRECTING(reason) and values documented by RFC5806 regardless of
+   whether they are quoted or not.  RFC5806 values are mapped to the
+   equivalent REDIRECTING(reason) documented value and is set in
+   REDIRECTING(reason).  e.g., an incoming RFC5806 'unconditional' value or a
+   quoted string version ('"unconditional"') is converted to
+   REDIRECTING(reason)'s 'cfu' value.  The user's dialplan only needs to deal
+   with 'cfu' instead of any of the aliases.
+
+   The incoming 480 response reason text supported by chan_sip checks for
+   known reason values and if not matched then puts quotes around the reason
+   string and assigns that to REDIRECTING(reason).
+
+   Both channel drivers send outgoing known REDIRECTING(reason) values as the
+   unquoted RFC5806 equivalent.  User custom values are either sent as is or
+   with added quotes if SIP doesn't allow a character within the value as
+   part of a RFC3261 Section 25.1 token.  Note that there are still
+   limitations on what characters can be put in a custom user value.  e.g.,
+   embedding quotes in the middle of the reason string is just going to cause
+   you grief.
+
+ * Setting a REDIRECTING(reason) value now recognizes RFC5806 aliases.
+   e.g., Setting REDIRECTING(reason) to 'unconditional' is converted to the
+   'cfu' value.
+
 res_pjproject
 ------------------
  * This module is the successor of res_pjsip_log_forwarder.  As well as
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 0b0d114820cd7b59ab920a4b50e4d107b1dfcacb..2d5843d91abbe04f9146e37c0deec5f3af0a2013 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -710,7 +710,7 @@ static const struct  cfsip_methods {
  */
 static const struct sip_reasons {
 	enum AST_REDIRECTING_REASON code;
-	char * const text;
+	const char *text;
 } sip_reason_table[] = {
 	{ AST_REDIRECTING_REASON_UNKNOWN, "unknown" },
 	{ AST_REDIRECTING_REASON_USER_BUSY, "user-busy" },
@@ -723,8 +723,8 @@ static const struct sip_reasons {
 	{ AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" },
 	{ AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" },
 	{ AST_REDIRECTING_REASON_AWAY, "away" },
-	{ AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"},
-	{ AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm"},
+	{ AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte" },		/* Non-standard */
+	{ AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm" },	/* Non-standard */
 };
 
 
@@ -2375,52 +2375,54 @@ static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue)
 	return errorvalue;
 }
 
-static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text)
+/*!
+ * \internal
+ * \brief Determine if the given string is a SIP token.
+ * \since 13.8.0
+ *
+ * \param str String to determine if is a SIP token.
+ *
+ * \note A token is defined by RFC3261 Section 25.1
+ *
+ * \return Non-zero if the string is a SIP token.
+ */
+static int sip_is_token(const char *str)
 {
-	enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN;
-	int i;
+	int is_token;
+
+	if (ast_strlen_zero(str)) {
+		/* An empty string is not a token. */
+		return 0;
+	}
 
-	for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) {
-		if (!strcasecmp(text, sip_reason_table[i].text)) {
-			ast = sip_reason_table[i].code;
+	is_token = 1;
+	do {
+		if (!isalnum(*str)
+			&& !strchr("-.!%*_+`'~", *str)) {
+			/* The character is not allowed in a token. */
+			is_token = 0;
 			break;
 		}
-	}
+	} while (*++str);
 
-	return ast;
+	return is_token;
 }
 
-static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason, int *table_lookup)
+static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason)
 {
-	int code = reason->code;
+	int idx;
+	int code;
 
-	/* If there's a specific string set, then we just
-	 * use it.
-	 */
+	/* use specific string if given */
 	if (!ast_strlen_zero(reason->str)) {
-		/* If we care about whether this can be found in
-		 * the table, then we need to check about that.
-		 */
-		if (table_lookup) {
-			/* If the string is literally "unknown" then don't bother with the lookup
-			 * because it can lead to a false negative.
-			 */
-			if (!strcasecmp(reason->str, "unknown") ||
-					sip_reason_str_to_code(reason->str) != AST_REDIRECTING_REASON_UNKNOWN) {
-				*table_lookup = TRUE;
-			} else {
-				*table_lookup = FALSE;
-			}
-		}
 		return reason->str;
 	}
 
-	if (table_lookup) {
-		*table_lookup = TRUE;
-	}
-
-	if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) {
-		return sip_reason_table[code].text;
+	code = reason->code;
+	for (idx = 0; idx < ARRAY_LEN(sip_reason_table); ++idx) {
+		if (code == sip_reason_table[idx].code) {
+			return sip_reason_table[idx].text;
+		}
 	}
 
 	return "unknown";
@@ -14219,7 +14221,7 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
 {
 	struct ast_party_id diverting_from;
 	const char *reason;
-	int found_in_table;
+	const char *quote_str;
 	char header_text[256];
 	char encoded_number[SIPBUFSIZE/2];
 
@@ -14244,17 +14246,18 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
 		ast_copy_string(encoded_number, diverting_from.number.str, sizeof(encoded_number));
 	}
 
-	reason = sip_reason_code_to_str(&ast_channel_redirecting(pvt->owner)->reason, &found_in_table);
+	reason = sip_reason_code_to_str(&ast_channel_redirecting(pvt->owner)->reason);
+
+	/* Reason is either already quoted or it is a token to not need quotes added. */
+	quote_str = *reason == '\"' || sip_is_token(reason) ? "" : "\"";
 
 	/* We at least have a number to place in the Diversion header, which is enough */
 	if (!diverting_from.name.valid
 		|| ast_strlen_zero(diverting_from.name.str)) {
 		snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=%s%s%s",
-				encoded_number,
-				ast_sockaddr_stringify_host_remote(&pvt->ourip),
-				found_in_table ? "" : "\"",
-				reason,
-				found_in_table ? "" : "\"");
+			encoded_number,
+			ast_sockaddr_stringify_host_remote(&pvt->ourip),
+			quote_str, reason, quote_str);
 	} else {
 		char escaped_name[SIPBUFSIZE/2];
 		if (sip_cfg.pedanticsipchecking) {
@@ -14263,12 +14266,10 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
 			ast_copy_string(escaped_name, diverting_from.name.str, sizeof(escaped_name));
 		}
 		snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s%s%s",
-				escaped_name,
-				encoded_number,
-				ast_sockaddr_stringify_host_remote(&pvt->ourip),
-				found_in_table ? "" : "\"",
-				reason,
-				found_in_table ? "" : "\"");
+			escaped_name,
+			encoded_number,
+			ast_sockaddr_stringify_host_remote(&pvt->ourip),
+			quote_str, reason, quote_str);
 	}
 
 	add_header(req, "Diversion", header_text);
@@ -17740,6 +17741,9 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
 	char *params, *reason_param = NULL;
 	struct sip_request *req;
 
+	ast_assert(reason_code != NULL);
+	ast_assert(reason_str != NULL);
+
 	req = oreq ? oreq : &p->initreq;
 
 	ast_copy_string(tmp, sip_get_header(req, "Diversion"), sizeof(tmp));
@@ -17773,16 +17777,6 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
 			if ((end = strchr(reason_param, ';'))) {
 				*end = '\0';
 			}
-			/* Remove enclosing double-quotes */
-			if (*reason_param == '"')
-				reason_param = ast_strip_quoted(reason_param, "\"", "\"");
-			if (!ast_strlen_zero(reason_param)) {
-				sip_set_redirstr(p, reason_param);
-				if (p->owner) {
-					pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause);
-					pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param);
-				}
-			}
 		}
 	}
 
@@ -17814,12 +17808,27 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
 	}
 
 	if (!ast_strlen_zero(reason_param)) {
-		if (reason_code) {
-			*reason_code = sip_reason_str_to_code(reason_param);
+		*reason_str = ast_strdup(reason_param);
+
+		/* Remove any enclosing double-quotes */
+		if (*reason_param == '"') {
+			reason_param = ast_strip_quoted(reason_param, "\"", "\"");
 		}
 
-		if (reason_str) {
-			*reason_str = ast_strdup(reason_param);
+		*reason_code = ast_redirecting_reason_parse(reason_param);
+		if (*reason_code < 0) {
+			*reason_code = AST_REDIRECTING_REASON_UNKNOWN;
+		} else {
+			ast_free(*reason_str);
+			*reason_str = ast_strdup("");
+		}
+
+		if (!ast_strlen_zero(reason_param)) {
+			sip_set_redirstr(p, reason_param);
+			if (p->owner) {
+				pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause);
+				pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param);
+			}
 		}
 	}
 
@@ -22698,10 +22707,11 @@ static void change_redirecting_information(struct sip_pvt *p, struct sip_request
 		redirecting->to.name.str = redirecting_to_name;
 	}
 	redirecting->reason.code = reason;
+	ast_free(redirecting->reason.str);
+	redirecting->reason.str = reason_str;
 	if (reason_str) {
-		ast_debug(3, "Got redirecting reason %s\n", reason_str);
-		ast_free(redirecting->reason.str);
-		redirecting->reason.str = reason_str;
+		ast_debug(3, "Got redirecting reason %s\n", ast_strlen_zero(reason_str)
+			? sip_reason_code_to_str(&redirecting->reason) : reason_str);
 	}
 }
 
@@ -23502,14 +23512,22 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
 		if (p->owner && !req->ignore) {
 			struct ast_party_redirecting redirecting;
 			struct ast_set_party_redirecting update_redirecting;
+			char *quoted_rest = ast_alloca(strlen(rest) + 3);
+
 			ast_party_redirecting_set_init(&redirecting, ast_channel_redirecting(p->owner));
 			memset(&update_redirecting, 0, sizeof(update_redirecting));
 
-			redirecting.reason.code = sip_reason_str_to_code(rest);
-			redirecting.reason.str = ast_strdup(rest);
+			redirecting.reason.code = ast_redirecting_reason_parse(rest);
+			if (redirecting.reason.code < 0) {
+				sprintf(quoted_rest, "\"%s\"", rest);/* Safe */
+
+				redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+				redirecting.reason.str = quoted_rest;
+			} else {
+				redirecting.reason.str = "";
+			}
 
 			ast_channel_queue_redirecting_update(p->owner, &redirecting, &update_redirecting);
-			ast_party_redirecting_free(&redirecting);
 
 			ast_queue_control(p->owner, AST_CONTROL_BUSY);
 		}
diff --git a/main/callerid.c b/main/callerid.c
index 69fe6ff70629f835fa3b71ec7f0e8fa8416ff15c..d2b3218922897c08aaa270ec73fbdf13bd074d3f 100644
--- a/main/callerid.c
+++ b/main/callerid.c
@@ -1209,7 +1209,16 @@ static const struct ast_value_translation redirecting_reason_types[] = {
 	{ AST_REDIRECTING_REASON_OUT_OF_ORDER,   "out_of_order", "Called DTE Out-Of-Order" },
 	{ AST_REDIRECTING_REASON_AWAY,           "away",         "Callee is Away" },
 	{ AST_REDIRECTING_REASON_CALL_FWD_DTE,   "cf_dte",       "Call Forwarding By The Called DTE" },
-	{ AST_REDIRECTING_REASON_SEND_TO_VM,     "send_to_vm",   "Call is being redirected to user's voicemail"},
+	{ AST_REDIRECTING_REASON_SEND_TO_VM,     "send_to_vm",   "Call is being redirected to user's voicemail" },
+
+	/* Convenience SIP aliases.  Alias descriptions are not used. */
+	{ AST_REDIRECTING_REASON_USER_BUSY,      "user-busy" },
+	{ AST_REDIRECTING_REASON_NO_ANSWER,      "no-answer" },
+	{ AST_REDIRECTING_REASON_UNCONDITIONAL,  "unconditional" },
+	{ AST_REDIRECTING_REASON_TIME_OF_DAY,    "time-of-day" },
+	{ AST_REDIRECTING_REASON_DO_NOT_DISTURB, "do-not-disturb" },
+	{ AST_REDIRECTING_REASON_FOLLOW_ME,      "follow-me" },
+	{ AST_REDIRECTING_REASON_OUT_OF_ORDER,   "out-of-service" },
 /* *INDENT-ON* */
 };
 
@@ -1232,7 +1241,7 @@ const char *ast_redirecting_reason_describe(int data)
 
 	for (index = 0; index < ARRAY_LEN(redirecting_reason_types); ++index) {
 		if (redirecting_reason_types[index].value == data) {
-			return redirecting_reason_types[index].description;
+			return redirecting_reason_types[index].description ?: "Redirecting reason alias-bug";
 		}
 	}
 
diff --git a/res/res_pjsip_diversion.c b/res/res_pjsip_diversion.c
index ea2c7cd13351fcfc0d78646a0758f7e2bc99a207..f1a6ddf77e5bb9456cd5deb933b7e863ae0e01ca 100644
--- a/res/res_pjsip_diversion.c
+++ b/res/res_pjsip_diversion.c
@@ -37,6 +37,39 @@
 
 static const pj_str_t diversion_name = { "Diversion", 9 };
 
+/*!
+ * \internal
+ * \brief Determine if the given string is a SIP token.
+ * \since 13.8.0
+ *
+ * \param str String to determine if is a SIP token.
+ *
+ * \note A token is defined by RFC3261 Section 25.1
+ *
+ * \return Non-zero if the string is a SIP token.
+ */
+static int sip_is_token(const char *str)
+{
+	int is_token;
+
+	if (ast_strlen_zero(str)) {
+		/* An empty string is not a token. */
+		return 0;
+	}
+
+	is_token = 1;
+	do {
+		if (!isalnum(*str)
+			&& !strchr("-.!%*_+`'~", *str)) {
+			/* The character is not allowed in a token. */
+			is_token = 0;
+			break;
+		}
+	} while (*++str);
+
+	return is_token;
+}
+
 /*! \brief Diversion header reasons
  *
  * The core defines a bunch of constants used to define
@@ -46,7 +79,7 @@ static const pj_str_t diversion_name = { "Diversion", 9 };
  */
 static const struct reasons {
 	enum AST_REDIRECTING_REASON code;
-	char *const text;
+	const char *text;
 } reason_table[] = {
 	{ AST_REDIRECTING_REASON_UNKNOWN, "unknown" },
 	{ AST_REDIRECTING_REASON_USER_BUSY, "user-busy" },
@@ -59,39 +92,28 @@ static const struct reasons {
 	{ AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" },
 	{ AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" },
 	{ AST_REDIRECTING_REASON_AWAY, "away" },
-	{ AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"},
-	{ AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm"},
+	{ AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte" },		/* Non-standard */
+	{ AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm" },	/* Non-standard */
 };
 
 static const char *reason_code_to_str(const struct ast_party_redirecting_reason *reason)
 {
-	int code = reason->code;
+	int idx;
+	int code;
 
 	/* use specific string if given */
 	if (!ast_strlen_zero(reason->str)) {
 		return reason->str;
 	}
 
-	if (code >= 0 && code < ARRAY_LEN(reason_table)) {
-		return reason_table[code].text;
-	}
-
-	return "unknown";
-}
-
-static enum AST_REDIRECTING_REASON reason_str_to_code(const char *text)
-{
-	enum AST_REDIRECTING_REASON code = AST_REDIRECTING_REASON_UNKNOWN;
-	int i;
-
-	for (i = 0; i < ARRAY_LEN(reason_table); ++i) {
-		if (!strcasecmp(text, reason_table[i].text)) {
-			code = reason_table[i].code;
-			break;
+	code = reason->code;
+	for (idx = 0; idx < ARRAY_LEN(reason_table); ++idx) {
+		if (code == reason_table[idx].code) {
+			return reason_table[idx].text;
 		}
 	}
 
-	return code;
+	return "unknown";
 }
 
 static pjsip_fromto_hdr *get_diversion_header(pjsip_rx_data *rdata)
@@ -159,13 +181,31 @@ static void set_redirecting_reason(pjsip_fromto_hdr *hdr,
 {
 	static const pj_str_t reason_name = { "reason", 6 };
 	pjsip_param *reason = pjsip_param_find(&hdr->other_param, &reason_name);
+	char *reason_str;
 
 	if (!reason) {
 		return;
 	}
 
 	set_redirecting_value(&data->str, &reason->value);
-	data->code = reason_str_to_code(data->str);
+	if (!data->str) {
+		/* Oops, allocation failure */
+		return;
+	}
+	reason_str = ast_strdupa(data->str);
+
+	/* Remove any enclosing double-quotes */
+	if (*reason_str == '"') {
+		reason_str = ast_strip_quoted(reason_str, "\"", "\"");
+	}
+
+	data->code = ast_redirecting_reason_parse(reason_str);
+	if (data->code < 0) {
+		data->code = AST_REDIRECTING_REASON_UNKNOWN;
+	} else {
+		ast_free(data->str);
+		data->str = ast_strdup("");
+	}
 }
 
 static void set_redirecting(struct ast_sip_session *session,
@@ -251,6 +291,9 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
 	pjsip_sip_uri *uri;
 	pjsip_param *param;
 	pjsip_fromto_hdr *old_hdr;
+	const char *reason_str;
+	const char *quote_str;
+	char *reason_buf;
 
 	struct ast_party_id *id = &data->from;
 	pjsip_uri *base = PJSIP_MSG_FROM_HDR(tdata->msg)->uri;
@@ -272,7 +315,17 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
 
 	param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param);
 	param->name = pj_str("reason");
-	param->value = pj_str((char*)reason_code_to_str(&data->reason));
+
+	reason_str = reason_code_to_str(&data->reason);
+
+	/* Reason is either already quoted or it is a token to not need quotes added. */
+	quote_str = *reason_str == '\"' || sip_is_token(reason_str) ? "" : "\"";
+
+	reason_buf = pj_pool_alloc(tdata->pool, strlen(reason_str) + 3);
+	sprintf(reason_buf, "%s%s%s", quote_str, reason_str, quote_str);/* Safe */
+
+	param->value = pj_str(reason_buf);
+
 	pj_list_insert_before(&hdr->other_param, param);
 
 	hdr->uri = (pjsip_uri *) name_addr;