aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@gnumonks.org>2011-11-06 20:47:08 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2011-11-08 11:17:44 +0100
commit2b5d3ce7c69e1149bb2af815ab8b8534026063ca (patch)
treeadf544975a45d2bbcc7e46f3269aff101f1ed0bb /src
parentffe3cb3ef93483c45d23ba7ea7fe156d5bb4dbc6 (diff)
src: add generic channel infrastructure and A-bis IPA server support
This patch adds the generic channel infrastructure that allows to create channel of different types. Each channel has their own configuration functions. struct osmo_chan *chan; chan = osmo_chan_create(tall_example, CHAN_ABIS_IPA_SERVER); ... /* specific configuration functions per supported channel. */ osmo_chan_abis_ipa_server_set_cb_signalmsg(chan, signal_msg_cb); osmo_chan_abis_ipa_unit_add(chan, 1801, 0); /* open channel. */ osmo_chan_open(chan); The input path requires a callback to be registered. The output path is handled through: int osmo_chan_enqueue(struct osmo_chan *c, struct msgb *msg); The msg->dst must be set (it can be taken from the original message to route one reply). This patch also adds A-bis IPA server support. It has been tested with e1inp_ipa_bsc_test available in libosmo-abis.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am7
-rw-r--r--src/channel.c52
-rw-r--r--src/channel/Makefile.am7
-rw-r--r--src/channel/abis_ipa_server.c517
-rw-r--r--src/ipa.c204
-rw-r--r--src/stream.c7
6 files changed, 793 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index c501a57..06def6c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,8 +6,13 @@ 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)
+SUBDIRS = channel
+
lib_LTLIBRARIES = libosmonetif.la
-libosmonetif_la_SOURCES = datagram.c \
+libosmonetif_la_LIBADD = channel/libosmonetif-channel.la
+
+libosmonetif_la_SOURCES = channel.c \
+ datagram.c \
ipa.c \
stream.c
diff --git a/src/channel.c b/src/channel.c
new file mode 100644
index 0000000..5aedd83
--- /dev/null
+++ b/src/channel.c
@@ -0,0 +1,52 @@
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/netif/channel.h>
+
+extern struct osmo_chan_type chan_abis_ipa_server;
+
+static struct osmo_chan_type *chan_type[CHAN_MAX] = {
+ [CHAN_ABIS_IPA_SERVER] = &chan_abis_ipa_server,
+};
+
+struct osmo_chan *osmo_chan_create(void *ctx, int type_id)
+{
+ struct osmo_chan *c;
+
+ if (type_id >= CHAN_MAX)
+ return NULL;
+
+ c = talloc_zero_size(ctx, sizeof(struct osmo_chan) +
+ chan_type[type_id]->datasiz);
+ if (c == NULL)
+ return NULL;
+
+ c->ops = chan_type[type_id];
+
+ if (c->ops->create(c) < 0) {
+ talloc_free(c);
+ return NULL;
+ }
+ return c;
+}
+
+void osmo_chan_destroy(struct osmo_chan *c)
+{
+ c->ops->destroy(c);
+ talloc_free(c);
+}
+
+int osmo_chan_open(struct osmo_chan *c)
+{
+ return c->ops->open(c);
+}
+
+void osmo_chan_close(struct osmo_chan *c)
+{
+ c->ops->close(c);
+}
+
+int osmo_chan_enqueue(struct osmo_chan *c, struct msgb *msg)
+{
+ return c->ops->enqueue(c, msg);
+}
diff --git a/src/channel/Makefile.am b/src/channel/Makefile.am
new file mode 100644
index 0000000..674cacf
--- /dev/null
+++ b/src/channel/Makefile.am
@@ -0,0 +1,7 @@
+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-channel.la
+
+libosmonetif_channel_la_SOURCES = abis_ipa_server.c
diff --git a/src/channel/abis_ipa_server.c b/src/channel/abis_ipa_server.c
new file mode 100644
index 0000000..bc6bacc
--- /dev/null
+++ b/src/channel/abis_ipa_server.c
@@ -0,0 +1,517 @@
+#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>
+
+/* default IPA server ports. */
+#define IPA_TCP_PORT_OML 3002
+#define IPA_TCP_PORT_RSL 3003
+
+static void *abis_ipa_server_tall;
+
+static int oml_accept_cb(struct osmo_stream_server_link *server, int fd);
+static int rsl_accept_cb(struct osmo_stream_server_link *server, int fd);
+
+struct chan_abis_ipa_server {
+ struct osmo_stream_server_link *oml;
+ struct osmo_stream_server_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_conn {
+ struct llist_head head;
+ struct ipa_unit *unit;
+ struct osmo_chan *chan;
+ struct osmo_stream_server_conn *oml;
+ struct osmo_stream_server_conn *rsl;
+};
+
+static int chan_abis_ipa_server_create(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_server *c =
+ (struct chan_abis_ipa_server *)chan->data;
+ struct ipa_conn *ipa_conn;
+
+ /* dummy connection. */
+ ipa_conn = talloc_zero(chan->ctx, struct ipa_conn);
+ if (ipa_conn == NULL)
+ goto err;
+
+ ipa_conn->chan = chan;
+
+ c->oml = osmo_stream_server_link_create(abis_ipa_server_tall);
+ if (c->oml == NULL)
+ goto err_oml;
+
+ /* default address and port for OML. */
+ osmo_stream_server_link_set_addr(c->oml, "0.0.0.0");
+ osmo_stream_server_link_set_port(c->oml, IPA_TCP_PORT_OML);
+ osmo_stream_server_link_set_accept_cb(c->oml, oml_accept_cb);
+ osmo_stream_server_link_set_data(c->oml, ipa_conn);
+
+ c->rsl = osmo_stream_server_link_create(abis_ipa_server_tall);
+ if (c->rsl == NULL)
+ goto err_rsl;
+
+ /* default address and port for RSL. */
+ osmo_stream_server_link_set_addr(c->rsl, "0.0.0.0");
+ osmo_stream_server_link_set_port(c->rsl, IPA_TCP_PORT_RSL);
+ osmo_stream_server_link_set_accept_cb(c->rsl, rsl_accept_cb);
+ osmo_stream_server_link_set_data(c->rsl, ipa_conn);
+
+ INIT_LLIST_HEAD(&c->bts_list);
+ INIT_LLIST_HEAD(&c->conn_list);
+
+ return 0;
+err_rsl:
+ osmo_stream_server_link_destroy(c->oml);
+err_oml:
+ talloc_free(ipa_conn);
+err:
+ return -1;
+}
+
+static void chan_abis_ipa_server_destroy(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_server *c =
+ (struct chan_abis_ipa_server *)chan->data;
+ struct ipa_conn *ipa_conn =
+ osmo_stream_server_link_get_data(c->oml);
+
+ talloc_free(ipa_conn);
+ talloc_free(c->rsl);
+ talloc_free(c->oml);
+}
+
+static int chan_abis_ipa_server_open(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_server *c =
+ (struct chan_abis_ipa_server *)chan->data;
+ struct osmo_fd *ofd;
+ int ret, on = 1;
+
+ if (osmo_stream_server_link_open(c->oml) < 0)
+ goto err;
+
+ ofd = osmo_stream_server_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_server_link_open(c->rsl) < 0)
+ goto err_oml;
+
+ ofd = osmo_stream_server_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_server_link_close(c->rsl);
+err_oml:
+ osmo_stream_server_link_close(c->oml);
+err:
+ return -1;
+}
+
+static void chan_abis_ipa_server_close(struct osmo_chan *chan)
+{
+ struct chan_abis_ipa_server *c =
+ (struct chan_abis_ipa_server *)chan->data;
+
+ osmo_stream_server_link_close(c->oml);
+ osmo_stream_server_link_close(c->rsl);
+}
+
+static int chan_abis_ipa_server_enqueue(struct osmo_chan *c, struct msgb *msg)
+{
+ osmo_stream_server_conn_send(msg->dst, msg);
+ return 0;
+}
+
+void
+osmo_chan_abis_ipa_server_set_oml_addr(struct osmo_chan *c, const char *addr)
+{
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)&c->data;
+
+ osmo_stream_server_link_set_addr(s->oml, addr);
+}
+
+void
+osmo_chan_abis_ipa_server_set_oml_port(struct osmo_chan *c, uint16_t port)
+{
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)&c->data;
+
+ osmo_stream_server_link_set_port(s->oml, port);
+}
+
+void
+osmo_chan_abis_ipa_server_set_rsl_addr(struct osmo_chan *c, const char *addr)
+{
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)&c->data;
+
+ osmo_stream_server_link_set_addr(s->rsl, addr);
+}
+
+void
+osmo_chan_abis_ipa_server_set_rsl_port(struct osmo_chan *c, uint16_t port)
+{
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)&c->data;
+
+ osmo_stream_server_link_set_port(s->rsl, port);
+}
+
+void
+osmo_chan_abis_ipa_server_set_cb_signalmsg(struct osmo_chan *c,
+ void (*signal_msg)(struct msgb *msg, int type))
+{
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)&c->data;
+
+ s->signal_msg = signal_msg;
+}
+
+static int oml_read_cb(struct osmo_stream_server_conn *conn);
+
+static int oml_accept_cb(struct osmo_stream_server_link *server, int fd)
+{
+ struct osmo_stream_server_conn *conn;
+ struct ipa_conn *ipa_conn = osmo_stream_server_link_get_data(server);
+ struct osmo_fd *ofd;
+
+ conn = osmo_stream_server_conn_create(abis_ipa_server_tall,
+ server, fd,
+ oml_read_cb, NULL, ipa_conn);
+ if (conn == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "error while creating connection\n");
+ return -1;
+ }
+
+ ofd = osmo_stream_server_conn_get_ofd(conn);
+
+ /* XXX: better use chan_abis_ipa_server_enqueue. */
+ ipaccess_send_id_req(ofd->fd);
+
+ return 0;
+}
+
+static int rsl_read_cb(struct osmo_stream_server_conn *conn);
+
+static int rsl_accept_cb(struct osmo_stream_server_link *server, int fd)
+{
+ struct osmo_stream_server_conn *conn;
+ struct ipa_conn *ipa_conn = osmo_stream_server_link_get_data(server);
+ struct osmo_fd *ofd;
+
+ conn = osmo_stream_server_conn_create(abis_ipa_server_tall,
+ server, fd,
+ rsl_read_cb, NULL, ipa_conn);
+ if (conn == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "error while creating connection\n");
+ return -1;
+ }
+
+ ofd = osmo_stream_server_conn_get_ofd(conn);
+
+ /* XXX: better use chan_abis_ipa_server_enqueue. */
+ ipaccess_send_id_req(ofd->fd);
+
+ return 0;
+}
+
+static struct ipa_unit *
+osmo_ipa_unit_find(struct osmo_chan *c, uint16_t site_id, uint16_t bts_id)
+{
+ struct ipa_unit *unit;
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)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_chan_abis_ipa_unit_add(struct osmo_chan *c,
+ uint16_t site_id, uint16_t bts_id)
+{
+ struct ipa_unit *unit;
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)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_conn *
+osmo_ipa_conn_alloc(struct ipa_unit *unit, struct osmo_chan *chan)
+{
+ struct ipa_conn *ipa_conn;
+
+ ipa_conn = talloc_zero(chan->ctx, struct ipa_conn);
+ if (ipa_conn == NULL)
+ return NULL;
+
+ ipa_conn->unit = unit;
+ ipa_conn->chan = chan;
+
+ return ipa_conn;
+}
+
+static struct ipa_conn *
+osmo_ipa_conn_add(struct ipa_unit *unit, struct osmo_chan *chan)
+{
+ struct ipa_conn *ipa_conn;
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)chan->data;
+
+ ipa_conn = osmo_ipa_conn_alloc(unit, chan);
+ if (ipa_conn == NULL)
+ return NULL;
+
+ llist_add(&ipa_conn->head, &s->conn_list);
+
+ return ipa_conn;
+}
+
+static struct ipa_conn *
+osmo_ipa_conn_find(struct ipa_unit *unit, struct osmo_chan *chan)
+{
+ struct ipa_conn *ipa_conn;
+ struct chan_abis_ipa_server *s =
+ (struct chan_abis_ipa_server *)chan->data;
+
+ llist_for_each_entry(ipa_conn, &s->conn_list, head) {
+ if (ipa_conn->unit->site_id == unit->site_id &&
+ ipa_conn->unit->bts_id == unit->bts_id) {
+ return ipa_conn;
+ }
+ }
+ return NULL;
+}
+
+static void
+osmo_ipa_conn_put(struct ipa_conn *ipa_conn)
+{
+ llist_del(&ipa_conn->head);
+ osmo_stream_server_conn_destroy(ipa_conn->oml);
+ osmo_stream_server_conn_destroy(ipa_conn->rsl);
+ talloc_free(ipa_conn);
+}
+
+static int
+abis_ipa_server_rcvmsg(struct osmo_chan *c,
+ struct osmo_stream_server_conn *conn,
+ struct msgb *msg, int type)
+{
+ struct tlv_parsed tlvp;
+ uint8_t msg_type = *(msg->l2h);
+ struct osmo_fd *ofd = osmo_stream_server_conn_get_ofd(conn);
+ char *unitid;
+ int len, ret;
+
+ /* Handle IPA PING, PONG and ID_ACK messages. */
+ if (osmo_ipa_rcvmsg_base(msg, ofd))
+ return 0;
+
+ if (msg_type == IPAC_MSGT_ID_RESP) {
+ struct ipa_unit *unit;
+ struct ipa_conn *ipa_conn;
+ 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_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_conn = osmo_ipa_conn_find(unit, c);
+ if (ipa_conn == NULL) {
+ ipa_conn = osmo_ipa_conn_add(unit, c);
+ if (ipa_conn == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "OOM\n");
+ return 0;
+ }
+ osmo_stream_server_conn_set_data(conn, ipa_conn);
+ }
+
+ if (type == CHAN_SIGN_OML) {
+ if (ipa_conn->oml) {
+ /* link already exists, kill it. */
+ osmo_stream_server_conn_destroy(ipa_conn->oml);
+ return 0;
+ }
+ ipa_conn->oml = conn;
+ } else if (type == CHAN_SIGN_RSL) {
+ if (!ipa_conn->oml) {
+ /* no OML link? Restart from scratch. */
+ osmo_ipa_conn_put(ipa_conn);
+ return 0;
+ }
+ if (ipa_conn->rsl) {
+ /* RSL link already exists, kill it. */
+ osmo_stream_server_conn_destroy(ipa_conn->rsl);
+ return 0;
+ }
+ ipa_conn->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_server_conn *conn, int type)
+{
+ int ret;
+ struct msgb *msg;
+ struct osmo_fd *ofd = osmo_stream_server_conn_get_ofd(conn);
+ struct ipa_conn *ipa_conn = osmo_stream_server_conn_get_data(conn);
+ struct chan_abis_ipa_server *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_conn->unit != NULL)
+ osmo_ipa_conn_put(ipa_conn);
+ return 0;
+ } else if (ret == 0) {
+ /* link has vanished, dead socket. */
+ LOGP(DLINP, LOGL_ERROR, "closed connection\n");
+ msgb_free(msg);
+ if (ipa_conn->unit != NULL)
+ osmo_ipa_conn_put(ipa_conn);
+ return 0;
+ }
+
+ hh = (struct ipa_head *) msg->data;
+ if (hh->proto == IPAC_PROTO_IPACCESS) {
+ abis_ipa_server_rcvmsg(ipa_conn->chan, conn, msg, type);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ ipa_conn = osmo_stream_server_conn_get_data(conn);
+ if (ipa_conn == 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_conn;
+
+ s = (struct chan_abis_ipa_server *)ipa_conn->chan->data;
+ s->signal_msg(msg, type);
+
+ return 0;
+}
+
+static int oml_read_cb(struct osmo_stream_server_conn *conn)
+{
+ return read_cb(conn, CHAN_SIGN_OML);
+}
+
+static int rsl_read_cb(struct osmo_stream_server_conn *conn)
+{
+ return read_cb(conn, CHAN_SIGN_RSL);
+}
+
+struct osmo_chan_type chan_abis_ipa_server = {
+ .type = CHAN_ABIS_IPA_SERVER,
+ .datasiz = sizeof(struct chan_abis_ipa_server),
+ .create = chan_abis_ipa_server_create,
+ .destroy = chan_abis_ipa_server_destroy,
+ .open = chan_abis_ipa_server_open,
+ .close = chan_abis_ipa_server_close,
+ .enqueue = chan_abis_ipa_server_enqueue,
+};
diff --git a/src/ipa.c b/src/ipa.c
index 27df56f..a4c3a5f 100644
--- a/src/ipa.c
+++ b/src/ipa.c
@@ -1,13 +1,69 @@
#include <stdlib.h>
#include <arpa/inet.h>
+#include <string.h>
+#include <unistd.h>
#include <errno.h>
+#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/gsm/tlv.h>
+
+#include <osmocom/netif/channel.h>
#include <osmocom/netif/ipa.h>
#define IPA_ALLOC_SIZE 1200
+/*
+ * Common propietary IPA messages:
+ * - PONG: in reply to PING.
+ * - ID_REQUEST: first messages once OML has been established.
+ * - ID_ACK: in reply to ID_ACK.
+ */
+const uint8_t ipa_pong_msg[] = {
+ 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
+};
+
+const uint8_t ipa_id_ack_msg[] = {
+ 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK
+};
+
+const uint8_t ipa_id_req_msg[] = {
+ 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
+ 0x01, IPAC_IDTAG_UNIT,
+ 0x01, IPAC_IDTAG_MACADDR,
+ 0x01, IPAC_IDTAG_LOCATION1,
+ 0x01, IPAC_IDTAG_LOCATION2,
+ 0x01, IPAC_IDTAG_EQUIPVERS,
+ 0x01, IPAC_IDTAG_SWVERSION,
+ 0x01, IPAC_IDTAG_UNITNAME,
+ 0x01, IPAC_IDTAG_SERNR,
+};
+
+static const char *idtag_names[] = {
+ [IPAC_IDTAG_SERNR] = "Serial_Number",
+ [IPAC_IDTAG_UNITNAME] = "Unit_Name",
+ [IPAC_IDTAG_LOCATION1] = "Location_1",
+ [IPAC_IDTAG_LOCATION2] = "Location_2",
+ [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
+ [IPAC_IDTAG_SWVERSION] = "Software_Version",
+ [IPAC_IDTAG_IPADDR] = "IP_Address",
+ [IPAC_IDTAG_MACADDR] = "MAC_Address",
+ [IPAC_IDTAG_UNIT] = "Unit_ID",
+};
+
+const char *ipaccess_idtag_name(uint8_t tag)
+{
+ if (tag >= ARRAY_SIZE(idtag_names))
+ return "unknown";
+
+ return idtag_names[tag];
+}
+
+
struct msgb *osmo_ipa_msg_alloc(int headroom)
{
struct msgb *msg;
@@ -68,3 +124,151 @@ int osmo_ipa_msg_recv(int fd, struct msgb *msg)
msgb_put(msg, ret);
return ret;
}
+
+int osmo_ipa_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
+{
+ uint8_t t_len;
+ uint8_t t_tag;
+ uint8_t *cur = buf;
+
+ memset(dec, 0, sizeof(*dec));
+
+ while (len >= 2) {
+ len -= 2;
+ t_len = *cur++;
+ t_tag = *cur++;
+
+ if (t_len > len + 1) {
+ LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len);
+ return -EINVAL;
+ }
+
+ DEBUGPC(DLMI, "%s='%s' ", ipaccess_idtag_name(t_tag), cur);
+
+ dec->lv[t_tag].len = t_len;
+ dec->lv[t_tag].val = cur;
+
+ cur += t_len;
+ len -= t_len;
+ }
+ return 0;
+}
+
+int osmo_ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
+{
+ unsigned long ul;
+ char *endptr;
+ const char *nptr;
+
+ nptr = str;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->site_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->bts_id = ul & 0xffff;
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->trx_id = ul & 0xffff;
+
+ return 0;
+}
+
+static int ipaccess_send(int fd, const void *msg, size_t msglen)
+{
+ int ret;
+
+ ret = write(fd, msg, msglen);
+ if (ret < 0)
+ return ret;
+ if (ret < msglen) {
+ LOGP(DLINP, LOGL_ERROR, "ipaccess_send: short write\n");
+ return -EIO;
+ }
+ return ret;
+}
+
+int ipaccess_send_pong(int fd)
+{
+ return ipaccess_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
+}
+
+int ipaccess_send_id_ack(int fd)
+{
+ return ipaccess_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
+}
+
+int ipaccess_send_id_req(int fd)
+{
+ return ipaccess_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
+}
+
+/* base handling of the ip.access protocol */
+int osmo_ipa_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
+{
+ int ipa_ccm = 0;
+ uint8_t msg_type = *(msg->l2h);
+ int ret = 0;
+
+ switch (msg_type) {
+ case IPAC_MSGT_PING:
+ ipa_ccm = 1;
+ ret = ipaccess_send_pong(bfd->fd);
+ break;
+ case IPAC_MSGT_PONG:
+ DEBUGP(DLMI, "PONG!\n");
+ ipa_ccm = 1;
+ break;
+ case IPAC_MSGT_ID_ACK:
+ DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
+ ipa_ccm = 1;
+ ret = ipaccess_send_id_ack(bfd->fd);
+ break;
+ }
+ return ipa_ccm;
+}
+
+int ipaccess_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
+{
+ unsigned long ul;
+ char *endptr;
+ const char *nptr;
+
+ nptr = str;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->site_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->bts_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->trx_id = ul & 0xffff;
+
+ return 0;
+}
diff --git a/src/stream.c b/src/stream.c
index c87f999..b32f9b5 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -510,6 +510,13 @@ osmo_stream_server_conn_create(void *ctx, struct osmo_stream_server_link *link,
return conn;
}
+void
+osmo_stream_server_conn_set_data(struct osmo_stream_server_conn *conn,
+ void *data)
+{
+ conn->data = data;
+}
+
void *osmo_stream_server_conn_get_data(struct osmo_stream_server_conn *link)
{
return link->data;