[ipa] Handle losing the RSL/OML connection..

This is addressing multiple issues regarding the loss of the
OML/RSL link to the BTS.

1.) When we lose the OML link, close down all RSL connections
on all TRXs (only tested with one TRX) and free the e1inp_line
allocated for the OML connection.
2.) When we lose the RSL link on any TRX and we know to which
lines this connection belongs, we will close down the OML connection
as we have a problem to just reactivate one RSL link.
3.) When we lose the RSL link on any TRX and we do not know
where it belongs to we will free the bfd we have allocated in the
rsl listen/accept method and we properly close the socket (i could
not test this one properly). This is made under the assumption
the BTS has not responded to the ID request.
4.) When we already have a bts->oml_link we will throw it away
and use the new link (it should not happen) and the same applies
to the rsl link.
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 8722791..8fb9d3a 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -1,6 +1,8 @@
 /* OpenBSC Abis input driver for ip.access */
 
 /* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by On-Waves
  *
  * All Rights Reserved
  *
@@ -234,6 +236,8 @@
 		}
 		DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id);
 		if (bfd->priv_nr == PRIV_OML) {
+			/* drop any old oml connection */
+			ipaccess_drop_oml(bts);
 			bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1],
 						  E1INP_SIGN_OML, bts->c0,
 						  bts->oml_tei, 0);
@@ -241,7 +245,18 @@
 			struct e1inp_ts *e1i_ts;
 			struct bsc_fd *newbfd;
 			struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id);
-			
+
+			/* drop any old rsl connection */
+			ipaccess_drop_rsl(trx);
+
+			if (!bts->oml_link) {
+				bsc_unregister_fd(bfd);
+				close(bfd->fd);
+				bfd->fd = -1;
+				talloc_free(bfd);
+				return 0;
+			}
+
 			bfd->data = line = bts->oml_link->ts->line;
 			e1i_ts = &line->ts[PRIV_RSL + trx_id - 1];
 			newbfd = &e1i_ts->driver.ipaccess.fd;
@@ -251,19 +266,13 @@
 							E1INP_SIGN_RSL, trx,
 							trx->rsl_tei, 0);
 
-			if (newbfd->fd >= 0) {
-				LOGP(DINP, LOGL_ERROR, "BTS is still registered. Closing old connection.\n");
-				bsc_unregister_fd(newbfd);
-				close(newbfd->fd);
-				newbfd->fd = -1;
-			}
-
 			/* get rid of our old temporary bfd */
 			memcpy(newbfd, bfd, sizeof(*newbfd));
 			newbfd->priv_nr = PRIV_RSL + trx_id;
 			bsc_unregister_fd(bfd);
-			bsc_register_fd(newbfd);
+			bfd->fd = -1;
 			talloc_free(bfd);
+			bsc_register_fd(newbfd);
 		}
 		break;
 	}
@@ -328,6 +337,103 @@
 	return msg;
 }
 
+int ipaccess_drop_oml(struct gsm_bts *bts)
+{
+	struct gsm_bts_trx *trx;
+	struct e1inp_ts *ts;
+	struct e1inp_line *line;
+	struct bsc_fd *bfd;
+
+	if (!bts || !bts->oml_link)
+		return -1;
+
+	/* send OML down */
+	ts = bts->oml_link->ts;
+	line = ts->line;
+	e1inp_event(ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
+
+	bfd = &ts->driver.ipaccess.fd;
+	bsc_unregister_fd(bfd);
+	close(bfd->fd);
+	bfd->fd = -1;
+
+	/* clean up OML and RSL */
+	e1inp_sign_link_destroy(bts->oml_link);
+	bts->oml_link = NULL;
+	bts->ip_access.flags = 0;
+
+	/* drop all RSL connections too */
+	llist_for_each_entry(trx, &bts->trx_list, list)
+		ipaccess_drop_rsl(trx);
+
+	/* kill the E1 line now... as we have no one left to use it */
+	talloc_free(line);
+
+	return -1;
+}
+
+static int ipaccess_drop(struct e1inp_ts *ts, struct bsc_fd *bfd)
+{
+	struct e1inp_sign_link *link;
+	int bts_nr;
+
+	if (!ts) {
+		/*
+		 * If we don't have a TS this means that this is a RSL
+		 * connection but we are not past the authentication
+		 * handling yet. So we can safely delete this bfd and
+		 * wait for a reconnect.
+		 */
+		bsc_unregister_fd(bfd);
+		close(bfd->fd);
+		bfd->fd = -1;
+		talloc_free(bfd);
+		return -1;
+	}
+
+	/* attempt to find a signalling link */
+	if (ts->type == E1INP_TS_TYPE_SIGN) {
+		llist_for_each_entry(link, &ts->sign.sign_links, list) {
+			bts_nr = link->trx->bts->bts_nr;
+			/* we have issues just reconnecting RLS so we drop OML */
+			ipaccess_drop_oml(link->trx->bts);
+			return bts_nr;
+		}
+	}
+
+	/* error case */
+	LOGP(DINP, LOGL_ERROR, "Failed to find a signalling link for ts: %p\n", ts);
+	bsc_unregister_fd(bfd);
+	close(bfd->fd);
+	bfd->fd = -1;
+	return -1;
+}
+
+int ipaccess_drop_rsl(struct gsm_bts_trx *trx)
+{
+	struct bsc_fd *bfd;
+	struct e1inp_ts *ts;
+
+	if (!trx || !trx->rsl_link)
+		return -1;
+
+	/* send RSL down */
+	ts = trx->rsl_link->ts;
+	e1inp_event(ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
+
+	/* close the socket */
+	bfd = &ts->driver.ipaccess.fd;
+	bsc_unregister_fd(bfd);
+	close(bfd->fd);
+	bfd->fd = -1;
+
+	/* destroy */
+	e1inp_sign_link_destroy(trx->rsl_link);
+	trx->rsl_link = NULL;
+
+	return -1;
+}
+
 static int handle_ts1_read(struct bsc_fd *bfd)
 {
 	struct e1inp_line *line = bfd->data;
@@ -341,18 +447,12 @@
 	msg = ipaccess_read_msg(bfd, &error);
 	if (!msg) {
 		if (error == 0) {
-			link = e1inp_lookup_sign_link(e1i_ts, IPAC_PROTO_OML, 0);
-			if (link) {
-				link->trx->bts->ip_access.flags = 0;
+			int ret = ipaccess_drop(e1i_ts, bfd);
+			if (ret >= 0)
 				LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n",
-					link->trx->bts->nr);
-			} else
+					ret);
+			else
 				LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n");
-			e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
-			e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
-			bsc_unregister_fd(bfd);
-			close(bfd->fd);
-			bfd->fd = -1;
 		}
 		return error;
 	}
@@ -362,13 +462,8 @@
 	hh = (struct ipaccess_head *) msg->data;
 	if (hh->proto == IPAC_PROTO_IPACCESS) {
 		ret = ipaccess_rcvmsg(line, msg, bfd);
-		if (ret < 0) {
-			e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
-			e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
-			bsc_unregister_fd(bfd);
-			close(bfd->fd);
-			bfd->fd = -1;
-		}
+		if (ret < 0)
+			ipaccess_drop(e1i_ts, bfd);
 		msgb_free(msg);
 		return ret;
 	}