ipa: add ipa_server_link abstraction

This patch adds the ipa_server_link which allows to create
IPA servers.

I have also changed the ipaccess driver to use it. Still
missing the port of HSL driver.
diff --git a/include/osmocom/abis/ipa.h b/include/osmocom/abis/ipa.h
index bba7e8f..1a9e5d4 100644
--- a/include/osmocom/abis/ipa.h
+++ b/include/osmocom/abis/ipa.h
@@ -5,6 +5,21 @@
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/timer.h>
 
+struct ipa_server_link {
+	struct e1inp_line		*line;
+	struct osmo_fd			ofd;
+	struct llist_head		tx_queue;
+	const char			*addr;
+	uint16_t			port;
+	int (*accept_cb)(struct ipa_server_link *link, int fd);
+};
+
+struct ipa_server_link *ipa_server_link_create(void *ctx, struct e1inp_line *line, const char *addr, uint16_t port, int (*accept_cb)(struct ipa_server_link *link, int fd));
+void ipa_server_link_destroy(struct ipa_server_link *link);
+
+int ipa_server_link_open(struct ipa_server_link *link);
+void ipa_server_link_close(struct ipa_server_link *link);
+
 enum ipa_client_link_state {
 	IPA_CLIENT_LINK_STATE_NONE         = 0,
 	IPA_CLIENT_LINK_STATE_CONNECTING   = 1,
diff --git a/src/input/ipa.c b/src/input/ipa.c
index 7c84559..d99e310 100644
--- a/src/input/ipa.c
+++ b/src/input/ipa.c
@@ -239,3 +239,76 @@
 		break;
 	}
 }
+
+int ipa_server_fd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+	int ret;
+	struct sockaddr_in sa;
+	socklen_t sa_len = sizeof(sa);
+	struct ipa_server_link *link = ofd->data;
+
+	ret = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len);
+	if (ret < 0) {
+		LOGP(DINP, LOGL_ERROR, "failed to accept from origin "
+			"peer, reason=`%s'\n", strerror(errno));
+		return ret;
+	}
+	LOGP(DINP, LOGL_NOTICE, "accept()ed new link from %s to port %u\n",
+		inet_ntoa(sa.sin_addr), link->port);
+
+	if (link->accept_cb)
+		link->accept_cb(link, ret);
+
+	return 0;
+}
+
+struct ipa_server_link *
+ipa_server_link_create(void *ctx, struct e1inp_line *line,
+		       const char *addr, uint16_t port,
+		       int (*accept_cb)(struct ipa_server_link *link, int fd))
+{
+	struct ipa_server_link *ipa_link;
+
+	ipa_link = talloc_zero(ctx, struct ipa_server_link);
+	if (!ipa_link)
+		return NULL;
+
+	ipa_link->ofd.when |= BSC_FD_READ | BSC_FD_WRITE;
+	ipa_link->ofd.cb = ipa_server_fd_cb;
+	ipa_link->ofd.data = ipa_link;
+	ipa_link->addr = talloc_strdup(ipa_link, addr);
+	ipa_link->port = port;
+	ipa_link->accept_cb = accept_cb;
+	ipa_link->line = line;
+
+	return ipa_link;
+
+}
+
+void ipa_server_link_destroy(struct ipa_server_link *link)
+{
+	talloc_free(link);
+}
+
+int ipa_server_link_open(struct ipa_server_link *link)
+{
+	int ret;
+
+	ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+			     link->addr, link->port, OSMO_SOCK_F_BIND);
+	if (ret < 0)
+		return ret;
+
+	link->ofd.fd = ret;
+	if (osmo_fd_register(&link->ofd) < 0) {
+		close(ret);
+		return -EIO;
+	}
+	return 0;
+}
+
+void ipa_server_link_close(struct ipa_server_link *link)
+{
+	osmo_fd_unregister(&link->ofd);
+	close(link->ofd.fd);
+}
diff --git a/src/input/ipaccess.c b/src/input/ipaccess.c
index 60e7109..3c0f59b 100644
--- a/src/input/ipaccess.c
+++ b/src/input/ipaccess.c
@@ -181,6 +181,7 @@
 		        osmo_fd_unregister(bfd);
 		        close(bfd->fd);
 		        bfd->fd = -1;
+			talloc_free(bfd);
 		}
 		return error;
 	}
@@ -338,27 +339,18 @@
 };
 
 /* callback of the OML listening filedescriptor */
-static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
+static int ipaccess_bsc_oml_cb(struct ipa_server_link *link, int fd)
 {
 	int ret;
 	int idx = 0;
 	int i;
-	struct e1inp_line *line = listen_bfd->data;
+	struct e1inp_line *line = link->line;
 	struct e1inp_ts *e1i_ts;
 	struct osmo_fd *bfd;
-	struct sockaddr_in sa;
-	socklen_t sa_len = sizeof(sa);
 
-	if (!(what & BSC_FD_READ))
-		return 0;
-
-	ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
-	if (ret < 0) {
-		perror("accept");
-		return ret;
-	}
-	LOGP(DINP, LOGL_NOTICE, "accept()ed new OML link from %s\n",
-		inet_ntoa(sa.sin_addr));
+	bfd = talloc_zero(tall_ipa_ctx, struct osmo_fd);
+	if (!bfd)
+		return -ENOMEM;
 
 	/* create virrtual E1 timeslots for signalling */
 	e1inp_ts_config_sign(&line->ts[1-1], line);
@@ -369,8 +361,7 @@
 
 	e1i_ts = &line->ts[idx];
 
-	bfd = &e1i_ts->driver.ipaccess.fd;
-	bfd->fd = ret;
+	bfd->fd = fd;
 	bfd->data = line;
 	bfd->priv_nr = PRIV_OML;
 	bfd->cb = ipaccess_fd_cb;
@@ -379,6 +370,7 @@
 	if (ret < 0) {
 		LOGP(DINP, LOGL_ERROR, "could not register FD\n");
 		close(bfd->fd);
+		talloc_free(bfd);
 		return ret;
 	}
 
@@ -388,30 +380,16 @@
         return ret;
 }
 
-static int rsl_listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
+static int ipaccess_bsc_rsl_cb(struct ipa_server_link *link, int fd)
 {
-	struct sockaddr_in sa;
-	socklen_t sa_len = sizeof(sa);
 	struct osmo_fd *bfd;
 	int ret;
 
-	if (!(what & BSC_FD_READ))
-		return 0;
-
 	bfd = talloc_zero(tall_ipa_ctx, struct osmo_fd);
 	if (!bfd)
 		return -ENOMEM;
 
-	/* Some BTS has connected to us, but we don't know yet which line
-	 * (as created by the OML link) to associate it with.  Thus, we
-	 * allocate a temporary bfd until we have received ID from BTS */
-
-	bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
-	if (bfd->fd < 0) {
-		perror("accept");
-		return bfd->fd;
-	}
-	LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
+	bfd->fd = fd;
 	bfd->priv_nr = PRIV_RSL;
 	bfd->cb = ipaccess_fd_cb;
 	bfd->when = BSC_FD_READ;
@@ -484,40 +462,44 @@
 	int ret = -ENOENT;
 
 	switch(role) {
-	case E1INP_LINE_R_BSC:
+	case E1INP_LINE_R_BSC: {
+		struct ipa_server_link *oml_link, *rsl_link;
+
 		LOGP(DINP, LOGL_NOTICE, "enabling ipaccess BSC mode\n");
 
-		/* Listen for OML connections */
-		ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
-				     addr, IPA_TCP_PORT_OML, OSMO_SOCK_F_BIND);
-		if (ret < 0)
-			return ret;
-
-		e1h->listen_fd.fd = ret;
-		e1h->listen_fd.when |= BSC_FD_READ;
-		e1h->listen_fd.cb = listen_fd_cb;
-		e1h->listen_fd.data = line;
-
-		if (osmo_fd_register(&e1h->listen_fd) < 0) {
-			close(ret);
-			return ret;
+		oml_link = ipa_server_link_create(tall_ipa_ctx, line,
+					          "0.0.0.0", IPA_TCP_PORT_OML,
+						  ipaccess_bsc_oml_cb);
+		if (oml_link == NULL) {
+			LOGP(DINP, LOGL_ERROR, "cannot create OML "
+				"BSC link: %s\n", strerror(errno));
+			return -ENOMEM;
 		}
-		/* Listen for RSL connections */
-		ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
-				     addr, IPA_TCP_PORT_RSL, OSMO_SOCK_F_BIND);
-		if (ret < 0)
-			return ret;
-
-		e1h->rsl_listen_fd.fd = ret;
-		e1h->rsl_listen_fd.when |= BSC_FD_READ;
-		e1h->rsl_listen_fd.cb = rsl_listen_fd_cb;
-		e1h->rsl_listen_fd.data = NULL;
-
-		if (osmo_fd_register(&e1h->rsl_listen_fd) < 0) {
-			close(ret);
-			return ret;
+		if (ipa_server_link_open(oml_link) < 0) {
+			LOGP(DINP, LOGL_ERROR, "cannot open OML BSC link: %s\n",
+				strerror(errno));
+			ipa_server_link_close(oml_link);
+			ipa_server_link_destroy(oml_link);
+			return -EIO;
 		}
+		rsl_link = ipa_server_link_create(tall_ipa_ctx, line,
+						  "0.0.0.0", IPA_TCP_PORT_RSL,
+						  ipaccess_bsc_rsl_cb);
+		if (rsl_link == NULL) {
+			LOGP(DINP, LOGL_ERROR, "cannot create RSL "
+				"BSC link: %s\n", strerror(errno));
+			return -ENOMEM;
+		}
+		if (ipa_server_link_open(rsl_link) < 0) {
+			LOGP(DINP, LOGL_ERROR, "cannot open RSL BSC link: %s\n",
+				strerror(errno));
+			ipa_server_link_close(rsl_link);
+			ipa_server_link_destroy(rsl_link);
+			return -EIO;
+		}
+		ret = 0;
 		break;
+	}
 	case E1INP_LINE_R_BTS: {
 		struct ipa_client_link *link, *rsl_link;