aboutsummaryrefslogtreecommitdiffstats
path: root/src/channel/abis
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@gnumonks.org>2012-08-19 01:05:54 +0200
committerPablo Neira Ayuso <pablo@gnumonks.org>2012-08-19 01:06:57 +0200
commit060e46ea6dd6b25d851baaa8757b3531a37edb1a (patch)
tree18314a68737517b0a6923547444ec99617b53276 /src/channel/abis
parent775c1de0891da2d3b16efada5ee8b99f96d045a1 (diff)
channel: add abis directory under channel
And move all existing A-bis channel implementation to channel/abis/ directory. This is just a cleanup to reorganize the source code tree.
Diffstat (limited to 'src/channel/abis')
-rw-r--r--src/channel/abis/Makefile.am8
-rw-r--r--src/channel/abis/ipa_stream_client.c370
-rw-r--r--src/channel/abis/ipa_stream_server.c522
3 files changed, 900 insertions, 0 deletions
diff --git a/src/channel/abis/Makefile.am b/src/channel/abis/Makefile.am
new file mode 100644
index 0000000..3c6fef6
--- /dev/null
+++ b/src/channel/abis/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
+AM_CFLAGS= -fPIC -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
+AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS)
+
+noinst_LTLIBRARIES = libosmonetif-abis.la
+
+libosmonetif_abis_la_SOURCES = ipa_stream_server.c \
+ ipa_stream_client.c
diff --git a/src/channel/abis/ipa_stream_client.c b/src/channel/abis/ipa_stream_client.c
new file mode 100644
index 0000000..bb6a1ee
--- /dev/null
+++ b/src/channel/abis/ipa_stream_client.c
@@ -0,0 +1,370 @@
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/tlv.h>
+
+#include <osmocom/netif/channel.h>
+#include <osmocom/netif/stream.h>
+#include <osmocom/netif/ipa.h>
+#include <osmocom/netif/ipa_unit.h>
+
+#define CHAN_SIGN_OML 0
+#define CHAN_SIGN_RSL 1
+
+/* default IPA cli ports. */
+#define IPA_TCP_PORT_OML 3002
+#define IPA_TCP_PORT_RSL 3003
+
+static void *abis_ipa_cli_tall;
+
+struct chan_abis_ipa_cli {
+ struct ipaccess_unit *unit;
+
+ struct osmo_stream_cli *oml;
+ struct osmo_stream_cli *rsl;
+
+ void (*signal_msg)(struct msgb *msg, int type);
+};
+
+static int oml_read_cb(struct osmo_stream_cli *conn);
+static int rsl_read_cb(struct osmo_stream_cli *conn);
+
+static int chan_abis_ipa_cli_create(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_cli *c = (struct chan_abis_ipa_cli *)chan->data;
+
+ c->unit = osmo_ipa_unit_alloc();
+ if (c->unit == NULL)
+ goto err;
+
+ c->oml = osmo_stream_cli_create(abis_ipa_cli_tall);
+ if (c->oml == NULL)
+ goto err_oml;
+
+ /* default address and port for OML. */
+ osmo_stream_cli_set_addr(c->oml, "0.0.0.0");
+ osmo_stream_cli_set_port(c->oml, IPA_TCP_PORT_OML);
+ osmo_stream_cli_set_read_cb(c->oml, oml_read_cb);
+ osmo_stream_cli_set_data(c->oml, chan);
+
+ c->rsl = osmo_stream_cli_create(abis_ipa_cli_tall);
+ if (c->rsl == NULL)
+ goto err_rsl;
+
+ /* default address and port for RSL. */
+ osmo_stream_cli_set_addr(c->rsl, "0.0.0.0");
+ osmo_stream_cli_set_port(c->rsl, IPA_TCP_PORT_RSL);
+ osmo_stream_cli_set_read_cb(c->rsl, rsl_read_cb);
+ osmo_stream_cli_set_data(c->rsl, chan);
+
+ return 0;
+err_rsl:
+ osmo_stream_cli_destroy(c->oml);
+err_oml:
+ osmo_ipa_unit_free(c->unit);
+err:
+ return -1;
+}
+
+static void chan_abis_ipa_cli_destroy(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_cli *c = (struct chan_abis_ipa_cli *)chan->data;
+
+ osmo_ipa_unit_free(c->unit);
+ talloc_free(c->rsl);
+ talloc_free(c->oml);
+}
+
+static int chan_abis_ipa_cli_open(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_cli *c = (struct chan_abis_ipa_cli *)chan->data;
+ struct osmo_fd *ofd;
+ int ret, on = 1;
+
+ if (osmo_stream_cli_open(c->oml) < 0)
+ goto err;
+
+ ofd = osmo_stream_cli_get_ofd(c->oml);
+ ret = setsockopt(ofd->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+ if (ret < 0)
+ goto err_oml;
+
+ if (osmo_stream_cli_open(c->rsl) < 0)
+ goto err_oml;
+
+ ofd = osmo_stream_cli_get_ofd(c->rsl);
+ ret = setsockopt(ofd->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+ if (ret < 0)
+ goto err_rsl;
+
+ return 0;
+
+err_rsl:
+ osmo_stream_cli_close(c->rsl);
+err_oml:
+ osmo_stream_cli_close(c->oml);
+err:
+ return -1;
+}
+
+static void chan_abis_ipa_cli_close(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_cli *c = (struct chan_abis_ipa_cli *)chan->data;
+
+ osmo_stream_cli_close(c->oml);
+ osmo_stream_cli_close(c->rsl);
+}
+
+static int chan_abis_ipa_cli_enqueue(struct osmo_chan *c, struct msgb *msg)
+{
+ osmo_stream_cli_send(msg->dst, msg);
+ return 0;
+}
+
+void osmo_abis_ipa_cli_set_oml_addr(struct osmo_chan *c, const char *addr)
+{
+ struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data;
+
+ osmo_stream_cli_set_addr(s->oml, addr);
+}
+
+void osmo_abis_ipa_cli_set_oml_port(struct osmo_chan *c, uint16_t port)
+{
+ struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data;
+
+ osmo_stream_cli_set_port(s->oml, port);
+}
+
+void osmo_abis_ipa_cli_set_rsl_addr(struct osmo_chan *c, const char *addr)
+{
+ struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data;
+
+ osmo_stream_cli_set_addr(s->rsl, addr);
+}
+
+void osmo_abis_ipa_cli_set_rsl_port(struct osmo_chan *c, uint16_t port)
+{
+ struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data;
+
+ osmo_stream_cli_set_port(s->rsl, port);
+}
+
+void osmo_abis_ipa_cli_set_unit(struct osmo_chan *c, struct ipaccess_unit *unit)
+{
+ struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data;
+
+ osmo_ipa_unit_free(s->unit);
+ s->unit = unit;
+}
+
+void osmo_abis_ipa_cli_set_cb_signalmsg(struct osmo_chan *c,
+ void (*signal_msg)(struct msgb *msg, int type))
+{
+ struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data;
+
+ s->signal_msg = signal_msg;
+}
+
+static struct msgb *
+ipa_cli_id_resp(struct ipaccess_unit *dev, uint8_t *data, int len)
+{
+ struct msgb *nmsg;
+ char str[64];
+ uint8_t *tag;
+
+ nmsg = osmo_ipa_msg_alloc(0);
+ if (nmsg == NULL)
+ return NULL;
+
+ *msgb_put(nmsg, 1) = IPAC_MSGT_ID_RESP;
+ while (len) {
+ if (len < 2) {
+ LOGP(DLINP, LOGL_NOTICE,
+ "Short read of ipaccess tag\n");
+ msgb_free(nmsg);
+ return NULL;
+ }
+ switch (data[1]) {
+ case IPAC_IDTAG_UNIT:
+ sprintf(str, "%u/%u/%u",
+ dev->site_id, dev->bts_id, dev->trx_id);
+ break;
+ case IPAC_IDTAG_MACADDR:
+ sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+ dev->mac_addr[0], dev->mac_addr[1],
+ dev->mac_addr[2], dev->mac_addr[3],
+ dev->mac_addr[4], dev->mac_addr[5]);
+ break;
+ case IPAC_IDTAG_LOCATION1:
+ strcpy(str, dev->location1);
+ break;
+ case IPAC_IDTAG_LOCATION2:
+ strcpy(str, dev->location2);
+ break;
+ case IPAC_IDTAG_EQUIPVERS:
+ strcpy(str, dev->equipvers);
+ break;
+ case IPAC_IDTAG_SWVERSION:
+ strcpy(str, dev->swversion);
+ break;
+ case IPAC_IDTAG_UNITNAME:
+ sprintf(str, "%s-%02x-%02x-%02x-%02x-%02x-%02x",
+ dev->unit_name,
+ dev->mac_addr[0], dev->mac_addr[1],
+ dev->mac_addr[2], dev->mac_addr[3],
+ dev->mac_addr[4], dev->mac_addr[5]);
+ break;
+ case IPAC_IDTAG_SERNR:
+ strcpy(str, dev->serno);
+ break;
+ default:
+ LOGP(DLINP, LOGL_NOTICE,
+ "Unknown ipaccess tag 0x%02x\n", *data);
+ msgb_free(nmsg);
+ return NULL;
+ }
+ LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", data[1], str);
+ tag = msgb_put(nmsg, 3 + strlen(str) + 1);
+ tag[0] = 0x00;
+ tag[1] = 1 + strlen(str) + 1;
+ tag[2] = data[1];
+ memcpy(tag + 3, str, strlen(str) + 1);
+ data += 2;
+ len -= 2;
+ }
+ osmo_ipa_msg_push_header(nmsg, IPAC_PROTO_IPACCESS);
+ return nmsg;
+}
+
+static struct msgb *ipa_cli_id_ack(void)
+{
+ struct msgb *nmsg2;
+
+ nmsg2 = osmo_ipa_msg_alloc(0);
+ if (nmsg2 == NULL)
+ return NULL;
+
+ *msgb_put(nmsg2, 1) = IPAC_MSGT_ID_ACK;
+ osmo_ipa_msg_push_header(nmsg2, IPAC_PROTO_IPACCESS);
+
+ return nmsg2;
+}
+
+static int
+abis_ipa_cli_rcvmsg(struct osmo_chan *c, struct osmo_stream_cli *conn,
+ struct msgb *msg, int type)
+{
+ uint8_t msg_type = *(msg->l2h);
+ struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
+ struct chan_abis_ipa_cli *chan = (struct chan_abis_ipa_cli *)&c->data;
+ int ret;
+
+ /* Handle IPA PING, PONG and ID_ACK messages. */
+ if (osmo_ipa_rcvmsg_base(msg, ofd, 0)) /* XXX: 0 indicates client */
+ return 0;
+
+ if (msg_type == IPAC_MSGT_ID_GET) {
+ struct msgb *rmsg;
+ uint8_t *data = msgb_l2(msg);
+ int len = msgb_l2len(msg);
+
+ LOGP(DLINP, LOGL_NOTICE, "received ID get\n");
+
+ rmsg = ipa_cli_id_resp(chan->unit, data + 1, len - 1);
+ osmo_stream_cli_send(conn, rmsg);
+
+ /* send ID_ACK. */
+ rmsg = ipa_cli_id_ack();
+ osmo_stream_cli_send(conn, rmsg);
+ ret = 0;
+ } else {
+ LOGP(DLINP, LOGL_ERROR, "Unknown IPA message type\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int read_cb(struct osmo_stream_cli *conn, int type)
+{
+ int ret;
+ struct msgb *msg;
+ struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
+ struct osmo_chan *chan = osmo_stream_cli_get_data(conn);
+ struct chan_abis_ipa_cli *s;
+ struct ipa_head *hh;
+
+ LOGP(DLINP, LOGL_DEBUG, "received message from stream\n");
+
+ msg = osmo_ipa_msg_alloc(0);
+ if (msg == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "cannot allocate message\n");
+ return 0;
+ }
+ /* XXX replace with generic stream reader */
+ ret = osmo_ipa_msg_recv(ofd->fd, msg);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "cannot receive message\n");
+ msgb_free(msg);
+ /* not the dummy connection, release it. */
+ return 0;
+ } else if (ret == 0) {
+ /* link has vanished, dead socket. */
+ LOGP(DLINP, LOGL_ERROR, "closed connection\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ hh = (struct ipa_head *) msg->data;
+ if (hh->proto == IPAC_PROTO_IPACCESS) {
+ abis_ipa_cli_rcvmsg(chan, conn, msg, type);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ chan = osmo_stream_cli_get_data(conn);
+ if (chan == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "no matching signalling link\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ if (hh->proto != IPAC_PROTO_OML && hh->proto != IPAC_PROTO_RSL) {
+ LOGP(DLINP, LOGL_ERROR, "wrong protocol\n");
+ return -EIO;
+ }
+ msg->dst = chan;
+
+ s = (struct chan_abis_ipa_cli *)chan->data;
+ s->signal_msg(msg, type);
+
+ return 0;
+}
+
+static int oml_read_cb(struct osmo_stream_cli *conn)
+{
+ return read_cb(conn, CHAN_SIGN_OML);
+}
+
+static int rsl_read_cb(struct osmo_stream_cli *conn)
+{
+ return read_cb(conn, CHAN_SIGN_RSL);
+}
+
+struct osmo_chan_type chan_abis_ipa_cli = {
+ .type = OSMO_CHAN_ABIS_IPA_CLI,
+ .subtype = OSMO_SUBCHAN_STREAM,
+ .name = "A-bis IPA client",
+ .datasiz = sizeof(struct chan_abis_ipa_cli),
+ .create = chan_abis_ipa_cli_create,
+ .destroy = chan_abis_ipa_cli_destroy,
+ .open = chan_abis_ipa_cli_open,
+ .close = chan_abis_ipa_cli_close,
+ .enqueue = chan_abis_ipa_cli_enqueue,
+};
diff --git a/src/channel/abis/ipa_stream_server.c b/src/channel/abis/ipa_stream_server.c
new file mode 100644
index 0000000..6524033
--- /dev/null
+++ b/src/channel/abis/ipa_stream_server.c
@@ -0,0 +1,522 @@
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/tlv.h>
+
+#include <osmocom/netif/channel.h>
+#include <osmocom/netif/stream.h>
+#include <osmocom/netif/ipa.h>
+
+#define CHAN_SIGN_OML 0
+#define CHAN_SIGN_RSL 1
+
+/* default IPA srv ports. */
+#define IPA_TCP_PORT_OML 3002
+#define IPA_TCP_PORT_RSL 3003
+
+static void *abis_ipa_srv_tall;
+
+static int oml_accept_cb(struct osmo_stream_srv_link *srv, int fd);
+static int rsl_accept_cb(struct osmo_stream_srv_link *srv, int fd);
+
+struct chan_abis_ipa_srv {
+ struct osmo_stream_srv_link *oml;
+ struct osmo_stream_srv_link *rsl;
+
+ struct llist_head bts_list;
+ struct llist_head conn_list;
+
+ void (*signal_msg)(struct msgb *msg, int type);
+};
+
+struct ipa_unit {
+ struct llist_head head;
+ uint16_t site_id;
+ uint16_t bts_id;
+};
+
+struct ipa {
+ struct llist_head head;
+ struct ipa_unit *unit;
+ struct osmo_chan *chan;
+ struct osmo_stream_srv *oml;
+ struct osmo_stream_srv *rsl;
+};
+
+static int chan_abis_ipa_srv_create(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_srv *c =
+ (struct chan_abis_ipa_srv *)chan->data;
+ struct ipa *ipa;
+
+ /* dummy connection. */
+ ipa = talloc_zero(chan->ctx, struct ipa);
+ if (ipa == NULL)
+ goto err;
+
+ ipa->chan = chan;
+
+ c->oml = osmo_stream_srv_link_create(abis_ipa_srv_tall);
+ if (c->oml == NULL)
+ goto err_oml;
+
+ /* default address and port for OML. */
+ osmo_stream_srv_link_set_addr(c->oml, "0.0.0.0");
+ osmo_stream_srv_link_set_port(c->oml, IPA_TCP_PORT_OML);
+ osmo_stream_srv_link_set_accept_cb(c->oml, oml_accept_cb);
+ osmo_stream_srv_link_set_data(c->oml, ipa);
+
+ c->rsl = osmo_stream_srv_link_create(abis_ipa_srv_tall);
+ if (c->rsl == NULL)
+ goto err_rsl;
+
+ /* default address and port for RSL. */
+ osmo_stream_srv_link_set_addr(c->rsl, "0.0.0.0");
+ osmo_stream_srv_link_set_port(c->rsl, IPA_TCP_PORT_RSL);
+ osmo_stream_srv_link_set_accept_cb(c->rsl, rsl_accept_cb);
+ osmo_stream_srv_link_set_data(c->rsl, ipa);
+
+ INIT_LLIST_HEAD(&c->bts_list);
+ INIT_LLIST_HEAD(&c->conn_list);
+
+ return 0;
+err_rsl:
+ osmo_stream_srv_link_destroy(c->oml);
+err_oml:
+ talloc_free(ipa);
+err:
+ return -1;
+}
+
+static void chan_abis_ipa_srv_destroy(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_srv *c =
+ (struct chan_abis_ipa_srv *)chan->data;
+ struct ipa *ipa =
+ osmo_stream_srv_link_get_data(c->oml);
+
+ talloc_free(ipa);
+ talloc_free(c->rsl);
+ talloc_free(c->oml);
+}
+
+static int chan_abis_ipa_srv_open(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_srv *c =
+ (struct chan_abis_ipa_srv *)chan->data;
+ struct osmo_fd *ofd;
+ int ret, on = 1;
+
+ if (osmo_stream_srv_link_open(c->oml) < 0)
+ goto err;
+
+ ofd = osmo_stream_srv_link_get_ofd(c->oml);
+ ret = setsockopt(ofd->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+ if (ret < 0)
+ goto err_oml;
+
+ if (osmo_stream_srv_link_open(c->rsl) < 0)
+ goto err_oml;
+
+ ofd = osmo_stream_srv_link_get_ofd(c->rsl);
+ ret = setsockopt(ofd->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+ if (ret < 0)
+ goto err_rsl;
+
+ return 0;
+
+err_rsl:
+ osmo_stream_srv_link_close(c->rsl);
+err_oml:
+ osmo_stream_srv_link_close(c->oml);
+err:
+ return -1;
+}
+
+static void chan_abis_ipa_srv_close(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_srv *c =
+ (struct chan_abis_ipa_srv *)chan->data;
+
+ osmo_stream_srv_link_close(c->oml);
+ osmo_stream_srv_link_close(c->rsl);
+}
+
+static int chan_abis_ipa_srv_enqueue(struct osmo_chan *c, struct msgb *msg)
+{
+ osmo_stream_srv_send(msg->dst, msg);
+ return 0;
+}
+
+void
+osmo_abis_ipa_srv_set_oml_addr(struct osmo_chan *c, const char *addr)
+{
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)&c->data;
+
+ osmo_stream_srv_link_set_addr(s->oml, addr);
+}
+
+void
+osmo_abis_ipa_srv_set_oml_port(struct osmo_chan *c, uint16_t port)
+{
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)&c->data;
+
+ osmo_stream_srv_link_set_port(s->oml, port);
+}
+
+void
+osmo_abis_ipa_srv_set_rsl_addr(struct osmo_chan *c, const char *addr)
+{
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)&c->data;
+
+ osmo_stream_srv_link_set_addr(s->rsl, addr);
+}
+
+void
+osmo_abis_ipa_srv_set_rsl_port(struct osmo_chan *c, uint16_t port)
+{
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)&c->data;
+
+ osmo_stream_srv_link_set_port(s->rsl, port);
+}
+
+void
+osmo_abis_ipa_srv_set_cb_signalmsg(struct osmo_chan *c,
+ void (*signal_msg)(struct msgb *msg, int type))
+{
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)&c->data;
+
+ s->signal_msg = signal_msg;
+}
+
+static int oml_read_cb(struct osmo_stream_srv *conn);
+
+static int oml_accept_cb(struct osmo_stream_srv_link *srv, int fd)
+{
+ struct osmo_stream_srv *conn;
+ struct ipa *ipa = osmo_stream_srv_link_get_data(srv);
+ struct osmo_fd *ofd;
+
+ conn = osmo_stream_srv_create(abis_ipa_srv_tall,
+ srv, fd,
+ oml_read_cb, NULL, ipa);
+ if (conn == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "error while creating connection\n");
+ return -1;
+ }
+
+ ofd = osmo_stream_srv_get_ofd(conn);
+
+ /* XXX: better use chan_abis_ipa_srv_enqueue. */
+ ipaccess_send_id_req(ofd->fd);
+
+ return 0;
+}
+
+static int rsl_read_cb(struct osmo_stream_srv *conn);
+
+static int rsl_accept_cb(struct osmo_stream_srv_link *srv, int fd)
+{
+ struct osmo_stream_srv *conn;
+ struct ipa *ipa = osmo_stream_srv_link_get_data(srv);
+ struct osmo_fd *ofd;
+
+ conn = osmo_stream_srv_create(abis_ipa_srv_tall, srv, fd,
+ rsl_read_cb, NULL, ipa);
+ if (conn == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "error while creating connection\n");
+ return -1;
+ }
+
+ ofd = osmo_stream_srv_get_ofd(conn);
+
+ /* XXX: better use chan_abis_ipa_srv_enqueue. */
+ ipaccess_send_id_req(ofd->fd);
+
+ return 0;
+}
+
+static struct ipa_unit *
+osmo_abis_ipa_unit_find(struct osmo_chan *c, uint16_t site_id, uint16_t bts_id)
+{
+ struct ipa_unit *unit;
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)c->data;
+
+ llist_for_each_entry(unit, &s->bts_list, head) {
+ if (unit->site_id == site_id &&
+ unit->bts_id == bts_id)
+ return unit;
+ }
+ return NULL;
+}
+
+int
+osmo_abis_ipa_unit_add(struct osmo_chan *c,
+ uint16_t site_id, uint16_t bts_id)
+{
+ struct ipa_unit *unit;
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)c->data;
+
+ unit = talloc_zero(c->ctx, struct ipa_unit);
+ if (unit == NULL)
+ return -1;
+
+ unit->site_id = site_id;
+ unit->bts_id = bts_id;
+ llist_add(&unit->head, &s->bts_list);
+
+ return 0;
+}
+
+static struct ipa *
+osmo_abis_ipa_alloc(struct ipa_unit *unit, struct osmo_chan *chan)
+{
+ struct ipa *ipa;
+
+ ipa = talloc_zero(chan->ctx, struct ipa);
+ if (ipa == NULL)
+ return NULL;
+
+ ipa->unit = unit;
+ ipa->chan = chan;
+
+ return ipa;
+}
+
+static struct ipa *
+osmo_abis_ipa_add(struct ipa_unit *unit, struct osmo_chan *chan)
+{
+ struct ipa *ipa;
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)chan->data;
+
+ ipa = osmo_abis_ipa_alloc(unit, chan);
+ if (ipa == NULL)
+ return NULL;
+
+ llist_add(&ipa->head, &s->conn_list);
+
+ return ipa;
+}
+
+static struct ipa *
+osmo_abis_ipa_find(struct ipa_unit *unit, struct osmo_chan *chan)
+{
+ struct ipa *ipa;
+ struct chan_abis_ipa_srv *s =
+ (struct chan_abis_ipa_srv *)chan->data;
+
+ llist_for_each_entry(ipa, &s->conn_list, head) {
+ if (ipa->unit->site_id == unit->site_id &&
+ ipa->unit->bts_id == unit->bts_id) {
+ return ipa;
+ }
+ }
+ return NULL;
+}
+
+static void abis_ipa_put(struct ipa *ipa)
+{
+ llist_del(&ipa->head);
+ osmo_stream_srv_destroy(ipa->oml);
+ osmo_stream_srv_destroy(ipa->rsl);
+ talloc_free(ipa);
+}
+
+static int
+abis_ipa_srv_rcvmsg(struct osmo_chan *c,
+ struct osmo_stream_srv *conn,
+ struct msgb *msg, int type)
+{
+ struct tlv_parsed tlvp;
+ uint8_t msg_type = *(msg->l2h);
+ struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
+ char *unitid;
+ int len, ret;
+
+ /* Handle IPA PING, PONG and ID_ACK messages */
+ if (osmo_ipa_rcvmsg_base(msg, ofd, 1)) /* XXX: 1 indicates server */
+ return 0;
+
+ if (msg_type == IPAC_MSGT_ID_RESP) {
+ struct ipa_unit *unit;
+ struct ipa *ipa;
+ struct ipaccess_unit unit_data;
+
+ DEBUGP(DLMI, "ID_RESP\n");
+ /* parse tags, search for Unit ID */
+ ret = osmo_ipa_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2,
+ msgb_l2len(msg)-2);
+ DEBUGP(DLMI, "\n");
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "IPA response message "
+ "with malformed TLVs\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
+ LOGP(DLINP, LOGL_ERROR, "IPA response message "
+ "without unit ID\n");
+ ret = -EINVAL;
+ goto err;
+
+ }
+ len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT);
+ if (len < 1) {
+ LOGP(DLINP, LOGL_ERROR, "IPA response message "
+ "with too small unit ID\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT);
+ unitid[len - 1] = '\0';
+ osmo_ipa_parse_unitid(unitid, &unit_data);
+
+ unit = osmo_abis_ipa_unit_find(c, unit_data.site_id,
+ unit_data.bts_id);
+ if (unit == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "Unable to find BTS "
+ "configuration for %u/%u/%u, disconnecting\n",
+ unit_data.site_id, unit_data.bts_id,
+ unit_data.trx_id);
+ return 0;
+ }
+ DEBUGP(DLINP, "Identified BTS %u/%u/%u\n",
+ unit_data.site_id, unit_data.bts_id,
+ unit_data.trx_id);
+
+ ipa = osmo_abis_ipa_find(unit, c);
+ if (ipa == NULL) {
+ ipa = osmo_abis_ipa_add(unit, c);
+ if (ipa == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "OOM\n");
+ return 0;
+ }
+ osmo_stream_srv_set_data(conn, ipa);
+ }
+
+ if (type == CHAN_SIGN_OML) {
+ if (ipa->oml) {
+ /* link already exists, kill it. */
+ osmo_stream_srv_destroy(ipa->oml);
+ return 0;
+ }
+ ipa->oml = conn;
+ } else if (type == CHAN_SIGN_RSL) {
+ if (!ipa->oml) {
+ /* no OML link? Restart from scratch. */
+ abis_ipa_put(ipa);
+ return 0;
+ }
+ if (ipa->rsl) {
+ /* RSL link already exists, kill it. */
+ osmo_stream_srv_destroy(ipa->rsl);
+ return 0;
+ }
+ ipa->rsl = conn;
+ }
+ ret = 0;
+ } else {
+ LOGP(DLINP, LOGL_ERROR, "Unknown IPA message type\n");
+ ret = -EINVAL;
+ }
+err:
+ return ret;
+}
+
+static int read_cb(struct osmo_stream_srv *conn, int type)
+{
+ int ret;
+ struct msgb *msg;
+ struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
+ struct ipa *ipa = osmo_stream_srv_get_data(conn);
+ struct chan_abis_ipa_srv *s;
+ struct ipa_head *hh;
+
+ LOGP(DLINP, LOGL_DEBUG, "received message from stream\n");
+
+ msg = osmo_ipa_msg_alloc(0);
+ if (msg == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "cannot allocate message\n");
+ return 0;
+ }
+ ret = osmo_ipa_msg_recv(ofd->fd, msg);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "cannot receive message\n");
+ msgb_free(msg);
+ /* not the dummy connection, release it. */
+ if (ipa->unit != NULL)
+ abis_ipa_put(ipa);
+ return 0;
+ } else if (ret == 0) {
+ /* link has vanished, dead socket. */
+ LOGP(DLINP, LOGL_ERROR, "closed connection\n");
+ msgb_free(msg);
+ if (ipa->unit != NULL)
+ abis_ipa_put(ipa);
+
+ /* ... the stream socket releases this connection for us */
+ return 0;
+ }
+
+ hh = (struct ipa_head *) msg->data;
+ if (hh->proto == IPAC_PROTO_IPACCESS) {
+ abis_ipa_srv_rcvmsg(ipa->chan, conn, msg, type);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ ipa = osmo_stream_srv_get_data(conn);
+ if (ipa == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "no matching signalling link\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ if (hh->proto != IPAC_PROTO_OML && hh->proto != IPAC_PROTO_RSL) {
+ LOGP(DLINP, LOGL_ERROR, "wrong protocol\n");
+ return -EIO;
+ }
+ msg->dst = ipa;
+
+ s = (struct chan_abis_ipa_srv *)ipa->chan->data;
+ s->signal_msg(msg, type);
+
+ return 0;
+}
+
+static int oml_read_cb(struct osmo_stream_srv *conn)
+{
+ return read_cb(conn, CHAN_SIGN_OML);
+}
+
+static int rsl_read_cb(struct osmo_stream_srv *conn)
+{
+ return read_cb(conn, CHAN_SIGN_RSL);
+}
+
+struct osmo_chan_type chan_abis_ipa_srv = {
+ .type = OSMO_CHAN_ABIS_IPA_SRV,
+ .subtype = OSMO_SUBCHAN_STREAM,
+ .name = "A-bis IPA server",
+ .datasiz = sizeof(struct chan_abis_ipa_srv),
+ .create = chan_abis_ipa_srv_create,
+ .destroy = chan_abis_ipa_srv_destroy,
+ .open = chan_abis_ipa_srv_open,
+ .close = chan_abis_ipa_srv_close,
+ .enqueue = chan_abis_ipa_srv_enqueue,
+};