diff options
author | Pablo Neira Ayuso <pablo@gnumonks.org> | 2012-08-19 01:05:54 +0200 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@gnumonks.org> | 2012-08-19 01:06:57 +0200 |
commit | 060e46ea6dd6b25d851baaa8757b3531a37edb1a (patch) | |
tree | 18314a68737517b0a6923547444ec99617b53276 /src/channel/abis | |
parent | 775c1de0891da2d3b16efada5ee8b99f96d045a1 (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.am | 8 | ||||
-rw-r--r-- | src/channel/abis/ipa_stream_client.c | 370 | ||||
-rw-r--r-- | src/channel/abis/ipa_stream_server.c | 522 |
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, +}; |