diff options
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | examples/Makefile.am | 2 | ||||
-rw-r--r-- | examples/channel/Makefile.am | 9 | ||||
-rw-r--r-- | examples/channel/abis_ipa_server.c | 76 | ||||
-rw-r--r-- | include/osmocom/netif/Makefile.am | 5 | ||||
-rw-r--r-- | include/osmocom/netif/channel.h | 49 | ||||
-rw-r--r-- | include/osmocom/netif/channel/Makefile.am | 3 | ||||
-rw-r--r-- | include/osmocom/netif/channel/abis_ipa_server.h | 14 | ||||
-rw-r--r-- | include/osmocom/netif/ipa.h | 42 | ||||
-rw-r--r-- | include/osmocom/netif/stream.h | 2 | ||||
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/channel.c | 52 | ||||
-rw-r--r-- | src/channel/Makefile.am | 7 | ||||
-rw-r--r-- | src/channel/abis_ipa_server.c | 517 | ||||
-rw-r--r-- | src/ipa.c | 204 | ||||
-rw-r--r-- | src/stream.c | 7 |
16 files changed, 988 insertions, 11 deletions
diff --git a/configure.ac b/configure.ac index 5e6e497..692688a 100644 --- a/configure.ac +++ b/configure.ac @@ -48,6 +48,9 @@ AC_OUTPUT( include/Makefile include/osmocom/Makefile include/osmocom/netif/Makefile + include/osmocom/netif/channel/Makefile src/Makefile + src/channel/Makefile examples/Makefile + examples/channel/Makefile Makefile) diff --git a/examples/Makefile.am b/examples/Makefile.am index be4ecde..173b620 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -2,6 +2,8 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) +SUBDIRS = channel + noinst_PROGRAMS = ipa-stream-client \ ipa-stream-server \ lapd-over-datagram-user \ diff --git a/examples/channel/Makefile.am b/examples/channel/Makefile.am new file mode 100644 index 0000000..1906465 --- /dev/null +++ b/examples/channel/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +noinst_PROGRAMS = abis_ipa_server + +abis_ipa_server_SOURCES = abis_ipa_server.c +abis_ipa_server_LDADD = $(top_builddir)/src/libosmonetif.la \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) diff --git a/examples/channel/abis_ipa_server.c b/examples/channel/abis_ipa_server.c new file mode 100644 index 0000000..014975d --- /dev/null +++ b/examples/channel/abis_ipa_server.c @@ -0,0 +1,76 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <osmocom/core/select.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/application.h> + +#include <osmocom/netif/channel.h> +#include <osmocom/netif/channel/abis_ipa_server.h> + +static void *tall_example; + +#define DEXAMPLE 0 + +struct log_info_cat example_cat[] = { + [DEXAMPLE] = { + .name = "DEXAMPLE", + .description = "example", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +const struct log_info example_log_info = { + .filter_fn = NULL, + .cat = example_cat, + .num_cat = ARRAY_SIZE(example_cat), +}; + +void sighandler(int foo) +{ + LOGP(DEXAMPLE, LOGL_NOTICE, "closing test.\n"); + exit(EXIT_SUCCESS); +} + +static void signal_msg_cb(struct msgb *msg, int type) +{ + LOGP(DEXAMPLE, LOGL_NOTICE, "received signal message\n"); +} + +static struct osmo_chan *chan; + +int main(void) +{ + tall_example = talloc_named_const(NULL, 1, "example"); + + osmo_init_logging(&example_log_info); + log_set_log_level(osmo_stderr_target, LOGL_DEBUG); + + /* create channel. */ + chan = osmo_chan_create(tall_example, CHAN_ABIS_IPA_SERVER); + if (chan == NULL) { + LOGP(DEXAMPLE, LOGL_ERROR, "Cannot create A-bis IPA server\n"); + exit(EXIT_FAILURE); + } + + /* set specific parameters (depends on channel type). */ + osmo_chan_abis_ipa_server_set_cb_signalmsg(chan, signal_msg_cb); + osmo_chan_abis_ipa_unit_add(chan, 1801, 0); + + /* open channel. */ + if (osmo_chan_open(chan) < 0) { + LOGP(DEXAMPLE, LOGL_ERROR, "Cannot create A-bis IPA server\n"); + exit(EXIT_FAILURE); + } + + LOGP(DEXAMPLE, LOGL_NOTICE, "Entering main loop\n"); + + while(1) { + osmo_select_main(0); + } +} diff --git a/include/osmocom/netif/Makefile.am b/include/osmocom/netif/Makefile.am index 4e63846..6d2980d 100644 --- a/include/osmocom/netif/Makefile.am +++ b/include/osmocom/netif/Makefile.am @@ -1,4 +1,7 @@ -osmonetif_HEADERS = datagram.h \ +SUBDIRS = channel + +osmonetif_HEADERS = channel.h \ + datagram.h \ ipa.h \ stream.h diff --git a/include/osmocom/netif/channel.h b/include/osmocom/netif/channel.h new file mode 100644 index 0000000..65955e8 --- /dev/null +++ b/include/osmocom/netif/channel.h @@ -0,0 +1,49 @@ +#ifndef _CHANNEL_H_ +#define _CHANNEL_H_ + +#include <stdint.h> + +#define CHAN_SIGN_OML 0 +#define CHAN_SIGN_RSL 1 + +enum { + CHAN_NONE, + CHAN_ABIS_IPA_SERVER, + CHAN_MAX, +}; + +#define CHAN_F_DEFAULT (1 << 0) +#define CHAN_F_BUFFERED (1 << 1) +#define CHAN_F_STREAM (1 << 2) +#define CHAN_F_ERRORS (1 << 3) +#define CHAN_F_MAX (1 << 4) + +struct osmo_chan; +struct msgb; + +struct osmo_chan_type { + int type; + int datasiz; + + int (*create)(struct osmo_chan *chan); + void (*destroy)(struct osmo_chan *chan); + int (*open)(struct osmo_chan *chan); + void (*close)(struct osmo_chan *chan); + int (*enqueue)(struct osmo_chan *chan, struct msgb *msg); +}; + +struct osmo_chan { + void *ctx; + struct osmo_chan_type *ops; + char data[0]; +}; + +struct osmo_chan *osmo_chan_create(void *ctx, int type); +void osmo_chan_destroy(struct osmo_chan *c); + +int osmo_chan_open(struct osmo_chan *c); +void osmo_chan_close(struct osmo_chan *c); + +int osmo_chan_enqueue(struct osmo_chan *c, struct msgb *msg); + +#endif /* _CHANNEL_H_ */ diff --git a/include/osmocom/netif/channel/Makefile.am b/include/osmocom/netif/channel/Makefile.am new file mode 100644 index 0000000..4f2b876 --- /dev/null +++ b/include/osmocom/netif/channel/Makefile.am @@ -0,0 +1,3 @@ +osmonetif_channel_HEADERS = abis_ipa_server.h + +osmonetif_channeldir = $(includedir)/osmocom/netif/channel diff --git a/include/osmocom/netif/channel/abis_ipa_server.h b/include/osmocom/netif/channel/abis_ipa_server.h new file mode 100644 index 0000000..0338021 --- /dev/null +++ b/include/osmocom/netif/channel/abis_ipa_server.h @@ -0,0 +1,14 @@ +#ifndef _ABIS_IPA_SERVER_H_ +#define _ABIS_IPA_SERVER_H_ + +void osmo_chan_abis_ipa_server_set_oml_addr(struct osmo_chan *c, const char *addr); +void osmo_chan_abis_ipa_server_set_oml_port(struct osmo_chan *c, uint16_t port); + +void osmo_chan_abis_ipa_server_set_rsl_addr(struct osmo_chan *c, const char *addr); +void osmo_chan_abis_ipa_server_set_rsl_port(struct osmo_chan *c, uint16_t port); + +void osmo_chan_abis_ipa_server_set_cb_signalmsg(struct osmo_chan *c, void (*signal_msg)(struct msgb *msg, int type)); + +int osmo_chan_abis_ipa_unit_add(struct osmo_chan *c, uint16_t site_id, uint16_t bts_id); + +#endif diff --git a/include/osmocom/netif/ipa.h b/include/osmocom/netif/ipa.h index 5b53b3a..152ea24 100644 --- a/include/osmocom/netif/ipa.h +++ b/include/osmocom/netif/ipa.h @@ -34,19 +34,43 @@ struct ipa_head_ext { #define IPAC_MSGT_SCCP_OLD 0xff /* OpenBSC extension */ enum ipaccess_id_tags { - IPAC_IDTAG_SERNR = 0x00, - IPAC_IDTAG_UNITNAME = 0x01, - IPAC_IDTAG_LOCATION1 = 0x02, - IPAC_IDTAG_LOCATION2 = 0x03, - IPAC_IDTAG_EQUIPVERS = 0x04, - IPAC_IDTAG_SWVERSION = 0x05, - IPAC_IDTAG_IPADDR = 0x06, - IPAC_IDTAG_MACADDR = 0x07, - IPAC_IDTAG_UNIT = 0x08, + IPAC_IDTAG_SERNR = 0x00, + IPAC_IDTAG_UNITNAME = 0x01, + IPAC_IDTAG_LOCATION1 = 0x02, + IPAC_IDTAG_LOCATION2 = 0x03, + IPAC_IDTAG_EQUIPVERS = 0x04, + IPAC_IDTAG_SWVERSION = 0x05, + IPAC_IDTAG_IPADDR = 0x06, + IPAC_IDTAG_MACADDR = 0x07, + IPAC_IDTAG_UNIT = 0x08, }; struct msgb *osmo_ipa_msg_alloc(int headroom); void osmo_ipa_msg_push_header(struct msgb *msg, uint8_t proto); int osmo_ipa_msg_recv(int fd, struct msgb *msg); +struct ipaccess_unit { + uint16_t site_id; + uint16_t bts_id; + uint16_t trx_id; + char *unit_name; + char *equipvers; + char *swversion; + uint8_t mac_addr[6]; + char *location1; + char *location2; + char *serno; +}; + +struct osmo_fd; +struct tlv_parsed; + +int osmo_ipa_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd); +int osmo_ipa_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len); +int osmo_ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data); + +int ipaccess_send_pong(int fd); +int ipaccess_send_id_ack(int fd); +int ipaccess_send_id_req(int fd); + #endif diff --git a/include/osmocom/netif/stream.h b/include/osmocom/netif/stream.h index a43516e..fe04088 100644 --- a/include/osmocom/netif/stream.h +++ b/include/osmocom/netif/stream.h @@ -23,6 +23,8 @@ void *osmo_stream_server_conn_get_data(struct osmo_stream_server_conn *conn); struct osmo_fd *osmo_stream_server_conn_get_ofd(struct osmo_stream_server_conn *link); void osmo_stream_server_conn_destroy(struct osmo_stream_server_conn *conn); +void osmo_stream_server_conn_set_data(struct osmo_stream_server_conn *conn, void *data); + void osmo_stream_server_conn_send(struct osmo_stream_server_conn *conn, struct msgb *msg); int osmo_stream_server_conn_recv(struct osmo_stream_server_conn *conn, struct msgb *msg); 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, +}; @@ -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; |