diff --git a/CHANGES b/CHANGES
index 031533fd2b1e17730268f811f6ecef9db3425888..c3774fa6ad0cd5dbe6ad0dadc3187175fc978844 100644
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,18 @@ AMI
    res_manager_presence_state.so. If the high frequency of these events is
    problematic for you, do not load these modules.
 
+chan_dahdi
+------------------
+ * SS7 support now requires libss7 v2.0 or later.
+
+ * Added SS7 support for connected line and redirecting.
+
+ * Most SS7 CLI commands are reworked as well as new SS7 commands added.
+   See online CLI help.
+
+ * Added several SS7 config option parameters described in
+   chan_dahdi.conf.sample.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 12.3.0 to Asterisk 12.4.0 ------------
 ------------------------------------------------------------------------------
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 9d3f462227449b3508397466a32e7f73c93a02b1..82dad8c2e17790a90d84f7b4ead13154fcfce504 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -117,6 +117,9 @@ CDRs:
    handler subroutine). In general, this is not the preferred default: this
    causes extra CDRs to be generated for a channel in many common dialplans.
 
+chan_dahdi:
+ - SS7 support now requires libss7 v2.0 or later.
+
 chan_sip:
  - Made set SIPREFERREDBYHDR as inheritable for better chan_pjsip
    interoperability.
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 71e5831e031126b920bdbe51b1b4325f89399379..adc025ae0672bdcec5b6124dca5609d3f0151ce1 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -81,7 +81,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #if defined(HAVE_SS7)
 #include "sig_ss7.h"
-#if defined(LIBSS7_ABI_COMPATIBILITY)
+#if !defined(LIBSS7_ABI_COMPATIBILITY)
+#error "Upgrade your libss7"
+#elif LIBSS7_ABI_COMPATIBILITY != 2
 #error "Your installed libss7 is not compatible"
 #endif
 #endif	/* defined(HAVE_SS7) */
@@ -611,6 +613,7 @@ struct dahdi_ss7 {
 static struct dahdi_ss7 linksets[NUM_SPANS];
 
 static int cur_ss7type = -1;
+static int cur_slc = -1;
 static int cur_linkset = -1;
 static int cur_pointcode = -1;
 static int cur_cicbeginswith = -1;
@@ -758,8 +761,8 @@ const char * const subnames[] = {
 	MEMBER(dahdi_pvt, mwimonitoractive, AST_DATA_BOOLEAN)			\
 	MEMBER(dahdi_pvt, mwisendactive, AST_DATA_BOOLEAN)			\
 	MEMBER(dahdi_pvt, inservice, AST_DATA_BOOLEAN)				\
-	MEMBER(dahdi_pvt, locallyblocked, AST_DATA_BOOLEAN)			\
-	MEMBER(dahdi_pvt, remotelyblocked, AST_DATA_BOOLEAN)			\
+	MEMBER(dahdi_pvt, locallyblocked, AST_DATA_UNSIGNED_INTEGER)		\
+	MEMBER(dahdi_pvt, remotelyblocked, AST_DATA_UNSIGNED_INTEGER)		\
 	MEMBER(dahdi_pvt, manages_span_alarms, AST_DATA_BOOLEAN)		\
 	MEMBER(dahdi_pvt, use_smdi, AST_DATA_BOOLEAN)				\
 	MEMBER(dahdi_pvt, context, AST_DATA_STRING)				\
@@ -873,7 +876,8 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
 			.internationalprefix = "",
 			.nationalprefix = "",
 			.subscriberprefix = "",
-			.unknownprefix = ""
+			.unknownprefix = "",
+			.networkroutedprefix = ""
 		},
 #endif	/* defined(HAVE_SS7) */
 #ifdef HAVE_OPENR2
@@ -3019,6 +3023,34 @@ static void my_ss7_set_loopback(void *pvt, int enable)
 }
 #endif	/* defined(HAVE_SS7) */
 
+#if defined(HAVE_SS7)
+/*!
+ * \internal
+ * \brief Find the linkset to which SS7 belongs.
+ * \since 11.0
+ *
+ * \param ss7 structure to match on.
+ *
+ * \retval linkset if found.
+ * \retval NULL if not found.
+ */
+static struct sig_ss7_linkset *my_ss7_find_linkset(struct ss7 *ss7)
+{
+	int idx;
+
+	if (!ss7) {
+		return NULL;
+	}
+
+	for (idx = 0; idx < NUM_SPANS; ++idx) {
+		if (linksets[idx].ss7.ss7 == ss7) {
+			return &linksets[idx].ss7;
+		}
+	}
+	return NULL;
+}
+#endif	/* defined(HAVE_SS7) */
+
 #if defined(HAVE_SS7)
 /*!
  * \internal
@@ -3128,6 +3160,7 @@ struct sig_ss7_callback sig_ss7_callbacks =
 	.set_callerid = my_set_callerid,
 	.set_dnid = my_set_dnid,
 	.open_media = my_pri_ss7_open_media,
+	.find_linkset = my_ss7_find_linkset,
 };
 #endif	/* defined(HAVE_SS7) */
 
@@ -10688,9 +10721,10 @@ static int mwi_send_process_buffer(struct dahdi_pvt * pvt, int num_read)
 			break;
 		case MWI_SEND_SPILL:
 			/* We read some number of bytes.  Write an equal amount of data */
-			if(0 < num_read) {
-				if (num_read > pvt->cidlen - pvt->cidpos)
+			if (0 < num_read) {
+				if (num_read > pvt->cidlen - pvt->cidpos) {
 					num_read = pvt->cidlen - pvt->cidpos;
+				}
 				res = write(pvt->subs[SUB_REAL].dfd, pvt->cidspill + pvt->cidpos, num_read);
 				if (res > 0) {
 					pvt->cidpos += res;
@@ -10741,7 +10775,7 @@ static int mwi_send_process_event(struct dahdi_pvt * pvt, int event)
 	if (MWI_SEND_DONE != pvt->mwisend_data.mwisend_current) {
 		switch (event) {
 		case DAHDI_EVENT_RINGEROFF:
-			if(pvt->mwisend_data.mwisend_current == MWI_SEND_SA_WAIT) {
+			if (pvt->mwisend_data.mwisend_current == MWI_SEND_SA_WAIT) {
 				handled = 1;
 
 				if (dahdi_set_hook(pvt->subs[SUB_REAL].dfd, DAHDI_RINGOFF) ) {
@@ -11946,6 +11980,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 				ast_copy_string(ss7->ss7.nationalprefix, conf->ss7.ss7.nationalprefix, sizeof(ss7->ss7.nationalprefix));
 				ast_copy_string(ss7->ss7.subscriberprefix, conf->ss7.ss7.subscriberprefix, sizeof(ss7->ss7.subscriberprefix));
 				ast_copy_string(ss7->ss7.unknownprefix, conf->ss7.ss7.unknownprefix, sizeof(ss7->ss7.unknownprefix));
+				ast_copy_string(ss7->ss7.networkroutedprefix, conf->ss7.ss7.networkroutedprefix, sizeof(ss7->ss7.networkroutedprefix));
 
 				ss7->ss7.called_nai = conf->ss7.ss7.called_nai;
 				ss7->ss7.calling_nai = conf->ss7.ss7.calling_nai;
@@ -12452,12 +12487,12 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 		tmp->named_pickupgroups = ast_ref_namedgroups(conf->chan.named_pickupgroups);
 		if (conf->chan.vars) {
 			struct ast_variable *v, *tmpvar;
-	                for (v = conf->chan.vars ; v ; v = v->next) {
-        	                if ((tmpvar = ast_variable_new(v->name, v->value, v->file))) {
-                	                tmpvar->next = tmp->vars;
-                        	        tmp->vars = tmpvar;
-                        	}
-                	}
+			for (v = conf->chan.vars ; v ; v = v->next) {
+				if ((tmpvar = ast_variable_new(v->name, v->value, v->file))) {
+					tmpvar->next = tmp->vars;
+					tmp->vars = tmpvar;
+				}
+			}
 		}
 		tmp->hwrxgain_enabled = conf->chan.hwrxgain_enabled;
 		tmp->hwtxgain_enabled = conf->chan.hwtxgain_enabled;
@@ -12566,6 +12601,9 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 #if defined(HAVE_SS7)
 			case SIG_SS7:
 				tmp->inservice = 0;
+				if (tmp->ss7->flags & LINKSET_FLAG_INITIALHWBLO) {
+					tmp->remotelyblocked |= SS7_BLOCKED_HARDWARE;
+				}
 				break;
 #endif	/* defined(HAVE_SS7) */
 			default:
@@ -12600,6 +12638,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 		case SIG_SS7:
 			if (ss7_chan) {
 				ss7_chan->inalarm = tmp->inalarm;
+				ss7_chan->inservice = tmp->inservice;
 
 				ss7_chan->stripmsd = tmp->stripmsd;
 				ss7_chan->hidecallerid = tmp->hidecallerid;
@@ -14891,6 +14930,9 @@ static int dahdi_restart(void)
 	}
 	ss7_set_error(dahdi_ss7_error);
 	ss7_set_message(dahdi_ss7_message);
+	ss7_set_hangup(sig_ss7_cb_hangup);
+	ss7_set_notinservice(sig_ss7_cb_notinservice);
+	ss7_set_call_null(sig_ss7_cb_call_null);
 #endif	/* defined(HAVE_SS7) */
 
 	if (setup_dahdi(2) != 0) {
@@ -14943,9 +14985,8 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
 	ast_group_t targetnum = 0;
 	int filtertype = 0;
 	struct dahdi_pvt *tmp = NULL;
-	char tmps[20] = "";
-	char statestr[20] = "";
-	char blockstr[20] = "";
+	char tmps[20];
+	char blockstr[20];
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -14961,8 +15002,9 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
 
 	/* syntax: dahdi show channels [ group <group> | context <context> ] */
 
-	if (!((a->argc == 3) || (a->argc == 5)))
+	if (!((a->argc == 3) || (a->argc == 5))) {
 		return CLI_SHOWUSAGE;
+	}
 
 	if (a->argc == 5) {
 		if (!strcasecmp(a->argv[3], "group")) {
@@ -14977,7 +15019,7 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
 		}
 	}
 
-	ast_cli(a->fd, FORMAT2, "Chan", "Extension", "Context", "Language", "MOH Interpret", "Blocked", "State", "Description");
+	ast_cli(a->fd, FORMAT2, "Chan", "Extension", "Context", "Language", "MOH Interpret", "Blocked", "In Service", "Description");
 	ast_mutex_lock(&iflock);
 	for (tmp = iflist; tmp; tmp = tmp->next) {
 		if (filtertype) {
@@ -14998,24 +15040,15 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
 		}
 		if (tmp->channel > 0) {
 			snprintf(tmps, sizeof(tmps), "%d", tmp->channel);
-		} else
+		} else {
 			ast_copy_string(tmps, "pseudo", sizeof(tmps));
+		}
 
-		if (tmp->locallyblocked)
-			blockstr[0] = 'L';
-		else
-			blockstr[0] = ' ';
-
-		if (tmp->remotelyblocked)
-			blockstr[1] = 'R';
-		else
-			blockstr[1] = ' ';
-
+		blockstr[0] = tmp->locallyblocked ? 'L' : ' ';
+		blockstr[1] = tmp->remotelyblocked ? 'R' : ' ';
 		blockstr[2] = '\0';
 
-		snprintf(statestr, sizeof(statestr), "%s", "In Service");
-
-		ast_cli(a->fd, FORMAT, tmps, tmp->exten, tmp->context, tmp->language, tmp->mohinterpret, blockstr, statestr, tmp->description);
+		ast_cli(a->fd, FORMAT, tmps, tmp->exten, tmp->context, tmp->language, tmp->mohinterpret, blockstr, tmp->inservice ? "Yes" : "No", tmp->description);
 	}
 	ast_mutex_unlock(&iflock);
 	return CLI_SUCCESS;
@@ -16023,7 +16056,7 @@ static int linkset_addsigchan(int sigchan)
 		(params.sigtype == DAHDI_SIG_MTP2)
 			? SS7_TRANSPORT_DAHDIMTP2
 			: SS7_TRANSPORT_DAHDIDCHAN,
-		si.alarms, cur_networkindicator, cur_pointcode, cur_adjpointcode);
+		si.alarms, cur_networkindicator, cur_pointcode, cur_adjpointcode, cur_slc);
 	if (res) {
 		dahdi_close_ss7_fd(link, curfd);
 		return -1;
@@ -16049,8 +16082,11 @@ static char *handle_ss7_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 	case CLI_GENERATE:
 		return NULL;
 	}
-	if (a->argc < 6)
+
+	if (a->argc < 6) {
 		return CLI_SHOWUSAGE;
+	}
+
 	span = atoi(a->argv[5]);
 	if ((span < 1) || (span > NUM_SPANS)) {
 		ast_cli(a->fd, "Invalid linkset %s.  Should be a number from %d to %d\n", a->argv[5], 1, NUM_SPANS);
@@ -16075,24 +16111,35 @@ static char *handle_ss7_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 #endif	/* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
-static char *handle_ss7_block_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_ss7_cic_blocking(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	int linkset, cic;
-	int blocked = -1, i;
+	int blocked, i;
+	int do_block = 0;
+	unsigned int dpc;
+
 	switch (cmd) {
 	case CLI_INIT:
-		e->command = "ss7 block cic";
+		e->command = "ss7 {block|unblock} cic";
 		e->usage =
-			"Usage: ss7 block cic <linkset> <CIC>\n"
-			"       Sends a remote blocking request for the given CIC on the specified linkset\n";
+			"Usage: ss7 {block|unblock} cic <linkset> <dpc> <CIC>\n"
+			"       Sends a remote {blocking|unblocking} request for the given CIC on the specified linkset\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
-	if (a->argc == 5)
+
+	if (a->argc == 6) {
 		linkset = atoi(a->argv[3]);
-	else
+	} else {
 		return CLI_SHOWUSAGE;
+	}
+
+	if (!strcasecmp(a->argv[1], "block")) {
+		do_block = 1;
+	} else if (strcasecmp(a->argv[1], "unblock")) {
+		return CLI_SHOWUSAGE;
+	}
 
 	if ((linkset < 1) || (linkset > NUM_SPANS)) {
 		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
@@ -16104,63 +16151,157 @@ static char *handle_ss7_block_cic(struct ast_cli_entry *e, int cmd, struct ast_c
 		return CLI_SUCCESS;
 	}
 
-	cic = atoi(a->argv[4]);
-
+	cic = atoi(a->argv[5]);
 	if (cic < 1) {
 		ast_cli(a->fd, "Invalid CIC specified!\n");
 		return CLI_SUCCESS;
 	}
 
+	dpc = atoi(a->argv[4]);
+	if (dpc < 1) {
+		ast_cli(a->fd, "Invalid DPC specified!\n");
+		return CLI_SUCCESS;
+	}
+
 	for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) {
-		if (linksets[linkset-1].ss7.pvts[i]->cic == cic) {
+		if (linksets[linkset-1].ss7.pvts[i] && linksets[linkset-1].ss7.pvts[i]->cic == cic && linksets[linkset-1].ss7.pvts[i]->dpc == dpc) {
 			blocked = linksets[linkset-1].ss7.pvts[i]->locallyblocked;
-			if (!blocked) {
-				ast_mutex_lock(&linksets[linkset-1].ss7.lock);
-				isup_blo(linksets[linkset-1].ss7.ss7, cic, linksets[linkset-1].ss7.pvts[i]->dpc);
-				ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+			if (!do_block ^ !(blocked & SS7_BLOCKED_MAINTENANCE)) {
+				if (sig_ss7_cic_blocking(&linksets[linkset-1].ss7, do_block, i) < 0) {
+					ast_cli(a->fd, "Unable to allocate new ss7call\n");
+				} else {
+					ast_cli(a->fd, "Sent %sblocking request for linkset %d on CIC %d DPC %d\n", (do_block) ? "" : "un", linkset, cic, dpc);
+				}
+			} else if (!do_block && blocked) {
+				ast_cli(a->fd, "CIC %d is hardware locally blocked!\n", cic);
+			} else {
+				ast_cli(a->fd, "CIC %d %s locally blocked\n", cic, do_block ? "already" : "is not");
 			}
+			return CLI_SUCCESS;
 		}
 	}
 
-	if (blocked < 0) {
-		ast_cli(a->fd, "Invalid CIC specified!\n");
+	ast_cli(a->fd, "Invalid CIC specified!\n");
+	return CLI_SUCCESS;
+}
+#endif	/* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static char *handle_ss7_linkset_mng(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	int linkset, i;
+	enum {
+		DO_BLOCK,
+		DO_UNBLOCK,
+		DO_RESET,
+	} do_what;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ss7 {reset|block|unblock} linkset";
+		e->usage =
+			"Usage: ss7 {reset|block|unblock} linkset <linkset number>\n"
+			"       Sends a remote {reset|blocking|unblocking} request for all CICs on the given linkset\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc == 4) {
+		linkset = atoi(a->argv[3]);
+	} else {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (!strcasecmp(a->argv[1], "block")) {
+		do_what = DO_BLOCK;
+	} else if (!strcasecmp(a->argv[1], "unblock")) {
+		do_what = DO_UNBLOCK;
+	} else if (!strcasecmp(a->argv[1], "reset")) {
+		do_what = DO_RESET;
+	} else {
+		return CLI_SHOWUSAGE;
+	}
+
+	if ((linkset < 1) || (linkset > NUM_SPANS)) {
+		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
 		return CLI_SUCCESS;
 	}
 
-	if (!blocked)
-		ast_cli(a->fd, "Sent blocking request for linkset %d on CIC %d\n", linkset, cic);
-	else
-		ast_cli(a->fd, "CIC %d already locally blocked\n", cic);
+	if (!linksets[linkset - 1].ss7.ss7) {
+		ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+		return CLI_SUCCESS;
+	}
 
-	/* Break poll on the linkset so it sends our messages */
-	pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+	for (i = 0; i < linksets[linkset - 1].ss7.numchans; i++) {
+		/* XXX Should be done with GRS/CGB/CGU instead - see ss7_reset_linkset() */
+		if (linksets[linkset - 1].ss7.pvts[i]) {
+			switch (do_what) {
+			case DO_BLOCK:
+			case DO_UNBLOCK:
+				if (sig_ss7_cic_blocking(&linksets[linkset - 1].ss7, do_what == DO_BLOCK, i)) {
+					ast_cli(a->fd, "Sent remote %s request on CIC %d\n",
+						(do_what == DO_BLOCK) ? "blocking" : "unblocking",
+						linksets[linkset - 1].ss7.pvts[i]->cic);
+				}
+				break;
+			case DO_RESET:
+				if (sig_ss7_reset_cic(&linksets[linkset - 1].ss7,
+					linksets[linkset - 1].ss7.pvts[i]->cic,
+					linksets[linkset - 1].ss7.pvts[i]->dpc)) {
+					ast_cli(a->fd, "Sent reset request on CIC %d\n",
+						linksets[linkset - 1].ss7.pvts[i]->cic);
+				}
+				break;
+			}
+		}
+	}
 
 	return CLI_SUCCESS;
 }
 #endif	/* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
-static char *handle_ss7_block_linkset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_ss7_group_blocking(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	int linkset;
-	int i;
+	int linkset, cic, range, chanpos;
+	int i, dpc, orient = 0;
+	int do_block = 0;
+	unsigned char state[255];
+
 	switch (cmd) {
 	case CLI_INIT:
-		e->command = "ss7 block linkset";
+		e->command = "ss7 {block|unblock} group";
 		e->usage =
-			"Usage: ss7 block linkset <linkset number>\n"
-			"       Sends a remote blocking request for all CICs on the given linkset\n";
+			"Usage: ss7 {block|unblock} group <linkset> <dpc> <1st. CIC> <range> [H]\n"
+			"       Sends a remote {blocking|unblocking} request for CIC range on the specified linkset\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
-	if (a->argc == 4)
+
+	if (a->argc == 7 || a->argc == 8) {
 		linkset = atoi(a->argv[3]);
-	else
+	} else {
 		return CLI_SHOWUSAGE;
+	}
+
+	if (!strcasecmp(a->argv[1], "block")) {
+		do_block = 1;
+	} else if (strcasecmp(a->argv[1], "unblock")) {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (a->argc == 8) {
+		if (!strcasecmp(a->argv[7], "H")) {
+			orient = 1;
+		} else {
+			return CLI_SHOWUSAGE;
+		}
+	}
 
 	if ((linkset < 1) || (linkset > NUM_SPANS)) {
-		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[4], 1, NUM_SPANS);
 		return CLI_SUCCESS;
 	}
 
@@ -16169,43 +16310,81 @@ static char *handle_ss7_block_linkset(struct ast_cli_entry *e, int cmd, struct a
 		return CLI_SUCCESS;
 	}
 
-	for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) {
-		ast_cli(a->fd, "Sending remote blocking request on CIC %d\n", linksets[linkset-1].ss7.pvts[i]->cic);
-		ast_mutex_lock(&linksets[linkset-1].ss7.lock);
-		isup_blo(linksets[linkset-1].ss7.ss7, linksets[linkset-1].ss7.pvts[i]->cic, linksets[linkset-1].ss7.pvts[i]->dpc);
+	cic = atoi(a->argv[5]);
+	if (cic < 1) {
+		ast_cli(a->fd, "Invalid CIC specified!\n");
+		return CLI_SUCCESS;
+	}
+
+	range = atoi(a->argv[6]);
+	/* ITU-T Q.763 3.43 - range 0 is reserved, which makes a range of 2 CICs a minimum group */
+	if (range < 1 || range > (linksets[linkset - 1].ss7.type == SS7_ANSI ? 24 : 31)) {
+		ast_cli(a->fd, "Invalid range specified!\n");
+		return CLI_SUCCESS;
+	}
+
+	dpc = atoi(a->argv[4]);
+	if (dpc < 1) {
+		ast_cli(a->fd, "Invalid DPC specified!\n");
+		return CLI_SUCCESS;
+	}
+
+	ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+	if (!sig_ss7_find_cic_range(&linksets[linkset-1].ss7, cic, cic + range, dpc)) {
 		ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+		ast_cli(a->fd, "Invalid CIC/RANGE\n");
+		return CLI_SHOWUSAGE;
 	}
 
-	/* Break poll on the linkset so it sends our messages */
-	pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+	memset(state, 0, sizeof(state));
+	for (i = 0; i <= range; ++i) {
+		state[i] = 1;
+	}
 
+	/* We are guaranteed to find chanpos because of sig_ss7_find_cic_range() includes it. */
+	chanpos = sig_ss7_find_cic(&linksets[linkset-1].ss7, cic, dpc);
+	if (sig_ss7_group_blocking(&linksets[linkset-1].ss7, do_block, chanpos, cic + range, state, orient)) {
+		ast_cli(a->fd, "Unable allocate new ss7call\n");
+	} else {
+		ast_cli(a->fd, "Sending remote%s %sblocking request linkset %d on CIC %d range %d\n",
+			orient ? " hardware" : "", do_block ? "" : "un", linkset, cic, range);
+	}
+
+	ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
+	/* Break poll on the linkset so it sends our messages */
+	if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) {
+		pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+	}
 	return CLI_SUCCESS;
 }
 #endif	/* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
-static char *handle_ss7_unblock_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_ss7_group_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	int linkset, cic;
-	int i, blocked = -1;
+	int linkset, cic, range;
+	unsigned int dpc;
+
 	switch (cmd) {
 	case CLI_INIT:
-		e->command = "ss7 unblock cic";
+		e->command = "ss7 reset group";
 		e->usage =
-			"Usage: ss7 unblock cic <linkset> <CIC>\n"
-			"       Sends a remote unblocking request for the given CIC on the specified linkset\n";
+			"Usage: ss7 reset group <linkset> <dpc> <1st CIC> <range>\n"
+			"       Send a GRS for the given CIC range on the specified linkset\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
 
-	if (a->argc == 5)
+	if (a->argc == 7) {
 		linkset = atoi(a->argv[3]);
-	else
+	} else {
 		return CLI_SHOWUSAGE;
+	}
 
 	if ((linkset < 1) || (linkset > NUM_SPANS)) {
-		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[4], 1, NUM_SPANS);
 		return CLI_SUCCESS;
 	}
 
@@ -16214,54 +16393,69 @@ static char *handle_ss7_unblock_cic(struct ast_cli_entry *e, int cmd, struct ast
 		return CLI_SUCCESS;
 	}
 
-	cic = atoi(a->argv[4]);
+	cic = atoi(a->argv[5]);
 
 	if (cic < 1) {
 		ast_cli(a->fd, "Invalid CIC specified!\n");
 		return CLI_SUCCESS;
 	}
 
-	for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) {
-		if (linksets[linkset-1].ss7.pvts[i]->cic == cic) {
-			blocked = linksets[linkset-1].ss7.pvts[i]->locallyblocked;
-			if (blocked) {
-				ast_mutex_lock(&linksets[linkset-1].ss7.lock);
-				isup_ubl(linksets[linkset-1].ss7.ss7, cic, linksets[linkset-1].ss7.pvts[i]->dpc);
-				ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
-			}
-		}
+	range = atoi(a->argv[6]);
+	if (range < 1 || range > (linksets[linkset - 1].ss7.type == SS7_ANSI ? 24 : 31)) {
+		ast_cli(a->fd, "Invalid range specified!\n");
+		return CLI_SUCCESS;
 	}
 
-	if (blocked > 0)
-		ast_cli(a->fd, "Sent unblocking request for linkset %d on CIC %d\n", linkset, cic);
+	dpc = atoi(a->argv[4]);
+	if (dpc < 1) {
+		ast_cli(a->fd, "Invalid DPC specified!\n");
+		return CLI_SUCCESS;
+	}
 
-	/* Break poll on the linkset so it sends our messages */
-	pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+	ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+	if (!sig_ss7_find_cic_range(&linksets[linkset-1].ss7, cic, cic + range, dpc)) {
+		ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+		ast_cli(a->fd, "Invalid CIC/RANGE\n");
+		return CLI_SHOWUSAGE;
+	}
 
+	if (sig_ss7_reset_group(&linksets[linkset-1].ss7, cic, dpc, range)) {
+		ast_cli(a->fd, "Unable to allocate new ss7call\n");
+	} else {
+		ast_cli(a->fd, "GRS sent ... \n");
+	}
+
+	ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
+	/* Break poll on the linkset so it sends our messages */
+	if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) {
+		pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+	}
 	return CLI_SUCCESS;
 }
 #endif	/* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
-static char *handle_ss7_unblock_linkset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_ss7_show_calls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	int linkset;
-	int i;
+
 	switch (cmd) {
 	case CLI_INIT:
-		e->command = "ss7 unblock linkset";
+		e->command = "ss7 show calls";
 		e->usage =
-			"Usage: ss7 unblock linkset <linkset number>\n"
-			"       Sends a remote unblocking request for all CICs on the specified linkset\n";
+			"Usage: ss7 show calls <linkset>\n"
+			"       Show SS7 calls on the specified linkset\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
 
-	if (a->argc == 4)
+	if (a->argc == 4) {
 		linkset = atoi(a->argv[3]);
-	else
+	} else {
 		return CLI_SHOWUSAGE;
+	}
 
 	if ((linkset < 1) || (linkset > NUM_SPANS)) {
 		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
@@ -16273,15 +16467,164 @@ static char *handle_ss7_unblock_linkset(struct ast_cli_entry *e, int cmd, struct
 		return CLI_SUCCESS;
 	}
 
-	for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) {
-		ast_cli(a->fd, "Sending remote unblock request on CIC %d\n", linksets[linkset-1].ss7.pvts[i]->cic);
-		ast_mutex_lock(&linksets[linkset-1].ss7.lock);
-		isup_ubl(linksets[linkset-1].ss7.ss7, linksets[linkset-1].ss7.pvts[i]->cic, linksets[linkset-1].ss7.pvts[i]->dpc);
-		ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+	ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+	isup_show_calls(linksets[linkset-1].ss7.ss7, &ast_cli, a->fd);
+	ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
+	return CLI_SUCCESS;
+}
+#endif	/* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static char *handle_ss7_reset_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	int linkset, cic, res;
+	unsigned int dpc;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ss7 reset cic";
+		e->usage =
+			"Usage: ss7 reset cic <linkset> <dpc> <CIC>\n"
+			"       Send a RSC for the given CIC on the specified linkset\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc == 6) {
+		linkset = atoi(a->argv[3]);
+	} else {
+		return CLI_SHOWUSAGE;
+	}
+
+	if ((linkset < 1) || (linkset > NUM_SPANS)) {
+		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+		return CLI_SUCCESS;
+	}
+
+	if (!linksets[linkset-1].ss7.ss7) {
+		ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+		return CLI_SUCCESS;
+	}
+
+	cic = atoi(a->argv[5]);
+
+	if (cic < 1) {
+		ast_cli(a->fd, "Invalid CIC specified!\n");
+		return CLI_SUCCESS;
+	}
+
+	dpc = atoi(a->argv[4]);
+	if (dpc < 1) {
+		ast_cli(a->fd, "Invalid DPC specified!\n");
+		return CLI_SUCCESS;
+	}
+
+	res = sig_ss7_reset_cic(&linksets[linkset-1].ss7, cic, dpc);
+
+	ast_cli(a->fd, "%s RSC for linkset %d on CIC %d DPC %d\n", res ? "Sent" : "Failed", linkset, cic, dpc);
+
+	return CLI_SUCCESS;
+}
+#endif	/* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static char *handle_ss7_net_mng(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	int linkset;
+	unsigned int slc;
+	unsigned int arg = 0;
+	const char *res;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ss7 mtp3";
+		e->usage =
+			"Usage: ss7 mtp3 <linkset> <slc> coo|coa|cbd|cba|eco|eca|tfp|tfa|lin|lun|lia|lua|lid|lfu <arg>\n"
+			"       Send a NET MNG message\n"
+			"       WARNING!!! WARNING!!! We are not a STP, just for testing/development purposes\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc < 5) {
+		return CLI_SHOWUSAGE;
 	}
 
+	linkset = atoi(a->argv[2]);
+	if ((linkset < 1) || (linkset > NUM_SPANS)) {
+		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[2], 1, NUM_SPANS);
+		return CLI_SUCCESS;
+	}
+	if (!linksets[linkset-1].ss7.ss7) {
+		ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+		return CLI_SUCCESS;
+	}
+
+	slc = atoi(a->argv[3]);
+
+	if (a->argc == 6) {
+		arg = atoi(a->argv[5]);
+	}
+
+	ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+	res = mtp3_net_mng(linksets[linkset-1].ss7.ss7, slc, a->argv[4], arg);
+	ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
 	/* Break poll on the linkset so it sends our messages */
-	pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+	if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) {
+		pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+	}
+
+	ast_cli(a->fd, "%s", res);
+
+	return CLI_SUCCESS;
+}
+#endif	/* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static char *handle_ss7_mtp3_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	int linkset;
+	unsigned int slc = 0;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ss7 restart mtp3";
+		e->usage =
+			"Usage: ss7 restart mtp3 <linkset> <slc>\n"
+			"       Restart link\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc < 5) {
+		return CLI_SHOWUSAGE;
+	}
+
+	linkset = atoi(a->argv[3]);
+	if ((linkset < 1) || (linkset > NUM_SPANS)) {
+		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[2], 1, NUM_SPANS);
+		return CLI_SUCCESS;
+	}
+	if (!linksets[linkset-1].ss7.ss7) {
+		ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+		return CLI_SUCCESS;
+	}
+
+	slc = atoi(a->argv[4]);
+
+	ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+	mtp3_init_restart(linksets[linkset-1].ss7.ss7, slc);
+	ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
+	/* Break poll on the linkset so it sends our messages */
+	if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) {
+		pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+	}
 
 	return CLI_SUCCESS;
 }
@@ -16303,8 +16646,10 @@ static char *handle_ss7_show_linkset(struct ast_cli_entry *e, int cmd, struct as
 		return NULL;
 	}
 
-	if (a->argc < 4)
+	if (a->argc < 4) {
 		return CLI_SHOWUSAGE;
+	}
+
 	linkset = atoi(a->argv[3]);
 	if ((linkset < 1) || (linkset > NUM_SPANS)) {
 		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
@@ -16316,7 +16661,16 @@ static char *handle_ss7_show_linkset(struct ast_cli_entry *e, int cmd, struct as
 		return CLI_SUCCESS;
 	}
 
+	ast_cli(a->fd, "SS7 flags: 0x%x\n", ss7->flags);
 	ast_cli(a->fd, "SS7 linkset %d status: %s\n", linkset, (ss7->state == LINKSET_STATE_UP) ? "Up" : "Down");
+	ast_cli(a->fd, "SS7 calling nai: %i\n", ss7->calling_nai);
+	ast_cli(a->fd, "SS7 called nai: %i\n", ss7->called_nai);
+	ast_cli(a->fd, "SS7 nationalprefix: %s\n", ss7->nationalprefix);
+	ast_cli(a->fd, "SS7 internationalprefix: %s\n", ss7->internationalprefix);
+	ast_cli(a->fd, "SS7 unknownprefix: %s\n", ss7->unknownprefix);
+	ast_cli(a->fd, "SS7 networkroutedprefix: %s\n", ss7->networkroutedprefix);
+	ast_cli(a->fd, "SS7 subscriberprefix: %s\n", ss7->subscriberprefix);
+	ss7_show_linkset(ss7->ss7, &ast_cli, a->fd);
 
 	return CLI_SUCCESS;
 }
@@ -16338,8 +16692,9 @@ static char *handle_ss7_show_channels(struct ast_cli_entry *e, int cmd, struct a
 		return NULL;
 	}
 
-	if (a->argc != 3)
+	if (a->argc != 3) {
 		return CLI_SHOWUSAGE;
+	}
 
 	sig_ss7_cli_show_channels_header(a->fd);
 	for (linkset = 0; linkset < NUM_SPANS; ++linkset) {
@@ -16351,6 +16706,110 @@ static char *handle_ss7_show_channels(struct ast_cli_entry *e, int cmd, struct a
 }
 #endif	/* defined(HAVE_SS7) */
 
+#if defined(HAVE_SS7)
+static char *handle_ss7_show_cics(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%5s %5s %6s %12s   %-12s\n"
+#define FORMAT2 "%5i %5i %6i %12s   %-12s\n"
+	int i, linkset, dpc = 0;
+	struct sig_ss7_linkset *ss7;
+	char *state;
+	char blocking[12];
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ss7 show cics";
+		e->usage =
+			"Usage: ss7 show cics <linkset> [dpc]\n"
+			"       Shows the cics of an SS7 linkset.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc < 4 || a->argc > 5) {
+		return CLI_SHOWUSAGE;
+	}
+
+	linkset = atoi(a->argv[3]);
+
+	if ((linkset < 1) || (linkset > NUM_SPANS)) {
+		ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+		return CLI_SUCCESS;
+	}
+
+	if (!linksets[linkset-1].ss7.ss7) {
+		ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+		return CLI_SUCCESS;
+	}
+	ss7 = &linksets[linkset-1].ss7;
+
+	if (a->argc == 5) {
+		dpc = atoi(a->argv[4]);
+		if (dpc < 1) {
+			ast_cli(a->fd, "Invalid DPC specified!\n");
+			return CLI_SUCCESS;
+		}
+	}
+
+	ast_cli(a->fd, FORMAT, "CIC", "DPC", "DAHDI", "STATE", "BLOCKING");
+
+	for (i = 0; i < ss7->numchans; i++) {
+		if (!dpc || (ss7->pvts[i] && ss7->pvts[i]->dpc == dpc)) {
+			struct dahdi_pvt *p = ss7->pvts[i]->chan_pvt;
+
+			if (ss7->pvts[i]->owner) {
+				state = "Used";
+			} else if (ss7->pvts[i]->ss7call) {
+				state = "Pending";
+			} else if (!p->inservice) {
+				state = "NotInServ";
+			} else {
+				state = "Idle";
+			}
+
+			if (p->locallyblocked) {
+				strcpy(blocking, "L:");
+				if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) {
+					strcat(blocking, "M");
+				} else {
+					strcat(blocking, " ");
+				}
+
+				if (p->locallyblocked & SS7_BLOCKED_HARDWARE) {
+					strcat(blocking, "H");
+				} else {
+					strcat(blocking, " ");
+				}
+			} else {
+				strcpy(blocking, "    ");
+			}
+
+			if (p->remotelyblocked) {
+				strcat(blocking, " R:");
+				if (p->remotelyblocked & SS7_BLOCKED_MAINTENANCE) {
+					strcat(blocking, "M");
+				} else {
+					strcat(blocking, " ");
+				}
+
+				if (p->remotelyblocked & SS7_BLOCKED_HARDWARE) {
+					strcat(blocking, "H");
+				} else {
+					strcat(blocking, " ");
+				}
+			}
+
+			ast_cli(a->fd, FORMAT2, ss7->pvts[i]->cic, ss7->pvts[i]->dpc, ss7->pvts[i]->channel, state, blocking);
+		}
+	}
+
+	return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+#endif	/* defined(HAVE_SS7) */
+
 #if defined(HAVE_SS7)
 static char *handle_ss7_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -16374,12 +16833,17 @@ static char *handle_ss7_version(struct ast_cli_entry *e, int cmd, struct ast_cli
 #if defined(HAVE_SS7)
 static struct ast_cli_entry dahdi_ss7_cli[] = {
 	AST_CLI_DEFINE(handle_ss7_debug, "Enables SS7 debugging on a linkset"),
-	AST_CLI_DEFINE(handle_ss7_block_cic, "Blocks the given CIC"),
-	AST_CLI_DEFINE(handle_ss7_unblock_cic, "Unblocks the given CIC"),
-	AST_CLI_DEFINE(handle_ss7_block_linkset, "Blocks all CICs on a linkset"),
-	AST_CLI_DEFINE(handle_ss7_unblock_linkset, "Unblocks all CICs on a linkset"),
+	AST_CLI_DEFINE(handle_ss7_cic_blocking, "Blocks/Unblocks the given CIC"),
+	AST_CLI_DEFINE(handle_ss7_linkset_mng, "Resets/Blocks/Unblocks all CICs on a linkset"),
+	AST_CLI_DEFINE(handle_ss7_group_blocking, "Blocks/Unblocks the given CIC range"),
+	AST_CLI_DEFINE(handle_ss7_reset_cic, "Resets the given CIC"),
+	AST_CLI_DEFINE(handle_ss7_group_reset, "Resets the given CIC range"),
+	AST_CLI_DEFINE(handle_ss7_mtp3_restart, "Restart a link"),
+	AST_CLI_DEFINE(handle_ss7_net_mng, "Send an NET MNG message"),
 	AST_CLI_DEFINE(handle_ss7_show_linkset, "Shows the status of a linkset"),
 	AST_CLI_DEFINE(handle_ss7_show_channels, "Displays SS7 channel information"),
+	AST_CLI_DEFINE(handle_ss7_show_calls, "Show ss7 calls"),
+	AST_CLI_DEFINE(handle_ss7_show_cics, "Show cics on a linkset"),
 	AST_CLI_DEFINE(handle_ss7_version, "Displays libss7 version"),
 };
 #endif	/* defined(HAVE_SS7) */
@@ -16581,6 +17045,10 @@ static int __unload_module(void)
 		for (j = 0; j < SIG_SS7_NUM_DCHANS; j++) {
 			dahdi_close_ss7_fd(&(linksets[i]), j);
 		}
+		if (linksets[i].ss7.ss7) {
+			ss7_destroy(linksets[i].ss7.ss7);
+			linksets[i].ss7.ss7 = NULL;
+		}
 	}
 #endif	/* defined(HAVE_SS7) */
 	ast_cond_destroy(&ss_thread_complete);
@@ -16856,7 +17324,7 @@ static void parse_busy_pattern(struct ast_variable *v, struct ast_dsp_busy_patte
 
 	for (; ;) {
 		/* Scans the string for the next value in the pattern. If none, it checks to see if any have been entered so far. */
-		if(!sscanf(v->value, "%30d", &norval) && count_pattern == 0) {
+		if (!sscanf(v->value, "%30d", &norval) && count_pattern == 0) {
 			ast_log(LOG_ERROR, "busypattern= expects either busypattern=tonelength,quietlength or busypattern=t1length, q1length, t2length, q2length at line %d.\n", v->lineno);
 			break;
 		}
@@ -17220,23 +17688,23 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 #else
 			/* Default is fsk, to turn it off you must specify nofsk */
 			memset(&confp->chan.mwisend_setting, 0, sizeof(confp->chan.mwisend_setting));
-			if (strcasestr(v->value, "nofsk")) { 		/* NoFSK */
+			if (strcasestr(v->value, "nofsk")) {		/* NoFSK */
 				confp->chan.mwisend_fsk = 0;
 			} else {					/* Default FSK */
 				confp->chan.mwisend_fsk = 1;
 			}
-			if (strcasestr(v->value, "rpas")) { 		/* Ring Pulse Alert Signal, normally followed by FSK */
+			if (strcasestr(v->value, "rpas")) {		/* Ring Pulse Alert Signal, normally followed by FSK */
 				confp->chan.mwisend_rpas = 1;
 			} else {
 				confp->chan.mwisend_rpas = 0;
 			}
-			if (strcasestr(v->value, "lrev")) { 		/* Line Reversal */
+			if (strcasestr(v->value, "lrev")) {		/* Line Reversal */
 				confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_LREV;
 			}
-			if (strcasestr(v->value, "hvdc")) { 		/* HV 90VDC */
+			if (strcasestr(v->value, "hvdc")) {		/* HV 90VDC */
 				confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_HVDC;
 			}
-			if ( (strcasestr(v->value, "neon")) || (strcasestr(v->value, "hvac")) ){ 	/* 90V DC pulses */
+			if ( (strcasestr(v->value, "neon")) || (strcasestr(v->value, "hvac")) ) {	/* 90V DC pulses */
 				confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_HVAC;
 			}
 #endif
@@ -17744,8 +18212,11 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 					cur_ss7type = SS7_ITU;
 				} else if (!strcasecmp(v->value, "ansi")) {
 					cur_ss7type = SS7_ANSI;
-				} else
+				} else {
 					ast_log(LOG_WARNING, "'%s' is an unknown ss7 switch type at line %d.!\n", v->value, v->lineno);
+				}
+			} else if (!strcasecmp(v->name, "slc")) {
+				cur_slc = atoi(v->value);
 			} else if (!strcasecmp(v->name, "linkset")) {
 				cur_linkset = atoi(v->value);
 			} else if (!strcasecmp(v->name, "pointcode")) {
@@ -17757,16 +18228,17 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 			} else if (!strcasecmp(v->name, "cicbeginswith")) {
 				cur_cicbeginswith = atoi(v->value);
 			} else if (!strcasecmp(v->name, "networkindicator")) {
-				if (!strcasecmp(v->value, "national"))
+				if (!strcasecmp(v->value, "national")) {
 					cur_networkindicator = SS7_NI_NAT;
-				else if (!strcasecmp(v->value, "national_spare"))
+				} else if (!strcasecmp(v->value, "national_spare")) {
 					cur_networkindicator = SS7_NI_NAT_SPARE;
-				else if (!strcasecmp(v->value, "international"))
+				} else if (!strcasecmp(v->value, "international")) {
 					cur_networkindicator = SS7_NI_INT;
-				else if (!strcasecmp(v->value, "international_spare"))
+				} else if (!strcasecmp(v->value, "international_spare")) {
 					cur_networkindicator = SS7_NI_INT_SPARE;
-				else
+				} else {
 					cur_networkindicator = -1;
+				}
 			} else if (!strcasecmp(v->name, "ss7_internationalprefix")) {
 				ast_copy_string(confp->ss7.ss7.internationalprefix, v->value, sizeof(confp->ss7.ss7.internationalprefix));
 			} else if (!strcasecmp(v->name, "ss7_nationalprefix")) {
@@ -17775,6 +18247,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 				ast_copy_string(confp->ss7.ss7.subscriberprefix, v->value, sizeof(confp->ss7.ss7.subscriberprefix));
 			} else if (!strcasecmp(v->name, "ss7_unknownprefix")) {
 				ast_copy_string(confp->ss7.ss7.unknownprefix, v->value, sizeof(confp->ss7.ss7.unknownprefix));
+			} else if (!strcasecmp(v->name, "ss7_networkroutedprefix")) {
+				ast_copy_string(confp->ss7.ss7.networkroutedprefix, v->value, sizeof(confp->ss7.ss7.networkroutedprefix));
 			} else if (!strcasecmp(v->name, "ss7_called_nai")) {
 				if (!strcasecmp(v->value, "national")) {
 					confp->ss7.ss7.called_nai = SS7_NAI_NATIONAL;
@@ -17807,9 +18281,9 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 				int sigchan, res;
 				sigchan = atoi(v->value);
 				res = linkset_addsigchan(sigchan);
-				if (res < 0)
+				if (res < 0) {
 					return -1;
-
+				}
 			} else if (!strcasecmp(v->name, "ss7_explicitacm")) {
 				struct dahdi_ss7 *link;
 				link = ss7_resolve_linkset(cur_linkset);
@@ -17817,8 +18291,148 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
 					return -1;
 				}
-				if (ast_true(v->value))
+				if (ast_true(v->value)) {
 					link->ss7.flags |= LINKSET_FLAG_EXPLICITACM;
+				} else {
+					link->ss7.flags &= ~LINKSET_FLAG_EXPLICITACM;
+				}
+			} else if (!strcasecmp(v->name, "ss7_autoacm")) {
+				struct dahdi_ss7 *link;
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (ast_true(v->value)) {
+					link->ss7.flags |= LINKSET_FLAG_AUTOACM;
+				} else {
+					link->ss7.flags &= ~LINKSET_FLAG_AUTOACM;
+				}
+			} else if (!strcasecmp(v->name, "ss7_initialhwblo")) {
+				struct dahdi_ss7 *link;
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (ast_true(v->value)) {
+					link->ss7.flags |= LINKSET_FLAG_INITIALHWBLO;
+				} else {
+					link->ss7.flags &= ~LINKSET_FLAG_INITIALHWBLO;
+				}
+			} else if (!strcasecmp(v->name, "ss7_use_echocontrol")) {
+				struct dahdi_ss7 *link;
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (ast_true(v->value)) {
+					link->ss7.flags |= LINKSET_FLAG_USEECHOCONTROL;
+				} else {
+					link->ss7.flags &= ~LINKSET_FLAG_USEECHOCONTROL;
+				}
+			} else if (!strcasecmp(v->name, "ss7_default_echocontrol")) {
+				struct dahdi_ss7 *link;
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (ast_true(v->value)) {
+					link->ss7.flags |= LINKSET_FLAG_DEFAULTECHOCONTROL;
+				} else {
+					link->ss7.flags &= ~LINKSET_FLAG_DEFAULTECHOCONTROL;
+				}
+			} else if (!strncasecmp(v->name, "isup_timer.", 11)) {
+				struct dahdi_ss7 *link;
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (!link->ss7.ss7) {
+					ast_log(LOG_ERROR, "Please specify isup timers after sigchan!\n");
+				} else if (!ss7_set_isup_timer(link->ss7.ss7, strstr(v->name, ".") + 1, atoi(v->value))) {
+					ast_log(LOG_ERROR, "Invalid isup timer %s\n", v->name);
+				}
+			} else if (!strncasecmp(v->name, "mtp3_timer.", 11)) {
+				struct dahdi_ss7 *link;
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (!link->ss7.ss7) {
+					ast_log(LOG_ERROR, "Please specify mtp3 timers after sigchan!\n");
+				} else if (!ss7_set_mtp3_timer(link->ss7.ss7, strstr(v->name, ".") + 1, atoi(v->value))) {
+					ast_log(LOG_ERROR, "Invalid mtp3 timer %s\n", v->name);
+				}
+			} else if (!strcasecmp(v->name, "inr_if_no_calling")) {
+				struct dahdi_ss7 *link;
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (!link->ss7.ss7) {
+					ast_log(LOG_ERROR, "Please specify inr_if_no_calling after sigchan!\n");
+				} else if (ast_true(v->value)) {
+					ss7_set_flags(link->ss7.ss7, SS7_INR_IF_NO_CALLING);
+				} else {
+					ss7_clear_flags(link->ss7.ss7, SS7_INR_IF_NO_CALLING);
+				}
+			} else if (!strcasecmp(v->name, "non_isdn_access")) {
+				struct dahdi_ss7 *link;
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (!link->ss7.ss7) {
+					ast_log(LOG_ERROR, "Please specify non_isdn_access after sigchan!\n");
+				} else if (ast_true(v->value)) {
+					ss7_clear_flags(link->ss7.ss7, SS7_ISDN_ACCESS_INDICATOR);
+				} else {
+					ss7_set_flags(link->ss7.ss7, SS7_ISDN_ACCESS_INDICATOR);
+				}
+			} else if (!strcasecmp(v->name, "sls_shift")) {
+				struct dahdi_ss7 *link;
+				int sls_shift = atoi(v->value);
+
+				if (sls_shift < 0 || sls_shift > 7) {
+					ast_log(LOG_ERROR, "Invalid sls_shift value.  Must be between 0 and 7\n");
+					return -1;
+				}
+
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (!link->ss7.ss7) {
+					ast_log(LOG_ERROR, "Please specify sls_shift after sigchan!\n");
+				} else {
+					ss7_set_sls_shift(link->ss7.ss7, sls_shift);
+				}
+			} else if (!strcasecmp(v->name, "cause_location")) {
+				struct dahdi_ss7 *link;
+				int cause_location = atoi(v->value);
+
+				if (cause_location < 0 || cause_location > 15) {
+					ast_log(LOG_ERROR, "Invalid cause_location value.  Must be between 0 and 15\n");
+					return -1;
+				}
+				link = ss7_resolve_linkset(cur_linkset);
+				if (!link) {
+					ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+					return -1;
+				}
+				if (!link->ss7.ss7) {
+					ast_log(LOG_ERROR, "Please specify cause_location after sigchan!\n");
+				} else {
+					ss7_set_cause_location(link->ss7.ss7, cause_location);
+				}
 #endif	/* defined(HAVE_SS7) */
 #ifdef HAVE_OPENR2
 			} else if (!strcasecmp(v->name, "mfcr2_advanced_protocol_file")) {
@@ -17881,12 +18495,12 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 				confp->mfcr2.call_files = ast_true(v->value) ? 1 : 0;
 			} else if (!strcasecmp(v->name, "mfcr2_max_ani")) {
 				confp->mfcr2.max_ani = atoi(v->value);
-				if (confp->mfcr2.max_ani >= AST_MAX_EXTENSION){
+				if (confp->mfcr2.max_ani >= AST_MAX_EXTENSION) {
 					confp->mfcr2.max_ani = AST_MAX_EXTENSION - 1;
 				}
 			} else if (!strcasecmp(v->name, "mfcr2_max_dnis")) {
 				confp->mfcr2.max_dnis = atoi(v->value);
-				if (confp->mfcr2.max_dnis >= AST_MAX_EXTENSION){
+				if (confp->mfcr2.max_dnis >= AST_MAX_EXTENSION) {
 					confp->mfcr2.max_dnis = AST_MAX_EXTENSION - 1;
 				}
 			} else if (!strcasecmp(v->name, "mfcr2_category")) {
@@ -18677,6 +19291,9 @@ static int load_module(void)
 	}
 	ss7_set_error(dahdi_ss7_error);
 	ss7_set_message(dahdi_ss7_message);
+	ss7_set_hangup(sig_ss7_cb_hangup);
+	ss7_set_notinservice(sig_ss7_cb_notinservice);
+	ss7_set_call_null(sig_ss7_cb_call_null);
 #endif	/* defined(HAVE_SS7) */
 	res = setup_dahdi(0);
 	/* Make sure we can register our DAHDI channel type */
diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h
index d302bd7f7102e00f8cbcc1ec57a7c13c556b09ca..e4a689a1abb538bb7e9ea2ac93d08fd62cda82fe 100644
--- a/channels/chan_dahdi.h
+++ b/channels/chan_dahdi.h
@@ -389,19 +389,27 @@ struct dahdi_pvt {
 	unsigned int mwisendactive:1;
 	/*!
 	 * \brief TRUE if channel is out of reset and ready
-	 * \note Set but not used.
+	 * \note Used by SS7.  Otherwise set but not used.
 	 */
 	unsigned int inservice:1;
 	/*!
-	 * \brief TRUE if the channel is locally blocked.
+	 * \brief Bitmask for the channel being locally blocked.
 	 * \note Applies to SS7 and MFCR2 channels.
+	 * \note For MFCR2 only the first bit is used - TRUE if blocked
+	 * \note For SS7 two bits are used
+	 * \note Bit 0 - TRUE if maintenance blocked
+	 * \note Bit 1 - TRUE if hardware blocked
 	 */
-	unsigned int locallyblocked:1;
+	unsigned int locallyblocked:2;
 	/*!
-	 * \brief TRUE if the channel is remotely blocked.
+	 * \brief Bitmask for the channel being remotely blocked. 1 maintenance, 2 blocked in hardware.
 	 * \note Applies to SS7 and MFCR2 channels.
+	 * \note For MFCR2 only the first bit is used - TRUE if blocked
+	 * \note For SS7 two bits are used
+	 * \note Bit 0 - TRUE if maintenance blocked
+	 * \note Bit 1 - TRUE if hardware blocked
 	 */
-	unsigned int remotelyblocked:1;
+	unsigned int remotelyblocked:2;
 	/*!
 	 * \brief TRUE if the channel alarms will be managed also as Span ones
 	 * \note Applies to all channels
diff --git a/channels/sig_ss7.c b/channels/sig_ss7.c
index 2e391891213ae27367067b134837b48ff5251839..a7df9e9e08b59a14495eb9c714d09266853385ac 100644
--- a/channels/sig_ss7.c
+++ b/channels/sig_ss7.c
@@ -41,11 +41,14 @@
 #include "asterisk/causes.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/cli.h"
+#include "asterisk/callerid.h"
 #include "asterisk/transcap.h"
 #include "asterisk/stasis_channels.h"
 
 #include "sig_ss7.h"
-#if defined(LIBSS7_ABI_COMPATIBILITY)
+#if !defined(LIBSS7_ABI_COMPATIBILITY)
+#error "Upgrade your libss7"
+#elif LIBSS7_ABI_COMPATIBILITY != 2
 #error "Your installed libss7 is not compatible"
 #endif
 
@@ -68,8 +71,6 @@ static const char *sig_ss7_call_level2str(enum sig_ss7_call_level level)
 		return "Alerting";
 	case SIG_SS7_CALL_LEVEL_CONNECT:
 		return "Connect";
-	case SIG_SS7_CALL_LEVEL_GLARE:
-		return "Glare";
 	}
 	return "Unknown";
 }
@@ -132,24 +133,35 @@ static void sig_ss7_set_outgoing(struct sig_ss7_chan *p, int is_outgoing)
 
 static void sig_ss7_set_inservice(struct sig_ss7_chan *p, int is_inservice)
 {
+	p->inservice = is_inservice;
 	if (sig_ss7_callbacks.set_inservice) {
 		sig_ss7_callbacks.set_inservice(p->chan_pvt, is_inservice);
 	}
 }
 
-static void sig_ss7_set_locallyblocked(struct sig_ss7_chan *p, int is_blocked)
+static void sig_ss7_set_locallyblocked(struct sig_ss7_chan *p, int is_blocked, int type)
 {
-	p->locallyblocked = is_blocked;
+	if (is_blocked) {
+		p->locallyblocked |= type;
+	} else {
+		p->locallyblocked &= ~type;
+	}
+
 	if (sig_ss7_callbacks.set_locallyblocked) {
-		sig_ss7_callbacks.set_locallyblocked(p->chan_pvt, is_blocked);
+		sig_ss7_callbacks.set_locallyblocked(p->chan_pvt, p->locallyblocked);
 	}
 }
 
-static void sig_ss7_set_remotelyblocked(struct sig_ss7_chan *p, int is_blocked)
+static void sig_ss7_set_remotelyblocked(struct sig_ss7_chan *p, int is_blocked, int type)
 {
-	p->remotelyblocked = is_blocked;
+	if (is_blocked) {
+		p->remotelyblocked |= type;
+	} else {
+		p->remotelyblocked &= ~type;
+	}
+
 	if (sig_ss7_callbacks.set_remotelyblocked) {
-		sig_ss7_callbacks.set_remotelyblocked(p->chan_pvt, is_blocked);
+		sig_ss7_callbacks.set_remotelyblocked(p->chan_pvt, p->remotelyblocked);
 	}
 }
 
@@ -277,7 +289,13 @@ static struct ast_channel *sig_ss7_new_ast_channel(struct sig_ss7_chan *p, int s
 	if (!p->owner) {
 		p->owner = ast;
 	}
-	p->alreadyhungup = 0;
+
+	if (p->outgoing) {
+		p->do_hangup = SS7_HANGUP_FREE_CALL;
+	} else {
+		p->do_hangup = SS7_HANGUP_SEND_REL;
+	}
+
 	ast_channel_transfercapability_set(ast, transfercapability);
 	pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY",
 		ast_transfercapability2str(transfercapability));
@@ -295,6 +313,14 @@ static void sig_ss7_handle_link_exception(struct sig_ss7_linkset *linkset, int w
 	}
 }
 
+static struct sig_ss7_linkset *sig_ss7_find_linkset(struct ss7 *ss7)
+{
+	if (sig_ss7_callbacks.find_linkset) {
+		return sig_ss7_callbacks.find_linkset(ss7);
+	}
+	return NULL;
+}
+
 /*!
  * \internal
  * \brief Determine if a private channel structure is available.
@@ -305,7 +331,7 @@ static void sig_ss7_handle_link_exception(struct sig_ss7_linkset *linkset, int w
  */
 static int sig_ss7_is_chan_available(struct sig_ss7_chan *pvt)
 {
-	if (!pvt->inalarm && !pvt->owner && !pvt->ss7call
+	if (pvt->inservice && !pvt->inalarm && !pvt->owner && !pvt->ss7call
 		&& pvt->call_level == SIG_SS7_CALL_LEVEL_IDLE
 		&& !pvt->locallyblocked && !pvt->remotelyblocked) {
 		return 1;
@@ -398,7 +424,7 @@ static void sig_ss7_queue_control(struct sig_ss7_linkset *ss7, int chanpos, int
 /*!
  * \internal
  * \brief Queue a PVT_CAUSE_CODE frame onto the owner channel.
- * \since 11
+ * \since 11.0
  *
  * \param owner Owner channel of the pvt.
  * \param cause String describing the cause to be placed into the frame.
@@ -425,7 +451,6 @@ static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *caus
 
 
 /*!
- * \internal
  * \brief Find the channel position by CIC/DPC.
  *
  * \param linkset SS7 linkset control structure.
@@ -435,7 +460,7 @@ static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *caus
  * \retval chanpos on success.
  * \retval -1 on error.
  */
-static int ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc)
+int sig_ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc)
 {
 	int i;
 	int winner = -1;
@@ -464,45 +489,206 @@ static int ss7_find_cic_gripe(struct sig_ss7_linkset *linkset, int cic, unsigned
 {
 	int chanpos;
 
-	chanpos = ss7_find_cic(linkset, cic, dpc);
+	chanpos = sig_ss7_find_cic(linkset, cic, dpc);
 	if (chanpos < 0) {
-		ast_log(LOG_WARNING, "Linkset %d: SS7 %s requested unconfigured CIC/DPC %d/%d.\n",
+		ast_log(LOG_WARNING, "Linkset %d: SS7 %s requested on unconfigured CIC/DPC %d/%d.\n",
 			linkset->span, msg_name, cic, dpc);
 		return -1;
 	}
 	return chanpos;
 }
 
-static void ss7_handle_cqm(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc)
+static struct sig_ss7_chan *ss7_find_pvt(struct ss7 *ss7, int cic, unsigned int dpc)
+{
+	int chanpos;
+	struct sig_ss7_linkset *winner;
+
+	winner = sig_ss7_find_linkset(ss7);
+	if (winner && (chanpos = sig_ss7_find_cic(winner, cic, dpc)) > -1) {
+		return winner->pvts[chanpos];
+	}
+	return NULL;
+}
+
+int sig_ss7_cb_hangup(struct ss7 *ss7, int cic, unsigned int dpc, int cause, int do_hangup)
+{
+	struct sig_ss7_chan *p;
+	int res;
+
+	if (!(p = ss7_find_pvt(ss7, cic, dpc))) {
+		return SS7_CIC_NOT_EXISTS;
+	}
+
+	sig_ss7_lock_private(p);
+	if (p->owner) {
+		ast_channel_hangupcause_set(p->owner, cause);
+		ast_channel_softhangup_internal_flag_add(p->owner, AST_SOFTHANGUP_DEV);
+		p->do_hangup = do_hangup;
+		res = SS7_CIC_USED;
+	} else {
+		res = SS7_CIC_IDLE;
+	}
+	sig_ss7_unlock_private(p);
+
+	return res;
+}
+
+void sig_ss7_cb_call_null(struct ss7 *ss7, struct isup_call *call, int lock)
+{
+	int i;
+	struct sig_ss7_linkset *winner;
+
+	winner = sig_ss7_find_linkset(ss7);
+	if (!winner) {
+		return;
+	}
+	for (i = 0; i < winner->numchans; i++) {
+		if (winner->pvts[i] && (winner->pvts[i]->ss7call == call)) {
+			if (lock) {
+				sig_ss7_lock_private(winner->pvts[i]);
+			}
+			winner->pvts[i]->ss7call = NULL;
+			if (winner->pvts[i]->owner) {
+				ast_channel_hangupcause_set(winner->pvts[i]->owner, AST_CAUSE_NORMAL_TEMPORARY_FAILURE);
+				ast_channel_softhangup_internal_flag_add(winner->pvts[i]->owner, AST_SOFTHANGUP_DEV);
+			}
+			if (lock) {
+				sig_ss7_unlock_private(winner->pvts[i]);
+			}
+			ast_log(LOG_WARNING, "libss7 asked set ss7 call to NULL on CIC %d DPC %d\n", winner->pvts[i]->cic, winner->pvts[i]->dpc);
+		}
+	}
+}
+
+void sig_ss7_cb_notinservice(struct ss7 *ss7, int cic, unsigned int dpc)
+{
+	struct sig_ss7_chan *p;
+
+	if (!(p = ss7_find_pvt(ss7, cic, dpc))) {
+		return;
+	}
+
+	sig_ss7_lock_private(p);
+	sig_ss7_set_inservice(p, 0);
+	sig_ss7_unlock_private(p);
+}
+
+/*!
+ * \internal
+ * \brief Check if CICs in a range belong to the linkset for a given DPC.
+ * \since 11.0
+ *
+ * \param linkset SS7 linkset control structure.
+ * \param startcic Circuit Identification Code to start from
+ * \param endcic Circuit Identification Code to search up-to
+ * \param dpc Destination Point Code
+ * \param state Array containing the status of the search
+ *
+ * \retval Nothing.
+ */
+static void ss7_check_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char *state)
+{
+	int cic;
+
+	for (cic = startcic; cic <= endcic; cic++) {
+		if (state[cic - startcic] && sig_ss7_find_cic(linkset, cic, dpc) == -1) {
+			state[cic - startcic] = 0;
+		}
+	}
+}
+
+static int ss7_match_range(struct sig_ss7_chan *pvt, int startcic, int endcic, unsigned int dpc)
+{
+	if (pvt && pvt->dpc == dpc && pvt->cic >= startcic && pvt->cic <= endcic) {
+		return 1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Check if a range is defined for the given DPC.
+ * \since 11.0
+ *
+ * \param linkset SS7 linkset control structure.
+ * \param startcic Start CIC of the range to clear.
+ * \param endcic End CIC of the range to clear.
+ * \param dpc Destination Point Code.
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ *
+ * \return TRUE if all CICs in the range are present
+ */
+int sig_ss7_find_cic_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc)
+{
+	int i, found = 0;
+
+	for (i = 0; i < linkset->numchans; i++) {
+		if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
+			found++;
+		}
+	}
+
+	if (found == endcic - startcic + 1) {
+		return  1;
+	}
+
+	return 0;
+}
+
+static void ss7_handle_cqm(struct sig_ss7_linkset *linkset, ss7_event *e)
 {
 	unsigned char status[32];
 	struct sig_ss7_chan *p = NULL;
-	int i, offset;
+	int i;
+	int offset;
+	int chanpos;
 
+	memset(status, 0, sizeof(status));
 	for (i = 0; i < linkset->numchans; i++) {
-		if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic)))) {
+		if (ss7_match_range(linkset->pvts[i], e->cqm.startcic, e->cqm.endcic, e->cqm.opc)) {
 			p = linkset->pvts[i];
-			offset = p->cic - startcic;
+			sig_ss7_lock_private(p);
+			offset = p->cic - e->cqm.startcic;
 			status[offset] = 0;
-			if (p->locallyblocked)
+			if (p->locallyblocked) {
 				status[offset] |= (1 << 0) | (1 << 4);
-			if (p->remotelyblocked)
+			}
+			if (p->remotelyblocked) {
 				status[offset] |= (1 << 1) | (1 << 5);
+			}
 			if (p->ss7call) {
-				if (p->outgoing)
+				if (p->outgoing) {
 					status[offset] |= (1 << 3);
-				else
+				} else {
 					status[offset] |= (1 << 2);
-			} else
+				}
+			} else {
 				status[offset] |= 0x3 << 2;
+			}
+			sig_ss7_unlock_private(p);
 		}
 	}
 
-	if (p)
-		isup_cqr(linkset->ss7, startcic, endcic, dpc, status);
-	else
+	if (p) {
+		isup_cqr(linkset->ss7, e->cqm.startcic, e->cqm.endcic, e->cqm.opc, status);
+	} else {
 		ast_log(LOG_WARNING, "Could not find any equipped circuits within CQM CICs\n");
+	}
 
+	chanpos = sig_ss7_find_cic(linkset, e->cqm.startcic, e->cqm.opc);
+	if (chanpos < 0) {
+		isup_free_call(linkset->ss7, e->cqm.call);
+		return;
+	}
+	p = linkset->pvts[chanpos];
+	sig_ss7_lock_private(p);
+	p->ss7call = e->cqm.call;
+	if (!p->owner) {
+		p->ss7call = isup_free_call_if_clear(linkset->ss7, e->cqm.call);
+	}
+	sig_ss7_unlock_private(p);
 }
 
 static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc)
@@ -510,7 +696,7 @@ static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic
 	int i;
 
 	for (i = 0; i < linkset->numchans; i++) {
-		if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic)))) {
+		if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
 			sig_ss7_lock_private(linkset->pvts[i]);
 			sig_ss7_lock_owner(linkset, i);
 			if (linkset->pvts[i]->owner) {
@@ -522,67 +708,274 @@ static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic
 	}
 }
 
-static inline void ss7_block_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char state[], int block)
+/*!
+ * \param linkset SS7 linkset control structure.
+ * \param startcic Start CIC of the range to clear.
+ * \param endcic End CIC of the range to clear.
+ * \param dpc Destination Point Code.
+ * \param state Affected CICs from the operation. NULL for all CICs in the range.
+ * \param block Operation to perform. TRUE to block.
+ * \param remotely Direction of the blocking. TRUE to block/unblock remotely.
+ * \param type Blocking type - hardware or maintenance.
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ * \note Must be called without sig_ss7_lock_private() obtained.
+ *
+ * \return Nothing.
+ */
+static inline void ss7_block_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char state[], int block, int remotely, int type)
 {
 	int i;
 
-	/* XXX the use of state here seems questionable about matching up with the linkset channels */
 	for (i = 0; i < linkset->numchans; i++) {
-		if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic)))) {
+		if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
+			sig_ss7_lock_private(linkset->pvts[i]);
 			if (state) {
-				if (state[i])
-					sig_ss7_set_remotelyblocked(linkset->pvts[i], block);
-			} else
-				sig_ss7_set_remotelyblocked(linkset->pvts[i], block);
+				if (state[linkset->pvts[i]->cic - startcic]) {
+
+					if (remotely) {
+						sig_ss7_set_remotelyblocked(linkset->pvts[i], block, type);
+					} else {
+						sig_ss7_set_locallyblocked(linkset->pvts[i], block, type);
+					}
+
+					sig_ss7_lock_owner(linkset, i);
+					if (linkset->pvts[i]->owner) {
+						if (ast_channel_state(linkset->pvts[i]->owner) == AST_STATE_DIALING
+							&& linkset->pvts[i]->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) {
+							ast_channel_hangupcause_set(linkset->pvts[i]->owner, SS7_CAUSE_TRY_AGAIN);
+						}
+						ast_channel_unlock(linkset->pvts[i]->owner);
+					}
+				}
+			} else {
+				if (remotely) {
+					sig_ss7_set_remotelyblocked(linkset->pvts[i], block, type);
+				} else {
+					sig_ss7_set_locallyblocked(linkset->pvts[i], block, type);
+				}
+			}
+			sig_ss7_unlock_private(linkset->pvts[i]);
 		}
 	}
 }
 
+/*!
+ * \param linkset SS7 linkset control structure.
+ * \param startcic Start CIC of the range to set in service.
+ * \param endcic End CIC of the range to set in service.
+ * \param dpc Destination Point Code.
+ *
+ * \note Must be called without sig_ss7_lock_private() obtained.
+ *
+ * \return Nothing.
+ */
 static void ss7_inservice(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc)
 {
 	int i;
 
 	for (i = 0; i < linkset->numchans; i++) {
-		if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic))))
+		if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
+			sig_ss7_lock_private(linkset->pvts[i]);
 			sig_ss7_set_inservice(linkset->pvts[i], 1);
+			sig_ss7_unlock_private(linkset->pvts[i]);
+		}
+	}
+}
+
+static int ss7_find_alloc_call(struct sig_ss7_chan *p)
+{
+	if (!p) {
+		return 0;
+	}
+
+	if (!p->ss7call) {
+		p->ss7call = isup_new_call(p->ss7->ss7, p->cic, p->dpc, 0);
+		if (!p->ss7call) {
+			return 0;
+		}
 	}
+	return 1;
 }
 
+/*
+ * XXX This routine is not tolerant of holes in the pvts[] array.
+ * XXX This routine assumes the cic's in the pvts[] array are sorted.
+ *
+ * Probably the easiest way to deal with the invalid assumptions
+ * is to have a local pvts[] array and sort it by dpc and cic.
+ * Then the existing algorithm could work.
+ */
 static void ss7_reset_linkset(struct sig_ss7_linkset *linkset)
 {
-	int i, startcic = -1, endcic, dpc;
+	int i, startcic, endcic, dpc;
+	struct sig_ss7_chan *p;
 
-	if (linkset->numchans <= 0)
+	if (linkset->numchans <= 0) {
 		return;
+	}
 
 	startcic = linkset->pvts[0]->cic;
+	p = linkset->pvts[0];
 	/* DB: CIC's DPC fix */
 	dpc = linkset->pvts[0]->dpc;
 
 	for (i = 0; i < linkset->numchans; i++) {
-		if (linkset->pvts[i+1] && linkset->pvts[i+1]->dpc == dpc && ((linkset->pvts[i+1]->cic - linkset->pvts[i]->cic) == 1) && (linkset->pvts[i]->cic - startcic < 31)) {
+		if (linkset->pvts[i+1]
+			&& linkset->pvts[i+1]->dpc == dpc
+			&& linkset->pvts[i+1]->cic - linkset->pvts[i]->cic == 1
+			&& linkset->pvts[i]->cic - startcic < (linkset->type == SS7_ANSI ? 24 : 31)) {
 			continue;
 		} else {
 			endcic = linkset->pvts[i]->cic;
-			ast_verbose("Resetting CICs %d to %d\n", startcic, endcic);
-			isup_grs(linkset->ss7, startcic, endcic, dpc);
+			ast_verb(1, "Resetting CICs %d to %d\n", startcic, endcic);
+
+			sig_ss7_lock_private(p);
+			if (!ss7_find_alloc_call(p)) {
+				ast_log(LOG_ERROR, "Unable to allocate new ss7call\n");
+			} else if (!(endcic - startcic)) {	/* GRS range can not be 0 - use RSC instead */
+				isup_rsc(linkset->ss7, p->ss7call);
+			} else {
+				isup_grs(linkset->ss7, p->ss7call, endcic);
+			}
+			sig_ss7_unlock_private(p);
 
 			/* DB: CIC's DPC fix */
 			if (linkset->pvts[i+1]) {
 				startcic = linkset->pvts[i+1]->cic;
 				dpc = linkset->pvts[i+1]->dpc;
+				p = linkset->pvts[i+1];
+			}
+		}
+	}
+}
+
+/*!
+ * \internal
+ * \brief Complete the RSC procedure started earlier
+ * \since 11.0
+ *
+ * \param p Signaling private structure pointer.
+ *
+ * \note Assumes the ss7->lock is already obtained.
+ * \note Assumes sig_ss7_lock_private(p) is already obtained.
+ *
+ * \return Nothing.
+ */
+static void ss7_do_rsc(struct sig_ss7_chan *p)
+{
+	if (!p || !p->ss7call) {
+		return;
+	}
+
+	isup_rsc(p->ss7->ss7, p->ss7call);
+
+	if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) {
+		isup_blo(p->ss7->ss7, p->ss7call);
+	} else {
+		sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Start RSC procedure on a specific link
+ * \since 11.0
+ *
+ * \param ss7 SS7 linkset control structure.
+ * \param which Channel position in the span.
+ *
+ * \note Assumes the ss7->lock is already obtained.
+ * \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained.
+ *
+ * \return TRUE on success
+ */
+static int ss7_start_rsc(struct sig_ss7_linkset *linkset, int which)
+{
+	if (!linkset->pvts[which]) {
+		return 0;
+	}
+
+	if (!ss7_find_alloc_call(linkset->pvts[which])) {
+		return 0;
+	}
+
+	sig_ss7_set_remotelyblocked(linkset->pvts[which], 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+	sig_ss7_set_inservice(linkset->pvts[which], 0);
+	sig_ss7_loopback(linkset->pvts[which], 0);
+
+	sig_ss7_lock_owner(linkset, which);
+	if (linkset->pvts[which]->owner) {
+		ast_channel_hangupcause_set(linkset->pvts[which]->owner, AST_CAUSE_NORMAL_CLEARING);
+		ast_softhangup_nolock(linkset->pvts[which]->owner, AST_SOFTHANGUP_DEV);
+		ast_channel_unlock(linkset->pvts[which]->owner);
+		linkset->pvts[which]->do_hangup = SS7_HANGUP_SEND_RSC;
+	} else {
+		ss7_do_rsc(linkset->pvts[which]);
+	}
+
+	return 1;
+}
+
+/*!
+ * \internal
+ * \brief Determine if a private channel structure is available.
+ * \since 11.0
+ *
+ * \param linkset SS7 linkset control structure.
+ * \param startcic Start CIC of the range to clear.
+ * \param endcic End CIC of the range to clear.
+ * \param dpc Destination Point Code.
+ * \param do_hangup What we have to do to clear the call.
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ * \note Must be called without sig_ss7_lock_private() obtained.
+ *
+ * \return Nothing.
+ */
+static void ss7_clear_channels(struct sig_ss7_linkset *linkset, int startcic, int endcic, int dpc, int do_hangup)
+{
+	int i;
+
+	for (i = 0; i < linkset->numchans; i++) {
+		if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
+			sig_ss7_lock_private(linkset->pvts[i]);
+			sig_ss7_set_inservice(linkset->pvts[i], 0);
+			sig_ss7_lock_owner(linkset, i);
+			if (linkset->pvts[i]->owner) {
+				ast_channel_hangupcause_set(linkset->pvts[i]->owner,
+											AST_CAUSE_NORMAL_CLEARING);
+				ast_softhangup_nolock(linkset->pvts[i]->owner, AST_SOFTHANGUP_DEV);
+				ast_channel_unlock(linkset->pvts[i]->owner);
+				linkset->pvts[i]->do_hangup = (linkset->pvts[i]->cic != startcic) ?
+											do_hangup : SS7_HANGUP_DO_NOTHING;
+			} else if (linkset->pvts[i] && linkset->pvts[i]->cic != startcic) {
+				isup_free_call(linkset->pvts[i]->ss7->ss7, linkset->pvts[i]->ss7call);
+				linkset->pvts[i]->ss7call = NULL;
 			}
+			sig_ss7_unlock_private(linkset->pvts[i]);
 		}
 	}
 }
 
-/* This function is assumed to be called with the private channel lock and linkset lock held */
+/*!
+ * \internal
+ *
+ * \param p Signaling private structure pointer.
+ * \param linkset SS7 linkset control structure.
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ * \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained.
+ *
+ * \return Nothing.
+ */
 static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *linkset)
 {
 	struct ss7 *ss7 = linkset->ss7;
 	int law;
 	struct ast_channel *c;
 	char tmp[256];
+	char *strp;
 	struct ast_callid *callid = NULL;
 	int callid_created = ast_callid_threadstorage_auto(&callid);
 
@@ -600,6 +993,8 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
 		law = SIG_SS7_ULAW;
 	}
 
+	isup_set_echocontrol(p->ss7call, (linkset->flags & LINKSET_FLAG_DEFAULTECHOCONTROL) ? 1 : 0);
+
 	/*
 	 * Release the SS7 lock while we create the channel so other
 	 * threads can send messages.  We must also release the private
@@ -614,7 +1009,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
 		sig_ss7_lock_private(p);
 		isup_rel(linkset->ss7, p->ss7call, AST_CAUSE_SWITCH_CONGESTION);
 		p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
-		p->alreadyhungup = 1;
 		ast_callid_threadstorage_auto_clean(callid, callid_created);
 		return;
 	}
@@ -623,8 +1017,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
 	ast_channel_lock(c);
 	sig_ss7_lock_private(p);
 
-	sig_ss7_set_echocanceller(p, 1);
-
 	ast_channel_stage_snapshot(c);
 
 	/*
@@ -658,11 +1050,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
 		/* Clear this after we set it */
 		p->gen_dig_number[0] = 0;
 	}
-	if (!ast_strlen_zero(p->orig_called_num)) {
-		pbx_builtin_setvar_helper(c, "SS7_ORIG_CALLED_NUM", p->orig_called_num);
-		/* Clear this after we set it */
-		p->orig_called_num[0] = 0;
-	}
 
 	snprintf(tmp, sizeof(tmp), "%d", p->gen_dig_type);
 	pbx_builtin_setvar_helper(c, "SS7_GENERIC_DIGTYPE", tmp);
@@ -695,28 +1082,187 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
 	/* Clear this after we set it */
 	p->calling_party_cat = 0;
 
-	if (!ast_strlen_zero(p->redirecting_num)) {
-		pbx_builtin_setvar_helper(c, "SS7_REDIRECTING_NUMBER", p->redirecting_num);
-		/* Clear this after we set it */
-		p->redirecting_num[0] = 0;
-	}
-	if (!ast_strlen_zero(p->generic_name)) {
-		pbx_builtin_setvar_helper(c, "SS7_GENERIC_NAME", p->generic_name);
+	if (p->redirect_counter) {
+		struct ast_party_redirecting redirecting;
+
+		switch (p->redirect_info_ind) {
+		case 0:
+			strp = "NO_REDIRECTION";
+			break;
+		case 1:
+			strp = "CALL_REROUTED_PRES_ALLOWED";
+			break;
+		case 2:
+			strp = "CALL_REROUTED_INFO_RESTRICTED";
+			break;
+		case 3:
+			strp = "CALL_DIVERTED_PRES_ALLOWED";
+			break;
+		case 4:
+			strp = "CALL_DIVERTED_INFO_RESTRICTED";
+			break;
+		case 5:
+			strp = "CALL_REROUTED_PRES_RESTRICTED";
+			break;
+		case 6:
+			strp = "CALL_DIVERTED_PRES_RESTRICTED";
+			break;
+		case 7:
+			strp = "SPARE";
+			break;
+		default:
+			strp = "NO_REDIRECTION";
+			break;
+		}
+		pbx_builtin_setvar_helper(c, "SS7_REDIRECT_INFO_IND", strp);
 		/* Clear this after we set it */
-		p->generic_name[0] = 0;
-	}
+		p->redirect_info_ind = 0;
 
-	ast_channel_stage_snapshot_done(c);
+		ast_party_redirecting_init(&redirecting);
 
-	sig_ss7_unlock_private(p);
-	ast_channel_unlock(c);
+		if (p->redirect_info_counter) {
+			redirecting.count = p->redirect_info_counter;
+			if (p->redirect_info_counter != p->redirect_counter) {
+				if (p->redirect_info_counter < p->redirect_counter) {
+					redirecting.count = p->redirect_counter;
+				}
+				ast_log(LOG_WARNING, "Redirect counters differ: %u while info says %u - using %u\n",
+					p->redirect_counter, p->redirect_info_counter, redirecting.count);
+			}
+			/* Clear this after we set it */
+			p->redirect_info_counter = 0;
+			p->redirect_counter = 0;
+		}
 
-	if (ast_pbx_start(c)) {
-		ast_log(LOG_WARNING, "Unable to start PBX on %s (CIC %d)\n", ast_channel_name(c), p->cic);
-		ast_hangup(c);
-	} else {
-		ast_verb(3, "Accepting call to '%s' on CIC %d\n", p->exten, p->cic);
-	}
+		if (p->redirect_counter) {
+			redirecting.count = p->redirect_counter;
+			/* Clear this after we set it */
+			p->redirect_counter = 0;
+		}
+
+		switch (p->redirect_info_orig_reas) {
+		case SS7_REDIRECTING_REASON_UNKNOWN:
+			redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+			break;
+		case SS7_REDIRECTING_REASON_USER_BUSY:
+			redirecting.orig_reason.code = AST_REDIRECTING_REASON_USER_BUSY;
+			break;
+		case SS7_REDIRECTING_REASON_NO_ANSWER:
+			redirecting.orig_reason.code = AST_REDIRECTING_REASON_NO_ANSWER;
+			break;
+		case SS7_REDIRECTING_REASON_UNCONDITIONAL:
+			redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL;
+			break;
+		default:
+			redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+			break;
+		}
+
+		switch (p->redirect_info_reas) {
+		case SS7_REDIRECTING_REASON_UNKNOWN:
+			redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+			break;
+		case SS7_REDIRECTING_REASON_USER_BUSY:
+			redirecting.reason.code = AST_REDIRECTING_REASON_USER_BUSY;
+			if (!p->redirect_info_orig_reas && redirecting.count == 1) {
+				redirecting.orig_reason.code = AST_REDIRECTING_REASON_USER_BUSY;
+			}
+			break;
+		case SS7_REDIRECTING_REASON_NO_ANSWER:
+			redirecting.reason.code = AST_REDIRECTING_REASON_NO_ANSWER;
+			if (!p->redirect_info_orig_reas && redirecting.count == 1) {
+				redirecting.orig_reason.code = AST_REDIRECTING_REASON_NO_ANSWER;
+			}
+			break;
+		case SS7_REDIRECTING_REASON_UNCONDITIONAL:
+			redirecting.reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL;
+			if (!p->redirect_info_orig_reas && redirecting.count == 1) {
+				redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL;
+			}
+			break;
+		case SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING:
+		case SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE:
+			redirecting.reason.code = AST_REDIRECTING_REASON_DEFLECTION;
+			break;
+		case SS7_REDIRECTING_REASON_UNAVAILABLE:
+			redirecting.reason.code = AST_REDIRECTING_REASON_UNAVAILABLE;
+			break;
+		default:
+			redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+			break;
+		}
+		/* Clear this after we set it */
+		p->redirect_info_orig_reas = 0;
+		p->redirect_info_reas = 0;
+
+		if (!ast_strlen_zero(p->redirecting_num)) {
+			redirecting.from.number.str = ast_strdup(p->redirecting_num);
+			redirecting.from.number.presentation = p->redirecting_presentation;
+			redirecting.from.number.valid = 1;
+			/* Clear this after we set it */
+			p->redirecting_num[0] = 0;
+		}
+
+		if (!ast_strlen_zero(p->generic_name)) {
+			redirecting.from.name.str = ast_strdup(p->generic_name);
+			redirecting.from.name.presentation = p->redirecting_presentation;
+			redirecting.from.name.valid = 1;
+			/* Clear this after we set it */
+			p->generic_name[0] = 0;
+		}
+
+		if (!ast_strlen_zero(p->orig_called_num)) {
+			redirecting.orig.number.str = ast_strdup(p->orig_called_num);
+			redirecting.orig.number.presentation = p->orig_called_presentation;
+			redirecting.orig.number.valid = 1;
+			/* Clear this after we set it */
+			p->orig_called_num[0] = 0;
+		} else if (redirecting.count == 1) {
+			ast_party_id_copy(&redirecting.orig, &redirecting.from);
+		}
+
+		ast_channel_update_redirecting(c, &redirecting, NULL);
+		ast_party_redirecting_free(&redirecting);
+	}
+
+	if (p->cug_indicator != ISUP_CUG_NON) {
+		sprintf(tmp, "%d", p->cug_interlock_code);
+		pbx_builtin_setvar_helper(c, "SS7_CUG_INTERLOCK_CODE", tmp);
+
+		switch (p->cug_indicator) {
+		case ISUP_CUG_NON:
+			strp = "NON_CUG";
+			break;
+		case ISUP_CUG_OUTGOING_ALLOWED:
+			strp = "OUTGOING_ALLOWED";
+			break;
+		case ISUP_CUG_OUTGOING_NOT_ALLOWED:
+			strp = "OUTGOING_NOT_ALLOWED";
+			break;
+		default:
+			strp = "SPARE";
+			break;
+		}
+		pbx_builtin_setvar_helper(c, "SS7_CUG_INDICATOR", strp);
+
+		if (!ast_strlen_zero(p->cug_interlock_ni)) {
+			pbx_builtin_setvar_helper(c, "SS7_CUG_INTERLOCK_NI", p->cug_interlock_ni);
+		}
+
+		p->cug_indicator = ISUP_CUG_NON;
+	}
+
+	ast_channel_stage_snapshot_done(c);
+
+	sig_ss7_unlock_private(p);
+	ast_channel_unlock(c);
+
+	if (ast_pbx_start(c)) {
+		ast_log(LOG_WARNING, "Unable to start PBX on %s (CIC %d)\n", ast_channel_name(c), p->cic);
+		ast_hangup(c);
+	} else {
+		ast_verb(3, "Accepting call to '%s' on CIC %d\n", p->exten, p->cic);
+	}
 
 	/* Must return with linkset and private lock. */
 	ast_mutex_lock(&linkset->lock);
@@ -745,6 +1291,9 @@ static void ss7_apply_plan_to_number(char *buf, size_t size, const struct sig_ss
 	case SS7_NAI_UNKNOWN:
 		snprintf(buf, size, "%s%s", ss7->unknownprefix, number);
 		break;
+	case SS7_NAI_NETWORKROUTED:
+		snprintf(buf, size, "%s%s", ss7->networkroutedprefix, number);
+		break;
 	default:
 		snprintf(buf, size, "%s", number);
 		break;
@@ -786,6 +1335,50 @@ static struct ast_callid *func_ss7_linkset_callid(struct sig_ss7_linkset *linkse
 	return callid;
 }
 
+/*!
+ * \internal
+ * \brief Proceed with the call based on the extension matching status
+ * is matching in the dialplan.
+ * \since 11.0
+ *
+ * \param linkset ss7 span control structure.
+ * \param p Signaling private structure pointer.
+ * \param e Event causing the match.
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ * \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained.
+ *
+ * \return Nothing.
+ */
+static void ss7_match_extension(struct sig_ss7_linkset *linkset, struct sig_ss7_chan *p, ss7_event *e)
+{
+	ast_verb(3, "SS7 exten: %s complete: %d\n", p->exten, p->called_complete);
+
+	if (!p->called_complete
+		&& linkset->type == SS7_ITU /* ANSI does not support overlap dialing. */
+		&& ast_matchmore_extension(NULL, p->context, p->exten, 1, p->cid_num)
+		&& !isup_start_digittimeout(linkset->ss7, p->ss7call)) {
+		/* Wait for more digits. */
+		return;
+	}
+	if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
+		/* DNID is complete */
+		p->called_complete = 1;
+		sig_ss7_set_dnid(p, p->exten);
+
+		/* If COT successful start call! */
+		if ((e->e == ISUP_EVENT_IAM)
+			? !(e->iam.cot_check_required || e->iam.cot_performed_on_previous_cic)
+			: (!(e->sam.cot_check_required || e->sam.cot_performed_on_previous_cic) || e->sam.cot_check_passed)) {
+			ss7_start_call(p, linkset);
+		}
+		return;
+	}
+
+	ast_debug(1, "Call on CIC for unconfigured extension %s\n", p->exten);
+	isup_rel(linkset->ss7, (e->e == ISUP_EVENT_IAM) ? e->iam.call : e->sam.call, AST_CAUSE_UNALLOCATED);
+}
+
 /* This is a thread per linkset that handles all received events from libss7. */
 void *ss7_linkset(void *data)
 {
@@ -796,6 +1389,7 @@ void *ss7_linkset(void *data)
 	ss7_event *e = NULL;
 	struct sig_ss7_chan *p;
 	struct pollfd pollers[SIG_SS7_NUM_DCHANS];
+	unsigned char mb_state[255];
 	int nextms;
 
 #define SS7_MAX_POLL	60000	/* Maximum poll time in ms. */
@@ -843,16 +1437,13 @@ void *ss7_linkset(void *data)
 		pthread_testcancel();
 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
 
+		ast_mutex_lock(&linkset->lock);
 		if ((res < 0) && (errno != EINTR)) {
 			ast_log(LOG_ERROR, "poll(%s)\n", strerror(errno));
 		} else if (!res) {
-			ast_mutex_lock(&linkset->lock);
 			ss7_schedule_run(ss7);
-			ast_mutex_unlock(&linkset->lock);
-			continue;
 		}
 
-		ast_mutex_lock(&linkset->lock);
 		for (i = 0; i < linkset->numsigchans; i++) {
 			if (pollers[i].revents & POLLPRI) {
 				sig_ss7_handle_link_exception(linkset, i);
@@ -881,23 +1472,26 @@ void *ss7_linkset(void *data)
 			switch (e->e) {
 			case SS7_EVENT_UP:
 				if (linkset->state != LINKSET_STATE_UP) {
-					ast_verbose("--- SS7 Up ---\n");
+					ast_verb(1, "--- SS7 Up ---\n");
 					ss7_reset_linkset(linkset);
 				}
 				linkset->state = LINKSET_STATE_UP;
 				break;
 			case SS7_EVENT_DOWN:
-				ast_verbose("--- SS7 Down ---\n");
+				ast_verb(1, "--- SS7 Down ---\n");
 				linkset->state = LINKSET_STATE_DOWN;
 				for (i = 0; i < linkset->numchans; i++) {
 					p = linkset->pvts[i];
 					if (p) {
-						sig_ss7_set_alarm(p, 1);
+						sig_ss7_set_inservice(p, 0);
+						if (linkset->flags & LINKSET_FLAG_INITIALHWBLO) {
+							sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_HARDWARE);
+						}
 					}
 				}
 				break;
 			case MTP2_LINK_UP:
-				ast_verbose("MTP2 link up (SLC %d)\n", e->gen.data);
+				ast_verb(1, "MTP2 link up (SLC %d)\n", e->gen.data);
 				break;
 			case MTP2_LINK_DOWN:
 				ast_log(LOG_WARNING, "MTP2 link down (SLC %d)\n", e->gen.data);
@@ -905,6 +1499,7 @@ void *ss7_linkset(void *data)
 			case ISUP_EVENT_CPG:
 				chanpos = ss7_find_cic_gripe(linkset, e->cpg.cic, e->cpg.opc, "CPG");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->cpg.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
@@ -919,6 +1514,21 @@ void *ss7_linkset(void *data)
 					sig_ss7_lock_owner(linkset, chanpos);
 					if (p->owner) {
 						ast_setstate(p->owner, AST_STATE_RINGING);
+						if (!ast_strlen_zero(e->cpg.connected_num)) {
+							struct ast_party_connected_line ast_connected;
+							char connected_num[AST_MAX_EXTENSION];
+
+							ast_party_connected_line_init(&ast_connected);
+							ast_connected.id.number.presentation =
+								ss7_pres_scr2cid_pres(e->cpg.connected_presentation_ind,
+								e->cpg.connected_screening_ind);
+							ss7_apply_plan_to_number(connected_num, sizeof(connected_num),
+								linkset, e->cpg.connected_num, e->cpg.connected_nai);
+							ast_connected.id.number.str = ast_strdup(connected_num);
+							ast_connected.id.number.valid = 1;
+							ast_channel_queue_connected_line_update(p->owner, &ast_connected, NULL);
+							ast_party_connected_line_free(&ast_connected);
+						}
 						ast_channel_unlock(p->owner);
 					}
 					sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING);
@@ -941,95 +1551,201 @@ void *ss7_linkset(void *data)
 				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_RSC:
-				ast_verbose("Resetting CIC %d\n", e->rsc.cic);
+				ast_verb(1, "Resetting CIC %d\n", e->rsc.cic);
 				chanpos = ss7_find_cic_gripe(linkset, e->rsc.cic, e->rsc.opc, "RSC");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->rsc.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
 				sig_ss7_lock_private(p);
+				p->ss7call = e->rsc.call;
 				callid = func_ss7_linkset_callid(linkset, chanpos);
 				sig_ss7_set_inservice(p, 1);
-				sig_ss7_set_remotelyblocked(p, 0);
+				sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+
+				if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) {
+					isup_blo(ss7, e->rsc.call);
+				} else if (p->locallyblocked & SS7_BLOCKED_HARDWARE) {
+					sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_HARDWARE);
+				}
+
 				isup_set_call_dpc(e->rsc.call, p->dpc);
 				sig_ss7_lock_owner(linkset, chanpos);
-				p->ss7call = NULL;
 				if (p->owner) {
+					p->do_hangup = SS7_HANGUP_SEND_RLC;
+					if (!(e->rsc.got_sent_msg & ISUP_SENT_IAM)) {
+						/* Q.784 6.2.3 */
+						ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING);
+					} else {
+						ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN);
+					}
+
 					ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_RSC", AST_CAUSE_INTERWORKING);
+
 					ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
 					ast_channel_unlock(p->owner);
+				} else {
+					isup_rlc(ss7, e->rsc.call);
+					p->ss7call = isup_free_call_if_clear(ss7, e->rsc.call);
 				}
+				/* End the loopback if we have one */
+				sig_ss7_loopback(p, 0);
+
 				sig_ss7_unlock_private(p);
-				isup_rlc(ss7, e->rsc.call);
 				break;
 			case ISUP_EVENT_GRS:
-				ast_debug(1, "Got Reset for CICs %d to %d: Acknowledging\n", e->grs.startcic, e->grs.endcic);
-				chanpos = ss7_find_cic_gripe(linkset, e->grs.startcic, e->grs.opc, "GRS");
-				if (chanpos < 0) {
+				if (!sig_ss7_find_cic_range(linkset, e->grs.startcic, e->grs.endcic,
+					e->grs.opc)) {
+					ast_log(LOG_WARNING, "GRS on unconfigured range CIC %d - %d PC %d\n",
+						e->grs.startcic, e->grs.endcic, e->grs.opc);
+					chanpos = sig_ss7_find_cic(linkset, e->grs.startcic, e->grs.opc);
+					if (chanpos < 0) {
+						isup_free_call(ss7, e->grs.call);
+						break;
+					}
+					p = linkset->pvts[chanpos];
+					sig_ss7_lock_private(p);
+					p->ss7call = isup_free_call_if_clear(ss7, e->grs.call);
+					sig_ss7_unlock_private(p);
 					break;
 				}
-				p = linkset->pvts[chanpos];
-				isup_gra(ss7, e->grs.startcic, e->grs.endcic, e->grs.opc);
-				ss7_block_cics(linkset, e->grs.startcic, e->grs.endcic, e->grs.opc, NULL, 0);
-				ss7_hangup_cics(linkset, e->grs.startcic, e->grs.endcic, e->grs.opc);
+
+				/* Leave startcic last to collect all cics mb_state */
+				for (i = e->grs.endcic - e->grs.startcic; 0 <= i; --i) {
+					/*
+					 * We are guaranteed to find chanpos because
+					 * sig_ss7_find_cic_range() includes it.
+					 */
+					chanpos = sig_ss7_find_cic(linkset, e->grs.startcic + i, e->grs.opc);
+					p = linkset->pvts[chanpos];
+					sig_ss7_lock_private(p);
+
+					if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) {
+						mb_state[i] = 1;
+					} else {
+						mb_state[i] = 0;
+						sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_HARDWARE);
+					}
+
+					sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+
+					if (!i) {
+						p->ss7call = e->grs.call;
+						isup_gra(ss7, p->ss7call, e->grs.endcic, mb_state);
+					}
+
+					sig_ss7_lock_owner(linkset, chanpos);
+					if (p->owner) {
+						ast_channel_softhangup_internal_flag_add(p->owner, AST_SOFTHANGUP_DEV);
+						if (ast_channel_state(p->owner) == AST_STATE_DIALING
+							&& linkset->pvts[i]->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) {
+							ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN);
+						} else {
+							ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING);
+						}
+						p->do_hangup = SS7_HANGUP_FREE_CALL;
+						ast_channel_unlock(p->owner);
+					} else if (!i) {
+						p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+					} else if (p->ss7call) {
+						/* clear any other session */
+						isup_free_call(ss7, p->ss7call);
+						p->ss7call = NULL;
+					}
+					sig_ss7_set_inservice(p, 1);
+					sig_ss7_unlock_private(p);
+				}
 				break;
 			case ISUP_EVENT_CQM:
-				ast_debug(1, "Got Circuit group query message from CICs %d to %d\n", e->cqm.startcic, e->cqm.endcic);
-				ss7_handle_cqm(linkset, e->cqm.startcic, e->cqm.endcic, e->cqm.opc);
+				ast_debug(1, "Got Circuit group query message from CICs %d to %d\n",
+							e->cqm.startcic, e->cqm.endcic);
+				ss7_handle_cqm(linkset, e);
 				break;
 			case ISUP_EVENT_GRA:
-				ast_verbose("Got reset acknowledgement from CIC %d to %d.\n", e->gra.startcic, e->gra.endcic);
+				if (!sig_ss7_find_cic_range(linkset, e->gra.startcic,
+							e->gra.endcic, e->gra.opc)) {	/* Never will be true */
+					ast_log(LOG_WARNING, "GRA on unconfigured range CIC %d - %d PC %d\n",
+							e->gra.startcic, e->gra.endcic, e->gra.opc);
+					isup_free_call(ss7, e->gra.call);
+					break;
+				}
+				ast_verb(1, "Got reset acknowledgement from CIC %d to %d DPC: %d\n",
+					e->gra.startcic, e->gra.endcic, e->gra.opc);
+				ss7_block_cics(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc,
+					e->gra.status, 1, 1, SS7_BLOCKED_MAINTENANCE);
 				ss7_inservice(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc);
-				ss7_block_cics(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc, e->gra.status, 1);
+
+				chanpos = sig_ss7_find_cic(linkset, e->gra.startcic, e->gra.opc);
+				if (chanpos < 0) {
+					isup_free_call(ss7, e->gra.call);
+					break;
+				}
+
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+
+				/* we may send a CBD with GRS! */
+				p->ss7call = isup_free_call_if_clear(ss7, e->gra.call);
+				sig_ss7_unlock_private(p);
 				break;
-			case ISUP_EVENT_IAM:
-				ast_debug(1, "Got IAM for CIC %d and called number %s, calling number %s\n", e->iam.cic, e->iam.called_party_num, e->iam.calling_party_num);
-				chanpos = ss7_find_cic_gripe(linkset, e->iam.cic, e->iam.opc, "IAM");
+			case ISUP_EVENT_SAM:
+				chanpos = ss7_find_cic_gripe(linkset, e->sam.cic, e->sam.opc, "SAM");
 				if (chanpos < 0) {
-					isup_rel(ss7, e->iam.call, -1);
+					isup_free_call(ss7, e->sam.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
 				sig_ss7_lock_private(p);
 				sig_ss7_lock_owner(linkset, chanpos);
-				if (p->call_level != SIG_SS7_CALL_LEVEL_IDLE) {
-					/*
-					 * Detected glare/dual-seizure
-					 *
-					 * Always abort both calls since we can't implement the dual
-					 * seizure procedures due to our channel assignment architecture
-					 * and the fact that we cannot tell libss7 to discard its call
-					 * structure to ignore the incoming IAM.
-					 */
-					ast_debug(1,
-						"Linkset %d: SS7 IAM glare on CIC/DPC %d/%d.  Dropping both calls.\n",
-						linkset->span, e->iam.cic, e->iam.opc);
-					if (p->call_level == SIG_SS7_CALL_LEVEL_ALLOCATED) {
-						/*
-						 * We have not sent our IAM yet and we never will at this point.
-						 */
-						p->alreadyhungup = 1;
-						isup_rel(ss7, e->iam.call, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-					}
-					p->call_level = SIG_SS7_CALL_LEVEL_GLARE;
-					if (p->owner) {
-						ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_IAM (glare)", AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-						ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-						ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
-						ast_channel_unlock(p->owner);
-					}
+				if (p->owner) {
+					ast_log(LOG_WARNING, "SAM on CIC %d PC %d already have call\n", e->sam.cic, e->sam.opc);
+					ast_channel_unlock(p->owner);
 					sig_ss7_unlock_private(p);
 					break;
 				}
+				p->called_complete = 0;
+				if (!ast_strlen_zero(e->sam.called_party_num)) {
+					char *st;
+					strncat(p->exten, e->sam.called_party_num, sizeof(p->exten) - strlen(p->exten) - 1);
+					st = strchr(p->exten, '#');
+					if (st) {
+						*st = '\0';
+						p->called_complete = 1;
+					}
+					ss7_match_extension(linkset, p, e);
+				}
+				sig_ss7_unlock_private(p);
+				break;
+			case ISUP_EVENT_IAM:
+				ast_debug(1, "Got IAM for CIC %d and called number %s, calling number %s\n", e->iam.cic, e->iam.called_party_num, e->iam.calling_party_num);
+				chanpos = ss7_find_cic_gripe(linkset, e->iam.cic, e->iam.opc, "IAM");
+				if (chanpos < 0) {
+					isup_free_call(ss7, e->iam.call);
+					break;
+				}
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
 				/*
-				 * The channel should not have an owner at this point since we
+				 * The channel should be idle and not have an owner at this point since we
 				 * are in the process of creating an owner for it.
 				 */
-				ast_assert(!p->owner);
+				ast_assert(!p->owner && p->call_level == SIG_SS7_CALL_LEVEL_IDLE);
+
+				if (p->remotelyblocked) {
+					ast_log(LOG_NOTICE, "Got IAM on remotely blocked CIC %d DPC %d remove blocking\n", e->iam.cic, e->iam.opc);
+					sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+					sig_ss7_set_inservice(p, 1);
+				}
 
 				if (!sig_ss7_is_chan_available(p)) {
 					/* Circuit is likely blocked or in alarm. */
 					isup_rel(ss7, e->iam.call, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
+					if (p->locallyblocked) {
+						isup_clear_callflags(ss7, e->iam.call, ISUP_GOT_IAM);
+						p->ss7call = isup_free_call_if_clear(ss7, e->iam.call);
+						ast_log(LOG_WARNING, "Got IAM on locally blocked CIC %d DPC %d, ignore\n", e->iam.cic, e->iam.opc);
+					}
 					sig_ss7_unlock_private(p);
 					break;
 				}
@@ -1043,18 +1759,14 @@ void *ss7_linkset(void *data)
 				if ((p->use_callerid) && (!ast_strlen_zero(e->iam.calling_party_num))) {
 					ss7_apply_plan_to_number(p->cid_num, sizeof(p->cid_num), linkset, e->iam.calling_party_num, e->iam.calling_nai);
 					p->callingpres = ss7_pres_scr2cid_pres(e->iam.presentation_ind, e->iam.screening_ind);
-				} else
-					p->cid_num[0] = 0;
-
-				/* Set DNID */
-				if (!ast_strlen_zero(e->iam.called_party_num)) {
-					ss7_apply_plan_to_number(p->exten, sizeof(p->exten), linkset,
-						e->iam.called_party_num, e->iam.called_nai);
 				} else {
-					p->exten[0] = '\0';
+					p->cid_num[0] = 0;
+					if (e->iam.presentation_ind) {
+						p->callingpres = ss7_pres_scr2cid_pres(e->iam.presentation_ind, e->iam.screening_ind);
+					}
 				}
-				sig_ss7_set_dnid(p, p->exten);
 
+				p->called_complete = 0;
 				if (p->immediate) {
 					p->exten[0] = 's';
 					p->exten[1] = '\0';
@@ -1064,16 +1776,18 @@ void *ss7_linkset(void *data)
 					st = strchr(p->exten, '#');
 					if (st) {
 						*st = '\0';
+						p->called_complete = 1;
 					}
 				} else {
 					p->exten[0] = '\0';
 				}
 
 				p->cid_ani[0] = '\0';
-				if ((p->use_callerid) && (!ast_strlen_zero(e->iam.generic_name)))
+				if ((p->use_callerid) && (!ast_strlen_zero(e->iam.generic_name))) {
 					ast_copy_string(p->cid_name, e->iam.generic_name, sizeof(p->cid_name));
-				else
+				} else {
 					p->cid_name[0] = '\0';
+				}
 
 				p->cid_ani2 = e->iam.oli_ani2;
 				p->cid_ton = 0;
@@ -1087,81 +1801,162 @@ void *ss7_linkset(void *data)
 				p->gen_dig_type = e->iam.gen_dig_type;
 				p->gen_dig_scheme = e->iam.gen_dig_scheme;
 				ast_copy_string(p->jip_number, e->iam.jip_number, sizeof(p->jip_number));
-				ast_copy_string(p->orig_called_num, e->iam.orig_called_num, sizeof(p->orig_called_num));
-				ast_copy_string(p->redirecting_num, e->iam.redirecting_num, sizeof(p->redirecting_num));
+				if (!ast_strlen_zero(e->iam.orig_called_num)) {
+					ss7_apply_plan_to_number(p->orig_called_num, sizeof(p->orig_called_num), linkset, e->iam.orig_called_num, e->iam.orig_called_nai);
+					p->orig_called_presentation = ss7_pres_scr2cid_pres(e->iam.orig_called_pres_ind, e->iam.orig_called_screening_ind);
+				}
+				if (!ast_strlen_zero(e->iam.redirecting_num)) {
+					ss7_apply_plan_to_number(p->redirecting_num, sizeof(p->redirecting_num), linkset, e->iam.redirecting_num, e->iam.redirecting_num_nai);
+					p->redirecting_presentation = ss7_pres_scr2cid_pres(e->iam.redirecting_num_presentation_ind, e->iam.redirecting_num_screening_ind);
+				}
 				ast_copy_string(p->generic_name, e->iam.generic_name, sizeof(p->generic_name));
 				p->calling_party_cat = e->iam.calling_party_cat;
+				p->redirect_counter = e->iam.redirect_counter;
+				p->redirect_info = e->iam.redirect_info;
+				p->redirect_info_ind = e->iam.redirect_info_ind;
+				p->redirect_info_orig_reas = e->iam.redirect_info_orig_reas;
+				p->redirect_info_counter = e->iam.redirect_info_counter;
+				if (p->redirect_info_counter && !p->redirect_counter) {
+					p->redirect_counter = p->redirect_info_counter;
+				}
+				p->redirect_info_reas = e->iam.redirect_info_reas;
+				p->cug_indicator = e->iam.cug_indicator;
+				p->cug_interlock_code = e->iam.cug_interlock_code;
+				ast_copy_string(p->cug_interlock_ni, e->iam.cug_interlock_ni, sizeof(p->cug_interlock_ni));
+
+				if (e->iam.cot_check_required) {
+					sig_ss7_loopback(p, 1);
+				}
 
+				p->echocontrol_ind = e->iam.echocontrol_ind;
 				sig_ss7_set_caller_id(p);
+				ss7_match_extension(linkset, p, e);
+				sig_ss7_unlock_private(p);
 
-				if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
-					if (e->iam.cot_check_required) {
-						p->call_level = SIG_SS7_CALL_LEVEL_CONTINUITY;
+				if (e->iam.cot_performed_on_previous_cic) {
+					chanpos = sig_ss7_find_cic(linkset, (e->iam.cic - 1), e->iam.opc);
+					if (chanpos < 0) {
+						/* some stupid switch do this */
+						ast_verb(1, "COT request on previous nonexistent CIC %d in IAM PC %d\n", (e->iam.cic - 1), e->iam.opc);
+						break;
+					}
+					ast_verb(1, "COT request on previous CIC %d in IAM PC %d\n", (e->iam.cic - 1), e->iam.opc);
+					p = linkset->pvts[chanpos];
+					sig_ss7_lock_private(p);
+					if (sig_ss7_is_chan_available(p)) {
+						sig_ss7_set_inservice(p, 0);	/* to prevent to use this circuit */
 						sig_ss7_loopback(p, 1);
-					} else {
+					} /* If already have a call don't loop */
+					sig_ss7_unlock_private(p);
+				}
+				break;
+			case ISUP_EVENT_DIGITTIMEOUT:
+				chanpos = ss7_find_cic_gripe(linkset, e->digittimeout.cic, e->digittimeout.opc, "DIGITTIMEOUT");
+				if (chanpos < 0) {
+					isup_free_call(ss7, e->digittimeout.call);
+					break;
+				}
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+				ast_debug(1, "Digittimeout on CIC: %d PC: %d\n", e->digittimeout.cic, e->digittimeout.opc);
+				if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
+					/* DNID is complete */
+					p->called_complete = 1;
+					sig_ss7_set_dnid(p, p->exten);
+
+					/* If COT successful start call! */
+					if (!(e->digittimeout.cot_check_required || e->digittimeout.cot_performed_on_previous_cic) || e->digittimeout.cot_check_passed) {
 						ss7_start_call(p, linkset);
 					}
 				} else {
 					ast_debug(1, "Call on CIC for unconfigured extension %s\n", p->exten);
-					p->alreadyhungup = 1;
-					isup_rel(ss7, e->iam.call, AST_CAUSE_UNALLOCATED);
+					isup_rel(linkset->ss7, e->digittimeout.call, AST_CAUSE_UNALLOCATED);
 				}
 				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_COT:
+				if (e->cot.cot_performed_on_previous_cic) {
+					chanpos = sig_ss7_find_cic(linkset, (e->cot.cic - 1), e->cot.opc);
+					/* some stupid switches do this!!! */
+					if (-1 < chanpos) {
+						p = linkset->pvts[chanpos];
+						sig_ss7_lock_private(p);
+						sig_ss7_set_inservice(p, 1);
+						sig_ss7_loopback(p, 0);
+						sig_ss7_unlock_private(p);;
+						ast_verb(1, "Loop turned off on CIC: %d PC: %d\n",  (e->cot.cic - 1), e->cot.opc);
+					}
+				}
+
 				chanpos = ss7_find_cic_gripe(linkset, e->cot.cic, e->cot.opc, "COT");
 				if (chanpos < 0) {
-					isup_rel(ss7, e->cot.call, -1);
+					isup_free_call(ss7, e->cot.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
 
 				sig_ss7_lock_private(p);
+				p->ss7call = e->cot.call;
+
 				if (p->loopedback) {
 					sig_ss7_loopback(p, 0);
+					ast_verb(1, "Loop turned off on CIC: %d PC: %d\n",  e->cot.cic, e->cot.opc);
+				}
+
+				/* Don't start call if we didn't get IAM or COT failed! */
+				if ((e->cot.got_sent_msg & ISUP_GOT_IAM) && e->cot.passed && p->called_complete) {
 					ss7_start_call(p, linkset);
 				}
+
+				p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
 				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_CCR:
 				ast_debug(1, "Got CCR request on CIC %d\n", e->ccr.cic);
 				chanpos = ss7_find_cic_gripe(linkset, e->ccr.cic, e->ccr.opc, "CCR");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->ccr.call);
 					break;
 				}
 
 				p = linkset->pvts[chanpos];
 
 				sig_ss7_lock_private(p);
+				p->ss7call = e->ccr.call;
 				sig_ss7_loopback(p, 1);
+				if (linkset->type == SS7_ANSI) {
+					isup_lpa(linkset->ss7, e->ccr.cic, p->dpc);
+				}
 				sig_ss7_unlock_private(p);
-
-				isup_lpa(linkset->ss7, e->ccr.cic, p->dpc);
 				break;
 			case ISUP_EVENT_CVT:
 				ast_debug(1, "Got CVT request on CIC %d\n", e->cvt.cic);
 				chanpos = ss7_find_cic_gripe(linkset, e->cvt.cic, e->cvt.opc, "CVT");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->cvt.call);
 					break;
 				}
 
 				p = linkset->pvts[chanpos];
 
 				sig_ss7_lock_private(p);
+				p->ss7call = e->cvt.call;
 				sig_ss7_loopback(p, 1);
-				sig_ss7_unlock_private(p);
-
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, e->cvt.call);
+				}
 				isup_cvr(linkset->ss7, e->cvt.cic, p->dpc);
+				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_REL:
 				chanpos = ss7_find_cic_gripe(linkset, e->rel.cic, e->rel.opc, "REL");
 				if (chanpos < 0) {
-					/* Continue hanging up the call anyway. */
-					isup_rlc(ss7, e->rel.call);
+					isup_free_call(ss7, e->rel.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
 				sig_ss7_lock_private(p);
+				p->ss7call = e->rel.call;
 				callid = func_ss7_linkset_callid(linkset, chanpos);
 				sig_ss7_lock_owner(linkset, chanpos);
 				if (p->owner) {
@@ -1170,128 +1965,193 @@ void *ss7_linkset(void *data)
 
 					ast_channel_hangupcause_set(p->owner, e->rel.cause);
 					ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+					p->do_hangup = SS7_HANGUP_SEND_RLC;
 					ast_channel_unlock(p->owner);
+				} else {
+					ast_verb(1, "REL on CIC %d DPC %d without owner!\n", p->cic, p->dpc);
+					isup_rlc(ss7, p->ss7call);
+					p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
 				}
 
 				/* End the loopback if we have one */
 				sig_ss7_loopback(p, 0);
 
-				isup_rlc(ss7, e->rel.call);
-				p->ss7call = NULL;
-
+				/* the rel is not complete here!!! */
 				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_ACM:
 				chanpos = ss7_find_cic_gripe(linkset, e->acm.cic, e->acm.opc, "ACM");
 				if (chanpos < 0) {
-					isup_rel(ss7, e->acm.call, -1);
+					isup_free_call(ss7, e->acm.call);
 					break;
 				}
-				{
-					p = linkset->pvts[chanpos];
 
-					ast_debug(1, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic);
+				p = linkset->pvts[chanpos];
 
-					if (e->acm.call_ref_ident > 0) {
-						p->rlt = 1; /* Setting it but not using it here*/
-					}
+				ast_debug(1, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic);
 
-					sig_ss7_lock_private(p);
-					callid = func_ss7_linkset_callid(linkset, chanpos);
-					sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_PROCEEDING);
-					if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) {
-						p->call_level = SIG_SS7_CALL_LEVEL_PROCEEDING;
+				if (e->acm.call_ref_ident > 0) {
+					p->rlt = 1; /* Setting it but not using it here*/
+				}
+
+				sig_ss7_lock_private(p);
+				p->ss7call = e->acm.call;
+				callid = func_ss7_linkset_callid(linkset, chanpos);
+				sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_PROCEEDING);
+				if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) {
+					p->call_level = SIG_SS7_CALL_LEVEL_PROCEEDING;
+				}
+				sig_ss7_set_dialing(p, 0);
+				/* Send alerting if subscriber is free */
+				if (e->acm.called_party_status_ind == 1) {
+					if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING) {
+						p->call_level = SIG_SS7_CALL_LEVEL_ALERTING;
 					}
-					sig_ss7_set_dialing(p, 0);
-					/* Send alerting if subscriber is free */
-					if (e->acm.called_party_status_ind == 1) {
-						if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING) {
-							p->call_level = SIG_SS7_CALL_LEVEL_ALERTING;
-						}
-						sig_ss7_lock_owner(linkset, chanpos);
-						if (p->owner) {
-							ast_setstate(p->owner, AST_STATE_RINGING);
-							ast_channel_unlock(p->owner);
-						}
-						sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING);
+					sig_ss7_lock_owner(linkset, chanpos);
+					if (p->owner) {
+						ast_setstate(p->owner, AST_STATE_RINGING);
+						ast_channel_unlock(p->owner);
 					}
-					sig_ss7_unlock_private(p);
+					sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING);
 				}
+				p->echocontrol_ind = e->acm.echocontrol_ind;
+				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_CGB:
 				chanpos = ss7_find_cic_gripe(linkset, e->cgb.startcic, e->cgb.opc, "CGB");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->cgb.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
-				ss7_block_cics(linkset, e->cgb.startcic, e->cgb.endcic, e->cgb.opc, e->cgb.status, 1);
-				isup_cgba(linkset->ss7, e->cgb.startcic, e->cgb.endcic, e->cgb.opc, e->cgb.status, e->cgb.type);
+				ss7_check_range(linkset, e->cgb.startcic, e->cgb.endcic,
+					e->cgb.opc, e->cgb.status);
+				ss7_block_cics(linkset, e->cgb.startcic, e->cgb.endcic,
+					e->cgb.opc, e->cgb.status, 1, 1,
+					(e->cgb.type) ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE);
+
+				sig_ss7_lock_private(p);
+				p->ss7call = e->cgb.call;
+
+				isup_cgba(linkset->ss7, p->ss7call, e->cgb.endcic, e->cgb.status);
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, e->cgb.call);
+				}
+				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_CGU:
 				chanpos = ss7_find_cic_gripe(linkset, e->cgu.startcic, e->cgu.opc, "CGU");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->cgu.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
-				ss7_block_cics(linkset, e->cgu.startcic, e->cgu.endcic, e->cgu.opc, e->cgu.status, 0);
-				isup_cgua(linkset->ss7, e->cgu.startcic, e->cgu.endcic, e->cgu.opc, e->cgu.status, e->cgu.type);
+				ss7_check_range(linkset, e->cgu.startcic, e->cgu.endcic,
+					e->cgu.opc, e->cgu.status);
+				ss7_block_cics(linkset, e->cgu.startcic, e->cgu.endcic,
+					e->cgu.opc, e->cgu.status, 0, 1,
+					e->cgu.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE);
+
+				sig_ss7_lock_private(p);
+				p->ss7call = e->cgu.call;
+
+				isup_cgua(linkset->ss7, p->ss7call, e->cgu.endcic, e->cgu.status);
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, e->cgu.call);
+				}
+				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_UCIC:
 				chanpos = ss7_find_cic_gripe(linkset, e->ucic.cic, e->ucic.opc, "UCIC");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->ucic.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
 				ast_debug(1, "Unequiped Circuit Id Code on CIC %d\n", e->ucic.cic);
 				sig_ss7_lock_private(p);
-				sig_ss7_set_remotelyblocked(p, 1);
+				sig_ss7_lock_owner(linkset, chanpos);
+				if (p->owner) {
+					ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+					ast_channel_unlock(p->owner);
+				}
+				sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_MAINTENANCE);
 				sig_ss7_set_inservice(p, 0);
+				p->ss7call = NULL;
+				isup_free_call(ss7, e->ucic.call);
 				sig_ss7_unlock_private(p);/* doesn't require a SS7 acknowledgement */
 				break;
 			case ISUP_EVENT_BLO:
 				chanpos = ss7_find_cic_gripe(linkset, e->blo.cic, e->blo.opc, "BLO");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->blo.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
 				ast_debug(1, "Blocking CIC %d\n", e->blo.cic);
 				sig_ss7_lock_private(p);
-				sig_ss7_set_remotelyblocked(p, 1);
+				p->ss7call = e->blo.call;
+				sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_MAINTENANCE);
+				isup_bla(linkset->ss7, e->blo.call);
+				sig_ss7_lock_owner(linkset, chanpos);
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, e->blo.call);
+				} else {
+					if (e->blo.got_sent_msg & ISUP_SENT_IAM) {
+						/* Q.784 6.2.2 */
+						ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN);
+					}
+					ast_channel_unlock(p->owner);
+				}
 				sig_ss7_unlock_private(p);
-				isup_bla(linkset->ss7, e->blo.cic, p->dpc);
 				break;
 			case ISUP_EVENT_BLA:
 				chanpos = ss7_find_cic_gripe(linkset, e->bla.cic, e->bla.opc, "BLA");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->bla.call);
 					break;
 				}
-				ast_debug(1, "Blocking CIC %d\n", e->bla.cic);
+				ast_debug(1, "Locally blocking CIC %d\n", e->bla.cic);
 				p = linkset->pvts[chanpos];
 				sig_ss7_lock_private(p);
-				sig_ss7_set_locallyblocked(p, 1);
+				p->ss7call = e->bla.call;
+				sig_ss7_set_locallyblocked(p, 1, SS7_BLOCKED_MAINTENANCE);
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+				}
 				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_UBL:
 				chanpos = ss7_find_cic_gripe(linkset, e->ubl.cic, e->ubl.opc, "UBL");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->ubl.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
-				ast_debug(1, "Unblocking CIC %d\n", e->ubl.cic);
+				ast_debug(1, "Remotely unblocking CIC %d PC %d\n", e->ubl.cic, e->ubl.opc);
 				sig_ss7_lock_private(p);
-				sig_ss7_set_remotelyblocked(p, 0);
+				p->ss7call = e->ubl.call;
+				sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE);
+				isup_uba(linkset->ss7, e->ubl.call);
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+				}
 				sig_ss7_unlock_private(p);
-				isup_uba(linkset->ss7, e->ubl.cic, p->dpc);
 				break;
 			case ISUP_EVENT_UBA:
 				chanpos = ss7_find_cic_gripe(linkset, e->uba.cic, e->uba.opc, "UBA");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->uba.call);
 					break;
 				}
 				p = linkset->pvts[chanpos];
-				ast_debug(1, "Unblocking CIC %d\n", e->uba.cic);
+				ast_debug(1, "Locally unblocking CIC %d PC %d\n", e->uba.cic, e->uba.opc);
 				sig_ss7_lock_private(p);
-				sig_ss7_set_locallyblocked(p, 0);
+				p->ss7call = e->uba.call;
+				sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_MAINTENANCE);
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+				}
 				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_CON:
@@ -1299,49 +2159,92 @@ void *ss7_linkset(void *data)
 				if (e->e == ISUP_EVENT_CON) {
 					chanpos = ss7_find_cic_gripe(linkset, e->con.cic, e->con.opc, "CON");
 					if (chanpos < 0) {
-						isup_rel(ss7, e->con.call, -1);
+						isup_free_call(ss7, e->con.call);
 						break;
 					}
 				} else {
 					chanpos = ss7_find_cic_gripe(linkset, e->anm.cic, e->anm.opc, "ANM");
 					if (chanpos < 0) {
-						isup_rel(ss7, e->anm.call, -1);
+						isup_free_call(ss7, e->anm.call);
 						break;
 					}
 				}
 
-				{
-					p = linkset->pvts[chanpos];
-					sig_ss7_lock_private(p);
-					callid = func_ss7_linkset_callid(linkset, chanpos);
-					if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) {
-						p->call_level = SIG_SS7_CALL_LEVEL_CONNECT;
-					}
-					sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_ANSWER);
-					sig_ss7_set_dialing(p, 0);
-					sig_ss7_open_media(p);
-					sig_ss7_set_echocanceller(p, 1);
-					sig_ss7_unlock_private(p);
-				}
-				break;
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+				p->ss7call = (e->e == ISUP_EVENT_ANM) ?  e->anm.call : e->con.call;
+				callid = func_ss7_linkset_callid(linkset, chanpos);
+				if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) {
+					p->call_level = SIG_SS7_CALL_LEVEL_CONNECT;
+				}
+
+				if (!ast_strlen_zero((e->e == ISUP_EVENT_ANM)
+					? e->anm.connected_num : e->con.connected_num)) {
+					sig_ss7_lock_owner(linkset, chanpos);
+					if (p->owner) {
+						struct ast_party_connected_line ast_connected;
+						char connected_num[AST_MAX_EXTENSION];
+
+						ast_party_connected_line_init(&ast_connected);
+						if (e->e == ISUP_EVENT_ANM) {
+							ast_connected.id.number.presentation = ss7_pres_scr2cid_pres(
+								e->anm.connected_presentation_ind,
+								e->anm.connected_screening_ind);
+							ss7_apply_plan_to_number(connected_num, sizeof(connected_num),
+								linkset, e->anm.connected_num, e->anm.connected_nai);
+							ast_connected.id.number.str = ast_strdup(connected_num);
+						} else {
+							ast_connected.id.number.presentation = ss7_pres_scr2cid_pres(
+								e->con.connected_presentation_ind,
+								e->con.connected_screening_ind);
+							ss7_apply_plan_to_number(connected_num, sizeof(connected_num),
+								linkset, e->con.connected_num, e->con.connected_nai);
+							ast_connected.id.number.str = ast_strdup(connected_num);
+						}
+						ast_connected.id.number.valid = 1;
+						ast_connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+						ast_channel_queue_connected_line_update(p->owner, &ast_connected, NULL);
+						ast_party_connected_line_free(&ast_connected);
+						ast_channel_unlock(p->owner);
+					}
+				}
+
+				sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_ANSWER);
+				sig_ss7_set_dialing(p, 0);
+				sig_ss7_open_media(p);
+				if (((e->e == ISUP_EVENT_ANM) ? !e->anm.echocontrol_ind  :
+						!e->con.echocontrol_ind) || !(linkset->flags & LINKSET_FLAG_USEECHOCONTROL)) {
+					sig_ss7_set_echocanceller(p, 1);
+				}
+				sig_ss7_unlock_private(p);
+				break;
 			case ISUP_EVENT_RLC:
-				/* XXX Call ptr should be passed up from libss7! */
 				chanpos = ss7_find_cic_gripe(linkset, e->rlc.cic, e->rlc.opc, "RLC");
 				if (chanpos < 0) {
+					isup_free_call(ss7, e->rlc.call);
 					break;
 				}
-				{
-					p = linkset->pvts[chanpos];
-					sig_ss7_lock_private(p);
-					callid = func_ss7_linkset_callid(linkset, chanpos);
-					if (p->alreadyhungup) {
-						if (!p->owner) {
-							p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
-						}
-						p->ss7call = NULL;
+
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+				p->ss7call = e->rlc.call;
+				callid = func_ss7_linkset_callid(linkset, chanpos);
+				if (e->rlc.got_sent_msg & (ISUP_SENT_RSC | ISUP_SENT_REL)) {
+					sig_ss7_loopback(p, 0);
+					if (e->rlc.got_sent_msg & ISUP_SENT_RSC) {
+						sig_ss7_set_inservice(p, 1);
 					}
-					sig_ss7_unlock_private(p);
 				}
+				sig_ss7_lock_owner(linkset, chanpos);
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, e->rlc.call);
+					p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
+				} else {
+					p->do_hangup = SS7_HANGUP_DO_NOTHING;
+					ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+					ast_channel_unlock(p->owner);
+				}
+				sig_ss7_unlock_private(p);
 				break;
 			case ISUP_EVENT_FAA:
 				/*!
@@ -1350,25 +2253,87 @@ void *ss7_linkset(void *data)
 				 */
 				chanpos = ss7_find_cic_gripe(linkset, e->faa.cic, e->faa.opc, "FAA");
 				if (chanpos < 0) {
-					isup_rel(linkset->ss7, e->faa.call, -1);
+					isup_free_call(ss7, e->faa.call);
 					break;
 				}
-				{
-					/* XXX FAR and FAA used for something dealing with transfers? */
-					p = linkset->pvts[chanpos];
-					ast_debug(1, "FAA received on CIC %d\n", e->faa.cic);
-					sig_ss7_lock_private(p);
-					callid = func_ss7_linkset_callid(linkset, chanpos);
-					if (p->alreadyhungup){
-						if (!p->owner) {
-							p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
-						}
-						/* XXX We seem to be leaking the isup call structure here. */
-						p->ss7call = NULL;
-						ast_log(LOG_NOTICE, "Received FAA and we haven't sent FAR.  Ignoring.\n");
-					}
-					sig_ss7_unlock_private(p);
+
+				/* XXX FAR and FAA used for something dealing with transfers? */
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+				callid = func_ss7_linkset_callid(linkset, chanpos);
+				ast_debug(1, "FAA received on CIC %d\n", e->faa.cic);
+				p->ss7call = isup_free_call_if_clear(ss7, e->faa.call);
+				sig_ss7_unlock_private(p);
+				break;
+			case ISUP_EVENT_CGBA:
+				chanpos = ss7_find_cic_gripe(linkset, e->cgba.startcic, e->cgba.opc, "CGBA");
+				if (chanpos < 0) {	/* Never will be true */
+					isup_free_call(ss7, e->cgba.call);
+					break;
+				}
+
+				ss7_block_cics(linkset, e->cgba.startcic, e->cgba.endcic,
+					e->cgba.opc, e->cgba.status, 1, 0,
+					e->cgba.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE);
+
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+				p->ss7call = e->cgba.call;
+
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+				}
+				sig_ss7_unlock_private(p);
+				break;
+			case ISUP_EVENT_CGUA:
+				chanpos = ss7_find_cic_gripe(linkset, e->cgua.startcic, e->cgua.opc, "CGUA");
+				if (chanpos < 0) { /* Never will be true */
+					isup_free_call(ss7, e->cgua.call);
+					break;
+				}
+
+				ss7_block_cics(linkset, e->cgua.startcic, e->cgua.endcic,
+					e->cgua.opc, e->cgua.status, 0, 0,
+					e->cgba.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE);
+
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+				p->ss7call = e->cgua.call;
+
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+				}
+				sig_ss7_unlock_private(p);
+				break;
+			case ISUP_EVENT_SUS:
+				chanpos = ss7_find_cic_gripe(linkset, e->sus.cic, e->sus.opc, "SUS");
+				if (chanpos < 0) {
+					isup_free_call(ss7, e->sus.call);
+					break;
+				}
+
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+				p->ss7call = e->sus.call;
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+				}
+				sig_ss7_unlock_private(p);
+				break;
+			case ISUP_EVENT_RES:
+				chanpos = ss7_find_cic_gripe(linkset, e->res.cic, e->res.opc, "RES");
+				if (chanpos < 0) {
+					isup_free_call(ss7, e->res.call);
+					break;
+				}
+
+				p = linkset->pvts[chanpos];
+				sig_ss7_lock_private(p);
+				p->ss7call = e->res.call;
+				if (!p->owner) {
+					p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
 				}
+				sig_ss7_unlock_private(p);
 				break;
 			default:
 				ast_debug(1, "Unknown event %s\n", ss7_event2str(e->e));
@@ -1387,9 +2352,15 @@ void *ss7_linkset(void *data)
 	return 0;
 }
 
-static inline void ss7_rel(struct sig_ss7_linkset *ss7)
+static void ss7_rel(struct sig_ss7_linkset *ss7)
 {
+	/* Release the lock first */
 	ast_mutex_unlock(&ss7->lock);
+
+	/* Then break the poll to send our messages */
+	if (ss7->master != AST_PTHREADT_NULL) {
+		pthread_kill(ss7->master, SIGURG);
+	}
 }
 
 static void ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7)
@@ -1399,10 +2370,177 @@ static void ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7)
 		/* Avoid deadlock */
 		sig_ss7_deadlock_avoidance_private(pvt);
 	}
-	/* Then break the poll */
-	if (ss7->master != AST_PTHREADT_NULL) {
-		pthread_kill(ss7->master, SIGURG);
+}
+
+/*!
+ * \brief Reset a specific CIC.
+ * \since 11.0
+ *
+ * \param linkset linkset control structure.
+ * \param cic Circuit Identification Code
+ * \param dpc Destination Point Code
+ *
+ * \return TRUE on success
+ */
+int sig_ss7_reset_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc)
+{
+	int i;
+
+	ast_mutex_lock(&linkset->lock);
+	for (i = 0; i < linkset->numchans; i++) {
+		if (linkset->pvts[i] && linkset->pvts[i]->cic == cic && linkset->pvts[i]->dpc == dpc) {
+			int res;
+
+			sig_ss7_lock_private(linkset->pvts[i]);
+			sig_ss7_set_locallyblocked(linkset->pvts[i], 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+			res = ss7_start_rsc(linkset, i);
+			sig_ss7_unlock_private(linkset->pvts[i]);
+			ss7_rel(linkset);	/* Also breaks the poll to send our messages */
+			return res;
+		}
+	}
+	ss7_rel(linkset);
+
+	return 0;
+}
+
+/*!
+ * \brief Block or Unblock a specific CIC.
+ * \since 11.0
+ *
+ * \param linkset linkset control structure.
+ * \param do_block Action to perform. Block if TRUE.
+ * \param which On which CIC to perform the operation.
+ *
+ * \return 0 on success
+ */
+int sig_ss7_cic_blocking(struct sig_ss7_linkset *linkset, int do_block, int which)
+{
+	ast_mutex_lock(&linkset->lock);
+	sig_ss7_lock_private(linkset->pvts[which]);
+	if (!ss7_find_alloc_call(linkset->pvts[which])) {
+		sig_ss7_unlock_private(linkset->pvts[which]);
+		ss7_rel(linkset);
+		return -1;
+	}
+
+	if (do_block) {
+		isup_blo(linkset->ss7, linkset->pvts[which]->ss7call);
+	} else {
+		isup_ubl(linkset->ss7, linkset->pvts[which]->ss7call);
+	}
+
+	sig_ss7_unlock_private(linkset->pvts[which]);
+	ss7_rel(linkset);	/* Also breaks the poll to send our messages */
+
+	return 0;
+}
+
+/*!
+ * \brief Block or Unblock a range of CICs.
+ * \since 11.0
+ *
+ * \param linkset linkset control structure.
+ * \param do_block Action to perform. Block if TRUE.
+ * \param chanpos Channel position to start from.
+ * \param endcic Circuit Identification Code of the end of the range.
+ * \param state Array of CIC blocking status.
+ * \param type Type of the blocking - maintenance or hardware
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ *
+ * \return 0 on success
+ */
+int sig_ss7_group_blocking(struct sig_ss7_linkset *linkset, int do_block, int chanpos, int endcic, unsigned char state[], int type)
+{
+	sig_ss7_lock_private(linkset->pvts[chanpos]);
+	if (!ss7_find_alloc_call(linkset->pvts[chanpos])) {
+		sig_ss7_unlock_private(linkset->pvts[chanpos]);
+		return -1;
+	}
+
+	if (do_block) {
+		isup_cgb(linkset->ss7, linkset->pvts[chanpos]->ss7call, endcic, state, type);
+	} else {
+		isup_cgu(linkset->ss7, linkset->pvts[chanpos]->ss7call, endcic, state, type);
+	}
+
+	sig_ss7_unlock_private(linkset->pvts[chanpos]);
+	return 0;
+}
+
+/*!
+ * \brief Reset a group of CICs.
+ * \since 11.0
+ *
+ * \param linkset linkset control structure.
+ * \param cic Circuit Identification Code
+ * \param dpc Destination Point Code
+ * \param range Range of the CICs to reset
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ *
+ * \return 0 on success
+ */
+int sig_ss7_reset_group(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc, int range)
+{
+	int i;
+
+	for (i = 0; i < linkset->numchans; i++) {
+		if (linkset->pvts[i] && linkset->pvts[i]->cic == cic && linkset->pvts[i]->dpc == dpc) {
+			ss7_clear_channels(linkset, cic, cic + range, dpc, SS7_HANGUP_FREE_CALL);
+			ss7_block_cics(linkset, cic, cic + range, dpc, NULL, 0, 1,
+				SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+			ss7_block_cics(linkset, cic, cic + range, dpc, NULL, 0, 0,
+				SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+
+			sig_ss7_lock_private(linkset->pvts[i]);
+			if (!ss7_find_alloc_call(linkset->pvts[i])) {
+				sig_ss7_unlock_private(linkset->pvts[i]);
+				return -1;
+			}
+			isup_grs(linkset->ss7, linkset->pvts[i]->ss7call, linkset->pvts[i]->cic + range);
+			sig_ss7_unlock_private(linkset->pvts[i]);
+			break;
+		}
 	}
+	return 0;
+}
+
+void sig_ss7_free_isup_call(struct sig_ss7_linkset *linkset, int channel)
+{
+	sig_ss7_lock_private(linkset->pvts[channel]);
+	if (linkset->pvts[channel]->ss7call) {
+		isup_free_call(linkset->ss7, linkset->pvts[channel]->ss7call);
+		linkset->pvts[channel]->ss7call = NULL;
+	}
+	sig_ss7_unlock_private(linkset->pvts[channel]);
+}
+
+static int ss7_parse_prefix(struct sig_ss7_chan *p, const char *number, char *nai)
+{
+	int strip = 0;
+
+	if (strncmp(number, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) {
+		strip = strlen(p->ss7->internationalprefix);
+		*nai = SS7_NAI_INTERNATIONAL;
+	} else if (strncmp(number, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) {
+		strip = strlen(p->ss7->nationalprefix);
+		*nai = SS7_NAI_NATIONAL;
+	} else if (strncmp(number, p->ss7->networkroutedprefix, strlen(p->ss7->networkroutedprefix)) == 0) {
+		strip = strlen(p->ss7->networkroutedprefix);
+		*nai = SS7_NAI_NETWORKROUTED;
+	} else if (strncmp(number, p->ss7->unknownprefix, strlen(p->ss7->unknownprefix)) == 0) {
+		strip = strlen(p->ss7->unknownprefix);
+		*nai = SS7_NAI_UNKNOWN;
+	} else if (strncmp(number, p->ss7->subscriberprefix, strlen(p->ss7->subscriberprefix)) == 0) {
+		strip = strlen(p->ss7->subscriberprefix);
+		*nai = SS7_NAI_SUBSCRIBER;
+	} else {
+		*nai = SS7_NAI_SUBSCRIBER;
+	}
+
+	return strip;
 }
 
 /*!
@@ -1453,7 +2591,7 @@ void sig_ss7_link_noalarm(struct sig_ss7_linkset *linkset, int which)
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode)
+int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode, int cur_slc)
 {
 	if (!linkset->ss7) {
 		linkset->type = ss7type;
@@ -1467,7 +2605,7 @@ int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type,
 	ss7_set_network_ind(linkset->ss7, networkindicator);
 	ss7_set_pc(linkset->ss7, pointcode);
 
-	if (ss7_add_link(linkset->ss7, transport, linkset->fds[which])) {
+	if (ss7_add_link(linkset->ss7, transport, linkset->fds[which], cur_slc, adjpointcode)) {
 		ast_log(LOG_WARNING, "Could not add SS7 link!\n");
 	}
 
@@ -1479,8 +2617,6 @@ int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type,
 		ss7_link_noalarm(linkset->ss7, linkset->fds[which]);
 	}
 
-	ss7_set_adjpc(linkset->ss7, linkset->fds[which], adjpointcode);
-
 	return 0;
 }
 
@@ -1505,7 +2641,13 @@ int sig_ss7_available(struct sig_ss7_chan *p)
 	ast_mutex_lock(&p->ss7->lock);
 	available = sig_ss7_is_chan_available(p);
 	if (available) {
-		p->call_level = SIG_SS7_CALL_LEVEL_ALLOCATED;
+		p->ss7call = isup_new_call(p->ss7->ss7, p->cic, p->dpc, 1);
+		if (!p->ss7call) {
+			ast_log(LOG_ERROR, "Unable to allocate new SS7 call!\n");
+			available = 0;
+		} else {
+			p->call_level = SIG_SS7_CALL_LEVEL_ALLOCATED;
+		}
 	}
 	ast_mutex_unlock(&p->ss7->lock);
 
@@ -1522,6 +2664,159 @@ static unsigned char cid_pres2ss7screen(int cid_pres)
 	return cid_pres & 0x03;
 }
 
+static void ss7_connected_line_update(struct sig_ss7_chan *p, struct ast_party_connected_line *connected)
+{
+	int connected_strip = 0;
+	char connected_nai;
+	unsigned char connected_pres;
+	unsigned char connected_screen;
+	const char *connected_num;
+
+	if (!connected->id.number.valid) {
+		return;
+	}
+
+	connected_num = S_OR(connected->id.number.str, "");
+	if (p->ss7->called_nai ==  SS7_NAI_DYNAMIC) {
+		connected_strip = ss7_parse_prefix(p, connected_num, &connected_nai);
+	} else {
+		connected_nai = p->ss7->called_nai;
+	}
+
+	connected_pres = cid_pres2ss7pres(connected->id.number.presentation);
+	connected_screen = cid_pres2ss7screen(connected->id.number.presentation);
+
+	isup_set_connected(p->ss7call, connected_num + connected_strip, connected_nai, connected_pres, connected_screen);
+}
+
+static unsigned char ss7_redirect_reason(struct sig_ss7_chan *p, struct ast_party_redirecting *redirecting, int orig)
+{
+	int reason = (orig) ? redirecting->orig_reason.code : redirecting->reason.code;
+
+	switch (reason) {
+	case AST_REDIRECTING_REASON_USER_BUSY:
+		return SS7_REDIRECTING_REASON_USER_BUSY;
+	case AST_REDIRECTING_REASON_NO_ANSWER:
+		return SS7_REDIRECTING_REASON_NO_ANSWER;
+	case AST_REDIRECTING_REASON_UNCONDITIONAL:
+		return SS7_REDIRECTING_REASON_UNCONDITIONAL;
+	}
+
+	if (orig || reason == AST_REDIRECTING_REASON_UNKNOWN) {
+		return SS7_REDIRECTING_REASON_UNKNOWN;
+	}
+
+	if (reason == AST_REDIRECTING_REASON_UNAVAILABLE) {
+		return SS7_REDIRECTING_REASON_UNAVAILABLE;
+	}
+
+	if (reason == AST_REDIRECTING_REASON_DEFLECTION) {
+		if (p->call_level > SIG_SS7_CALL_LEVEL_PROCEEDING) {
+			return SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING;
+		}
+		return SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE;
+	}
+
+	return SS7_REDIRECTING_REASON_UNKNOWN;
+}
+
+static unsigned char ss7_redirect_info_ind(struct ast_channel *ast)
+{
+	const char *redirect_info_ind;
+	struct ast_party_redirecting *redirecting = ast_channel_redirecting(ast);
+
+	redirect_info_ind = pbx_builtin_getvar_helper(ast, "SS7_REDIRECT_INFO_IND");
+	if (!ast_strlen_zero(redirect_info_ind)) {
+		if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_PRES_ALLOWED")) {
+			return SS7_INDICATION_REROUTED_PRES_ALLOWED;
+		}
+		if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_INFO_RESTRICTED")) {
+			return SS7_INDICATION_REROUTED_INFO_RESTRICTED;
+		}
+		if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_PRES_ALLOWED")) {
+			return SS7_INDICATION_DIVERTED_PRES_ALLOWED;
+		}
+		if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_INFO_RESTRICTED")) {
+			return SS7_INDICATION_DIVERTED_INFO_RESTRICTED;
+		}
+		if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_PRES_RESTRICTED")) {
+			return SS7_INDICATION_REROUTED_PRES_RESTRICTED;
+		}
+		if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_PRES_RESTRICTED")) {
+			return SS7_INDICATION_DIVERTED_PRES_RESTRICTED;
+		}
+		if (!strcasecmp(redirect_info_ind, "SPARE")) {
+			return SS7_INDICATION_SPARE;
+		}
+		return SS7_INDICATION_NO_REDIRECTION;
+	}
+
+	if (redirecting->reason.code == AST_REDIRECTING_REASON_DEFLECTION) {
+		if ((redirecting->to.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+			if ((redirecting->orig.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+				return SS7_INDICATION_DIVERTED_PRES_ALLOWED;
+			}
+			return SS7_INDICATION_DIVERTED_PRES_RESTRICTED;
+		}
+		return SS7_INDICATION_DIVERTED_INFO_RESTRICTED;
+	}
+
+	if ((redirecting->to.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+		if ((redirecting->orig.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+			return SS7_INDICATION_REROUTED_PRES_ALLOWED;
+		}
+		return SS7_INDICATION_REROUTED_PRES_RESTRICTED;
+	}
+	return SS7_INDICATION_REROUTED_INFO_RESTRICTED;
+}
+
+static void ss7_redirecting_update(struct sig_ss7_chan *p, struct ast_channel *ast)
+{
+	int num_nai_strip = 0;
+	struct ast_party_redirecting *redirecting = ast_channel_redirecting(ast);
+
+	if (!redirecting->count) {
+		return;
+	}
+
+	isup_set_redirect_counter(p->ss7call, redirecting->count);
+
+	if (redirecting->orig.number.valid) {
+		char ss7_orig_called_nai = p->ss7->called_nai;
+		const char *ss7_orig_called_num = S_OR(redirecting->orig.number.str, "");
+
+		if (ss7_orig_called_nai == SS7_NAI_DYNAMIC) {
+			num_nai_strip = ss7_parse_prefix(p, ss7_orig_called_num, &ss7_orig_called_nai);
+		} else {
+			num_nai_strip = 0;
+		}
+		isup_set_orig_called_num(p->ss7call, ss7_orig_called_num + num_nai_strip,
+			ss7_orig_called_nai,
+			cid_pres2ss7pres(redirecting->orig.number.presentation),
+			cid_pres2ss7screen(redirecting->orig.number.presentation));
+	}
+
+	if (redirecting->from.number.valid) {
+		char ss7_redirecting_num_nai = p->ss7->calling_nai;
+		const char *redirecting_number = S_OR(redirecting->from.number.str, "");
+
+		if (ss7_redirecting_num_nai == SS7_NAI_DYNAMIC) {
+			num_nai_strip = ss7_parse_prefix(p, redirecting_number, &ss7_redirecting_num_nai);
+		} else {
+			num_nai_strip = 0;
+		}
+
+		isup_set_redirecting_number(p->ss7call, redirecting_number + num_nai_strip,
+			ss7_redirecting_num_nai,
+			cid_pres2ss7pres(redirecting->from.number.presentation),
+			cid_pres2ss7screen(redirecting->from.number.presentation));
+	}
+
+	isup_set_redirection_info(p->ss7call, ss7_redirect_info_ind(ast),
+		ss7_redirect_reason(p, ast_channel_redirecting(ast), 1),
+		redirecting->count, ss7_redirect_reason(p, ast_channel_redirecting(ast), 0));
+}
+
 /*!
  * \brief Dial out using the specified SS7 channel.
  * \since 1.8
@@ -1539,6 +2834,13 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
 	int called_nai_strip;
 	char ss7_calling_nai;
 	int calling_nai_strip;
+	const char *col_req = NULL;
+	const char *ss7_cug_indicator_str;
+	const char *ss7_cug_interlock_ni;
+	const char *ss7_cug_interlock_code;
+	const char *ss7_interworking_indicator;
+	const char *ss7_forward_indicator_pmbits;
+	unsigned char ss7_cug_indicator;
 	const char *charge_str = NULL;
 	const char *gen_address = NULL;
 	const char *gen_digits = NULL;
@@ -1551,6 +2853,7 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
 	const char *call_ref_id = NULL;
 	const char *call_ref_pc = NULL;
 	const char *send_far = NULL;
+	const char *tmr = NULL;
 	char *c;
 	char *l;
 	char dest[256];
@@ -1582,47 +2885,27 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
 		return -1;
 	}
 
-	p->ss7call = isup_new_call(p->ss7->ss7);
-	if (!p->ss7call) {
-		ss7_rel(p->ss7);
-		ast_log(LOG_ERROR, "Unable to allocate new SS7 call!\n");
-		return -1;
-	}
-
 	called_nai_strip = 0;
 	ss7_called_nai = p->ss7->called_nai;
 	if (ss7_called_nai == SS7_NAI_DYNAMIC) { /* compute dynamically */
-		if (strncmp(c + p->stripmsd, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) {
-			called_nai_strip = strlen(p->ss7->internationalprefix);
-			ss7_called_nai = SS7_NAI_INTERNATIONAL;
-		} else if (strncmp(c + p->stripmsd, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) {
-			called_nai_strip = strlen(p->ss7->nationalprefix);
-			ss7_called_nai = SS7_NAI_NATIONAL;
-		} else {
-			ss7_called_nai = SS7_NAI_SUBSCRIBER;
-		}
+		called_nai_strip = ss7_parse_prefix(p, c + p->stripmsd, &ss7_called_nai);
 	}
 	isup_set_called(p->ss7call, c + p->stripmsd + called_nai_strip, ss7_called_nai, p->ss7->ss7);
 
 	calling_nai_strip = 0;
 	ss7_calling_nai = p->ss7->calling_nai;
 	if ((l != NULL) && (ss7_calling_nai == SS7_NAI_DYNAMIC)) { /* compute dynamically */
-		if (strncmp(l, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) {
-			calling_nai_strip = strlen(p->ss7->internationalprefix);
-			ss7_calling_nai = SS7_NAI_INTERNATIONAL;
-		} else if (strncmp(l, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) {
-			calling_nai_strip = strlen(p->ss7->nationalprefix);
-			ss7_calling_nai = SS7_NAI_NATIONAL;
-		} else {
-			ss7_calling_nai = SS7_NAI_SUBSCRIBER;
-		}
+		calling_nai_strip = ss7_parse_prefix(p, l, &ss7_calling_nai);
 	}
+
 	isup_set_calling(p->ss7call, l ? (l + calling_nai_strip) : NULL, ss7_calling_nai,
-		p->use_callingpres ? cid_pres2ss7pres(ast_channel_connected(ast)->id.number.presentation) : (l ? SS7_PRESENTATION_ALLOWED : SS7_PRESENTATION_RESTRICTED),
+		p->use_callingpres ? cid_pres2ss7pres(ast_channel_connected(ast)->id.number.presentation)
+			: (l ? SS7_PRESENTATION_ALLOWED
+				: (ast_channel_connected(ast)->id.number.presentation == AST_PRES_UNAVAILABLE
+					? SS7_PRESENTATION_ADDR_NOT_AVAILABLE : SS7_PRESENTATION_RESTRICTED)),
 		p->use_callingpres ? cid_pres2ss7screen(ast_channel_connected(ast)->id.number.presentation) : SS7_SCREENING_USER_PROVIDED);
 
 	isup_set_oli(p->ss7call, ast_channel_connected(ast)->ani2);
-	isup_init_call(p->ss7->ss7, p->ss7call, p->cic, p->dpc);
 
 	/* Set the charge number if it is set */
 	charge_str = pbx_builtin_getvar_helper(ast, "SS7_CHARGE_NUMBER");
@@ -1664,10 +2947,66 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
 	}
 
 	send_far = pbx_builtin_getvar_helper(ast, "SS7_SEND_FAR");
-	if ((send_far) && ((strncmp("NO", send_far, strlen(send_far))) != 0 ))
-		(isup_far(p->ss7->ss7, p->ss7call));
+	if (send_far && strncmp("NO", send_far, strlen(send_far)) != 0) {
+		isup_far(p->ss7->ss7, p->ss7call);
+	}
+
+	tmr = pbx_builtin_getvar_helper(ast, "SS7_TMR_NUM");
+	if (tmr) {
+		isup_set_tmr(p->ss7call, atoi(tmr));
+	} else if ((tmr = pbx_builtin_getvar_helper(ast, "SS7_TMR")) && tmr[0] != '\0') {
+		if (!strcasecmp(tmr, "SPEECH")) {
+			isup_set_tmr(p->ss7call, SS7_TMR_SPEECH);
+		} else if (!strcasecmp(tmr, "SPARE")) {
+			isup_set_tmr(p->ss7call, SS7_TMR_SPARE);
+		} else if (!strcasecmp(tmr, "3K1_AUDIO")) {
+			isup_set_tmr(p->ss7call, SS7_TMR_3K1_AUDIO);
+		} else if (!strcasecmp(tmr, "64K_UNRESTRICTED")) {
+			isup_set_tmr(p->ss7call, SS7_TMR_64K_UNRESTRICTED);
+		} else {
+			isup_set_tmr(p->ss7call, SS7_TMR_N64K_OR_SPARE);
+		}
+	}
+
+	col_req = pbx_builtin_getvar_helper(ast, "SS7_COL_REQUEST");
+	if (ast_true(col_req)) {
+		isup_set_col_req(p->ss7call);
+	}
+
+	ss7_cug_indicator_str = pbx_builtin_getvar_helper(ast, "SS7_CUG_INDICATOR");
+	if (!ast_strlen_zero(ss7_cug_indicator_str)) {
+		if (!strcasecmp(ss7_cug_indicator_str, "OUTGOING_ALLOWED")) {
+			ss7_cug_indicator = ISUP_CUG_OUTGOING_ALLOWED;
+		} else if (!strcasecmp(ss7_cug_indicator_str, "OUTGOING_NOT_ALLOWED")) {
+			ss7_cug_indicator = ISUP_CUG_OUTGOING_NOT_ALLOWED;
+		} else {
+			ss7_cug_indicator = ISUP_CUG_NON;
+		}
+
+		if (ss7_cug_indicator != ISUP_CUG_NON) {
+			ss7_cug_interlock_code = pbx_builtin_getvar_helper(ast, "SS7_CUG_INTERLOCK_CODE");
+			ss7_cug_interlock_ni = pbx_builtin_getvar_helper(ast, "SS7_CUG_INTERLOCK_NI");
+			if (ss7_cug_interlock_code && ss7_cug_interlock_ni && strlen(ss7_cug_interlock_ni) == 4) {
+				isup_set_cug(p->ss7call, ss7_cug_indicator, ss7_cug_interlock_ni, atoi(ss7_cug_interlock_code));
+			}
+		}
+	}
+
+	ss7_redirecting_update(p, ast);
+
+	isup_set_echocontrol(p->ss7call, (p->ss7->flags & LINKSET_FLAG_DEFAULTECHOCONTROL) ? 1 : 0);
+	ss7_interworking_indicator = pbx_builtin_getvar_helper(ast, "SS7_INTERWORKING_INDICATOR");
+	if (ss7_interworking_indicator) {
+		isup_set_interworking_indicator(p->ss7call, ast_true(ss7_interworking_indicator));
+	}
+
+	ss7_forward_indicator_pmbits = pbx_builtin_getvar_helper(ast, "SS7_FORWARD_INDICATOR_PMBITS");
+	if (ss7_forward_indicator_pmbits) {
+		isup_set_forward_indicator_pmbits(p->ss7call, atoi(ss7_forward_indicator_pmbits));
+	}
 
 	p->call_level = SIG_SS7_CALL_LEVEL_SETUP;
+	p->do_hangup = SS7_HANGUP_SEND_REL;
 	isup_iam(p->ss7->ss7, p->ss7call);
 	sig_ss7_set_dialing(p, 1);
 	ast_setstate(ast, AST_STATE_DIALING);
@@ -1687,8 +3026,6 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
  */
 int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast)
 {
-	int res = 0;
-
 	if (!ast_channel_tech_pvt(ast)) {
 		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
 		return 0;
@@ -1704,22 +3041,51 @@ int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast)
 	ss7_grab(p, p->ss7);
 	p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
 	if (p->ss7call) {
-		if (!p->alreadyhungup) {
-			const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE");
-			int icause = ast_channel_hangupcause(ast) ? ast_channel_hangupcause(ast) : -1;
-
-			if (cause) {
-				if (atoi(cause)) {
-					icause = atoi(cause);
+		switch (p->do_hangup) {
+		case SS7_HANGUP_SEND_REL:
+			{
+				const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE");
+				int icause = ast_channel_hangupcause(ast) ? ast_channel_hangupcause(ast) : -1;
+
+				if (cause) {
+					if (atoi(cause)) {
+						icause = atoi(cause);
+					}
+				}
+				if (icause > 255) {
+					icause = 16;
 				}
+
+				isup_rel(p->ss7->ss7, p->ss7call, icause);
+				p->do_hangup = SS7_HANGUP_DO_NOTHING;
 			}
-			isup_rel(p->ss7->ss7, p->ss7call, icause);
-			p->alreadyhungup = 1;
+			break;
+		case SS7_HANGUP_SEND_RSC:
+			ss7_do_rsc(p);
+			p->do_hangup = SS7_HANGUP_DO_NOTHING;
+			break;
+		case SS7_HANGUP_SEND_RLC:
+			isup_rlc(p->ss7->ss7, p->ss7call);
+			p->do_hangup = SS7_HANGUP_DO_NOTHING;
+			p->ss7call = isup_free_call_if_clear(p->ss7->ss7, p->ss7call);
+			break;
+		case SS7_HANGUP_FREE_CALL:
+			p->do_hangup = SS7_HANGUP_DO_NOTHING;
+			isup_free_call(p->ss7->ss7, p->ss7call);
+			p->ss7call = NULL;
+			break;
+		case SS7_HANGUP_REEVENT_IAM:
+			isup_event_iam(p->ss7->ss7, p->ss7call, p->dpc);
+			p->do_hangup = SS7_HANGUP_SEND_REL;
+			break;
+		case SS7_HANGUP_DO_NOTHING:
+			p->ss7call = isup_free_call_if_clear(p->ss7->ss7, p->ss7call);
+			break;
 		}
 	}
 	ss7_rel(p->ss7);
 
-	return res;
+	return 0;
 }
 
 /*!
@@ -1738,10 +3104,14 @@ int sig_ss7_answer(struct sig_ss7_chan *p, struct ast_channel *ast)
 
 	ss7_grab(p, p->ss7);
 	if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) {
+		if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING && (p->ss7->flags & LINKSET_FLAG_AUTOACM)) {
+			isup_acm(p->ss7->ss7, p->ss7call);
+		}
 		p->call_level = SIG_SS7_CALL_LEVEL_CONNECT;
 	}
-	sig_ss7_open_media(p);
+
 	res = isup_anm(p->ss7->ss7, p->ss7call);
+	sig_ss7_open_media(p);
 	ss7_rel(p->ss7);
 	return res;
 }
@@ -1764,7 +3134,7 @@ void sig_ss7_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, str
 }
 
 /*!
- * \brief SS7 answer channel.
+ * \brief SS7 indication.
  * \since 1.8
  *
  * \param p Signaling private structure pointer.
@@ -1793,15 +3163,20 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi
 	case AST_CONTROL_RINGING:
 		ss7_grab(p, p->ss7);
 		if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING && !p->outgoing) {
-			p->call_level = SIG_SS7_CALL_LEVEL_ALERTING;
 			if ((isup_far(p->ss7->ss7, p->ss7call)) != -1) {
 				p->rlt = 1;
 			}
 
+			if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING && (p->ss7->flags & LINKSET_FLAG_AUTOACM)) {
+				isup_acm(p->ss7->ss7, p->ss7call);
+			}
+
 			/* No need to send CPG if call will be RELEASE */
 			if (p->rlt != 1) {
 				isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_ALERTING);
 			}
+
+			p->call_level = SIG_SS7_CALL_LEVEL_ALERTING;
 		}
 		ss7_rel(p->ss7);
 
@@ -1833,15 +3208,15 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi
 		ast_debug(1,"Received AST_CONTROL_PROGRESS on %s\n",ast_channel_name(chan));
 		ss7_grab(p, p->ss7);
 		if (!p->progress && p->call_level < SIG_SS7_CALL_LEVEL_ALERTING && !p->outgoing) {
-			p->progress = 1;/* No need to send inband-information progress again. */
+			p->progress = 1;	/* No need to send inband-information progress again. */
 			isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_INBANDINFO);
-			ss7_rel(p->ss7);
 
 			/* enable echo canceler here on SS7 calls */
-			sig_ss7_set_echocanceller(p, 1);
-		} else {
-			ss7_rel(p->ss7);
+			if (!p->echocontrol_ind || !(p->ss7->flags & LINKSET_FLAG_USEECHOCONTROL)) {
+				sig_ss7_set_echocanceller(p, 1);
+			}
 		}
+		ss7_rel(p->ss7);
 		/* don't continue in ast_indicate */
 		res = 0;
 		break;
@@ -1873,6 +3248,14 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi
 	case AST_CONTROL_SRCUPDATE:
 		res = 0;
 		break;
+	case AST_CONTROL_CONNECTED_LINE:
+		ss7_connected_line_update(p, ast_channel_connected(chan));
+		res = 0;
+		break;
+	case AST_CONTROL_REDIRECTING:
+		ss7_redirecting_update(p, chan);
+		res = 0;
+		break;
 	case -1:
 		res = sig_ss7_play_tone(p, -1);
 		break;
@@ -1914,6 +3297,7 @@ struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law
 		/* Release the allocated channel.  Only have to deal with the linkset lock. */
 		ast_mutex_lock(&p->ss7->lock);
 		p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
+		isup_free_call(p->ss7->ss7, p->ss7call);
 		ast_mutex_unlock(&p->ss7->lock);
 	}
 	return ast;
diff --git a/channels/sig_ss7.h b/channels/sig_ss7.h
index 56d393c81efd4b0d1651fd4d246363685b86a367..e2bc8e4361d25c1f8a0ed81cca2b4a5fece669e0 100644
--- a/channels/sig_ss7.h
+++ b/channels/sig_ss7.h
@@ -66,6 +66,13 @@ extern "C" {
 #define SS7_NAI_DYNAMIC		-1
 
 #define LINKSET_FLAG_EXPLICITACM (1 << 0)
+#define LINKSET_FLAG_INITIALHWBLO (1 << 1)
+#define LINKSET_FLAG_USEECHOCONTROL (1 << 2)
+#define LINKSET_FLAG_DEFAULTECHOCONTROL (1 << 3)
+#define LINKSET_FLAG_AUTOACM (1 << 4)
+
+#define SS7_BLOCKED_MAINTENANCE	(1 << 0)
+#define SS7_BLOCKED_HARDWARE	(1 << 1)
 
 
 enum sig_ss7_tone {
@@ -84,6 +91,27 @@ enum sig_ss7_law {
 	SIG_SS7_ALAW
 };
 
+enum sig_ss7_redirect_idication {
+	SS7_INDICATION_NO_REDIRECTION = 0,
+	SS7_INDICATION_REROUTED_PRES_ALLOWED,
+	SS7_INDICATION_REROUTED_INFO_RESTRICTED,
+	SS7_INDICATION_DIVERTED_PRES_ALLOWED,
+	SS7_INDICATION_DIVERTED_INFO_RESTRICTED,
+	SS7_INDICATION_REROUTED_PRES_RESTRICTED,
+	SS7_INDICATION_DIVERTED_PRES_RESTRICTED,
+	SS7_INDICATION_SPARE
+};
+
+enum sig_ss7_redirect_reason {
+	SS7_REDIRECTING_REASON_UNKNOWN = 0,
+	SS7_REDIRECTING_REASON_USER_BUSY,
+	SS7_REDIRECTING_REASON_NO_ANSWER,
+	SS7_REDIRECTING_REASON_UNCONDITIONAL,
+	SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING,
+	SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE,
+	SS7_REDIRECTING_REASON_UNAVAILABLE
+};
+
 /*! Call establishment life cycle level for simple comparisons. */
 enum sig_ss7_call_level {
 	/*! Call does not exist. */
@@ -118,8 +146,6 @@ enum sig_ss7_call_level {
 	 * We have sent or received CON/ANM.
 	 */
 	SIG_SS7_CALL_LEVEL_CONNECT,
-	/*! Call has collided with incoming call. */
-	SIG_SS7_CALL_LEVEL_GLARE,
 };
 
 struct sig_ss7_linkset;
@@ -153,6 +179,8 @@ struct sig_ss7_callback {
 
 	void (* const queue_control)(void *pvt, int subclass);
 	void (* const open_media)(void *pvt);
+
+	struct sig_ss7_linkset *(* const find_linkset)(struct ss7 *ss7);
 };
 
 /*! Global sig_ss7 callbacks to the upper layer. */
@@ -192,10 +220,19 @@ struct sig_ss7_chan {
 	unsigned int use_callingpres:1;
 	unsigned int immediate:1;		/*!< Answer before getting digits? */
 
-	/*! \brief TRUE if the channel is locally blocked.  Set by user and link. */
-	unsigned int locallyblocked:1;
-	/*! \brief TRUE if the channel is remotely blocked.  Set by user and link. */
-	unsigned int remotelyblocked:1;
+	/*!
+	 * \brief Bitmask for the channel being locally blocked.
+	 * \note 1 maintenance blocked, 2 blocked in hardware.
+	 * \note Set by user and link.
+	 */
+	unsigned int locallyblocked:2;
+
+	/*!
+	 * \brief Bitmask for the channel being remotely blocked.
+	 * \note 1 maintenance blocked, 2 blocked in hardware.
+	 * \note Set by user and link.
+	 */
+	unsigned int remotelyblocked:2;
 
 	char context[AST_MAX_CONTEXT];
 	char mohinterpret[MAX_MUSICCLASS];
@@ -215,7 +252,15 @@ struct sig_ss7_chan {
 	char gen_add_number[50];
 	char gen_dig_number[50];
 	char orig_called_num[50];
+	int orig_called_presentation;
 	char redirecting_num[50];
+	int redirecting_presentation;
+	unsigned char redirect_counter;
+	unsigned char redirect_info;
+	unsigned char redirect_info_ind;
+	unsigned char redirect_info_orig_reas;
+	unsigned char redirect_info_counter;
+	unsigned char redirect_info_reas;
 	char generic_name[50];
 	unsigned char gen_add_num_plan;
 	unsigned char gen_add_nai;
@@ -233,22 +278,41 @@ struct sig_ss7_chan {
 	unsigned int call_ref_ident;
 	unsigned int call_ref_pc;
 	unsigned char calling_party_cat;
+	unsigned int do_hangup;	/* What we have to do to clear the call */
+	unsigned int echocontrol_ind;
 
 	/*
 	 * Channel status bits.
 	 */
-	/*! TRUE if channel is associated with a link that is down. */
+	/*! \brief TRUE if channel is associated with a link that is down. */
 	unsigned int inalarm:1;
-	/*! TRUE if this channel is being used for an outgoing call. */
+	/*! \brief TRUE if channel is in service. */
+	unsigned int inservice:1;
+	/*! \brief TRUE if this channel is being used for an outgoing call. */
 	unsigned int outgoing:1;
+	/*! \brief TRUE if the channel has completed collecting digits. */
+	unsigned int called_complete:1;
 	/*! \brief TRUE if the call has seen inband-information progress through the network. */
 	unsigned int progress:1;
-	/*! \brief TRUE if the call has already gone/hungup */
-	unsigned int alreadyhungup:1;
 	/*! \brief XXX BOOLEAN Purpose??? */
 	unsigned int rlt:1;
-	/*! TRUE if this channel is in loopback. */
+	/*! \brief TRUE if this channel is in loopback. */
 	unsigned int loopedback:1;
+
+	/*
+	 * Closed User Group fields Q.735.1
+	 */
+	/*! \brief Network Identify Code as per Q.763 3.15.a */
+	char cug_interlock_ni[5];
+	/*! \brief Binari Code to uniquely identify a CUG inside the network. */
+	unsigned short cug_interlock_code;
+	/*!
+	 * \brief Indication of the call being a CUG call and its permissions.
+	 * \note 0 or 1 - non-CUG call
+	 * \note 2 - CUG call, outgoing access alowed
+	 * \note 3 - CUG call, outgoing access not alowed
+	 */
+	unsigned char cug_indicator;
 };
 
 struct sig_ss7_linkset {
@@ -276,6 +340,7 @@ struct sig_ss7_linkset {
 	char nationalprefix[10];			/*!< area access code ('0' for european dialplans) */
 	char subscriberprefix[20];			/*!< area access code + area code ('0'+area code for european dialplans) */
 	char unknownprefix[20];				/*!< for unknown dialplans */
+	char networkroutedprefix[20];
 };
 
 void sig_ss7_set_alarm(struct sig_ss7_chan *p, int in_alarm);
@@ -284,12 +349,19 @@ void *ss7_linkset(void *data);
 
 void sig_ss7_link_alarm(struct sig_ss7_linkset *linkset, int which);
 void sig_ss7_link_noalarm(struct sig_ss7_linkset *linkset, int which);
-int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode);
+int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode, int cur_slc);
+
+int sig_ss7_reset_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc);
+int sig_ss7_reset_group(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc, int range);
+int sig_ss7_cic_blocking(struct sig_ss7_linkset *linkset, int do_block, int cic);
+int sig_ss7_group_blocking(struct sig_ss7_linkset *linkset, int do_block, int startcic, int endcic, unsigned char state[], int type);
 
 int sig_ss7_available(struct sig_ss7_chan *p);
 int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rdest);
 int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast);
 int sig_ss7_answer(struct sig_ss7_chan *p, struct ast_channel *ast);
+int sig_ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc);
+int sig_ss7_find_cic_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc);
 void sig_ss7_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, struct sig_ss7_chan *pchan);
 int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condition, const void *data, size_t datalen);
 struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law,
@@ -298,10 +370,14 @@ struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law
 void sig_ss7_chan_delete(struct sig_ss7_chan *doomed);
 struct sig_ss7_chan *sig_ss7_chan_new(void *pvt_data, struct sig_ss7_linkset *ss7);
 void sig_ss7_init_linkset(struct sig_ss7_linkset *ss7);
+void sig_ss7_free_isup_call(struct sig_ss7_linkset *linkset, int channel);
 
 void sig_ss7_cli_show_channels_header(int fd);
 void sig_ss7_cli_show_channels(int fd, struct sig_ss7_linkset *linkset);
 
+int sig_ss7_cb_hangup(struct ss7 *ss7, int cic, unsigned int dpc, int cause, int do_hangup);
+void sig_ss7_cb_call_null(struct ss7 *ss7, struct isup_call *c, int lock);
+void sig_ss7_cb_notinservice(struct ss7 *ss7, int cic, unsigned int dpc);
 
 /* ------------------------------------------------------------------- */
 
diff --git a/configs/chan_dahdi.conf.sample b/configs/chan_dahdi.conf.sample
index 83701ef0013856fc3537bf4ff9addd167809845f..2c0488aec8bbd7232304bf827a39da714fe6702b 100644
--- a/configs/chan_dahdi.conf.sample
+++ b/configs/chan_dahdi.conf.sample
@@ -1371,12 +1371,35 @@ pickupgroup=1
 
 ; This option is used to disable automatic sending of ACM when the call is started
 ; in the dialplan.  If you do use this option, you will need to use the Proceeding()
-; application in the dialplan to send ACM.
-;ss7_explictacm=yes
+; application in the dialplan to send ACM or enable ss7_autoacm below.
+;ss7_explicitacm=yes
+
+; Use this option to automatically send ACM when the call rings or is answered and
+; has not seen proceeding yet. If you use this option, you should disable ss7_explicitacm.
+; You may still use Proceeding() to explicitly send an ACM from the dialplan.
+;ss7_autoacm=yes
+
+; Create the linkset with all CICs in hardware remotely blocked state.
+;ss7_initialhwblo=yes
+
+; This option is whether or not to trust the remote echo control indication.  This means
+; that in cases where echo control is reported by the remote end, we will trust them and
+; not enable echo cancellation on the call.
+;ss7_use_echocontrol=yes
+
+; This option is to set what our echo control indication is to the other end.  Set to
+; yes to indicate that we are using echo cancellation or no if we are not.
+;ss7_default_echocontrol=yes
 
 ; All settings apply to linkset 1
 ;linkset = 1
 
+; Set the Signaling Link Code (SLC) for each sigchan.
+; If you manually set any you need to manually set all.
+; Should be defined before sigchan.
+; The default SLC starts with zero and increases for each defined sigchan.
+;slc=
+
 ; Point code of the linkset.  For ITU, this is the decimal number
 ; format of the point code.  For ANSI, this can either be in decimal
 ; number format or in the xxx-xxx-xxx format
@@ -1410,6 +1433,30 @@ pickupgroup=1
 ; Channels to associate with CICs on this linkset
 ;channel = 25-47
 ;
+
+; Set this option if you wish to send an Information Request Message (INR) request
+; if no calling party number is specified. This will attempt to tell the other end
+; to send it anyways. Should be defined after sigchan.
+;inr_if_no_calling=yes
+
+; Set this to set whether or not the originating access is (non) ISDN in the forward and
+; backward call indicators. Should be defined after sigchan
+;non_isdn_access=yes
+
+; This sets the number of binary places to shift the CIC when doing load balancing between
+; sigchans on a linkset. Should be defined after sigchan. Default 0
+;sls_shift = 0
+
+; Send custom cause_location value
+; Should be defined after sigchan. Default 1 (private local)
+;cause_location=1
+
+; SS7 timers (ISUP and MTP3) should be explicitly defined for each linkset to be used.
+; For a full list of supported timers and their default values (applicable for both ITU
+; and ANSI) see ss7.timers
+; Should be defined after sigchan
+;#include ss7.timers
+
 ; For more information on setting up SS7, see the README file in libss7 or
 ; https://wiki.asterisk.org/wiki/display/AST/Signaling+System+Number+7
 ; ----------------- SS7 Options ----------------------------------------
diff --git a/configs/ss7.timers.sample b/configs/ss7.timers.sample
new file mode 100644
index 0000000000000000000000000000000000000000..9cf9bd1ab479aabd718aa2f604c67d0dff6635d5
--- /dev/null
+++ b/configs/ss7.timers.sample
@@ -0,0 +1,65 @@
+;;;;;	ITU-T Q.707 timers
+
+;mtp3_timer.q707_t1 = 4000
+;mtp3_timer.q707_t2 = 30000
+
+;;;;;	MTP3 timers as specified in ITU-T Q.704 or ANSI T1.111-2001
+
+mtp3_timer.t1 = 500
+mtp3_timer.t2 = 700
+mtp3_timer.t3 = 500
+mtp3_timer.t4 = 500
+mtp3_timer.t5 = 500
+mtp3_timer.t6 = 500
+mtp3_timer.t7 = 1000
+
+mtp3_timer.t10 = 60000
+
+mtp3_timer.t12 = 800
+mtp3_timer.t13 = 800
+mtp3_timer.t14 = 2000
+
+;	enable for ITU only. Timers after T17 are defined differently for ANSI
+;mtp3_timer.t19 = 67000
+;mtp3_timer.t21 = 63000
+;
+;mtp3_timer.t22 = 300000
+;mtp3_timer.t23 = 300000
+
+
+;;;;;	ISUP timers as specified in ITU-T Q.764 or ANSI T1.113-2000
+
+isup_timer.t1 = 15000
+;isup_timer.t2 = 180000		; ITU only
+
+;isup_timer.t5 = 300000		; for ITU
+;isup_timer.t5 = 60000		; for ANSI
+isup_timer.t6 = 30000
+isup_timer.t7 = 20000
+isup_timer.t8 = 10000
+
+;isup_timer.t10 = 4000		; ITU only
+
+isup_timer.t12 = 15000
+;isup_timer.t13 = 300000	; for ITU
+;isup_timer.t13 = 60000		; for ANSI
+isup_timer.t14 = 15000
+;isup_timer.t15 = 300000	; for ITU
+;isup_timer.t15 = 60000		; for ANSI
+isup_timer.t16 = 15000
+;isup_timer.t17 = 300000	; for ITU
+;isup_timer.t17 = 60000		; for ANSI
+isup_timer.t18 = 15000
+;isup_timer.t19 = 300000	; for ITU
+;isup_timer.t19 = 60000		; for ANSI
+isup_timer.t20 = 15000
+;isup_timer.t21 = 300000	; for ITU
+;isup_timer.t21 = 60000		; for ANSI
+isup_timer.t22 = 15000
+;isup_timer.t23 = 300000	; for ITU
+;isup_timer.t23 = 60000		; for ANSI
+
+isup_timer.t27 = 240000
+
+isup_timer.t33 = 12000
+;isup_timer.t35 = 15000		; ITU only
diff --git a/configure b/configure
index a028170258908ed8af7164a0c6da1c2e431c6c5c..59d48c6c1daea647539be32b9a922ff226fc9616 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 412977 .
+# From configure.ac Revision: 413772 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.68 for asterisk trunk.
 #
@@ -26664,7 +26664,7 @@ fi
 
 fi
 
-# Check for libss7 v1.0 branch compatible version.
+# Check for libss7 v2.0 branch compatible version.
 
 if test "x${PBX_SS7}" != "x1" -a "${USE_SS7}" != "no"; then
    pbxlibdir=""
@@ -26676,7 +26676,7 @@ if test "x${PBX_SS7}" != "x1" -a "${USE_SS7}" != "no"; then
          pbxlibdir="-L${SS7_DIR}"
       fi
    fi
-   pbxfuncname="ss7_set_adjpc"
+   pbxfuncname="ss7_set_isup_timer"
    if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
       AST_SS7_FOUND=yes
    else
diff --git a/configure.ac b/configure.ac
index da9d2d7519072d1b9a52749f4eccb80b5a301538..95b7dd02bfe047c1b3903043c196351269676af9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2096,8 +2096,8 @@ if test "x${PBX_SPANDSP}" = "x1" ; then
 	AST_EXT_LIB_CHECK([SPANDSP], [spandsp], [t38_terminal_init], [spandsp.h], [-ltiff])
 fi
 
-# Check for libss7 v1.0 branch compatible version.
-AST_EXT_LIB_CHECK([SS7], [ss7], [ss7_set_adjpc], [libss7.h])
+# Check for libss7 v2.0 branch compatible version.
+AST_EXT_LIB_CHECK([SS7], [ss7], [ss7_set_isup_timer], [libss7.h])
 
 AST_EXT_LIB_CHECK([OPENR2], [openr2], [openr2_chan_new], [openr2.h])