diff options
author | Tobias Engel <tobias@ccc.de> | 2011-06-30 10:49:07 +0200 |
---|---|---|
committer | Tobias Engel <tobias@ccc.de> | 2012-03-08 14:17:16 +0100 |
commit | 5029ac4b3082ca565cde2cdaf36920c0ac3cfb31 (patch) | |
tree | 53a6e44b989e2bcbdffb2a09f3ae88ee4dc9e7c0 | |
parent | fa9d56ed4699d6284f102fae3d73b628367a0d70 (diff) |
- filter-abis interface for openbsc
-rw-r--r-- | openbsc/include/openbsc/filter_abis.h | 56 | ||||
-rw-r--r-- | openbsc/src/Makefile.am | 3 | ||||
-rw-r--r-- | openbsc/src/libbsc/Makefile.am | 1 | ||||
-rw-r--r-- | openbsc/src/libbsc/abis_rsl.c | 8 | ||||
-rw-r--r-- | openbsc/src/libbsc/bsc_init.c | 5 | ||||
-rw-r--r-- | openbsc/src/libbsc/filter_abis.c | 395 | ||||
-rw-r--r-- | openbsc/src/libmsc/vty_interface_layer3.c | 2 | ||||
-rw-r--r-- | openbsc/tests/Makefile.am | 3 |
8 files changed, 469 insertions, 4 deletions
diff --git a/openbsc/include/openbsc/filter_abis.h b/openbsc/include/openbsc/filter_abis.h new file mode 100644 index 000000000..89a8f6771 --- /dev/null +++ b/openbsc/include/openbsc/filter_abis.h @@ -0,0 +1,56 @@ +#ifndef FILTER_INTERFACE_H +#define FILTER_INTERFACE_H + +#include <osmocom/core/msgb.h> +#include <osmocom/core/select.h> + + +struct filter_connection { + void *priv; + struct osmo_fd fd; +}; + +#define FILTER_DOWNLINK_MSG 0 // BTS => MS +#define FILTER_UPLINK_MSG 1 // MS => BTS +#define FILTER_SILENT_CALL 2 + +struct filter_head { + uint8_t msg_type; + uint16_t len; /* network byte order, num of remaining bytes */ + uint8_t data[0]; +} __attribute__ ((packed)); + +struct filter_msg { + void *priv1; /* trx from msgb */ + void *priv2; /* lchan from msgb */ + uint8_t data[0]; +} __attribute__ ((packed)); + +#define FILTER_SUBSCR_ID_TYPE_EXT 0 +#define FILTER_SUBSCR_ID_TYPE_IMSI 1 +#define FILTER_SUBSCR_ID_TYPE_TMSI 2 +#define FILTER_SUBSCR_ID_TYPE_ID 3 + +struct filter_silentcall_req { + uint8_t activate; + uint8_t channel_type; + uint8_t subscr_id_type; + uint8_t subscr_id[0]; +} __attribute__ ((packed)); + +struct filter_silentcall_resp { + void *priv1; /* trx from msgb */ + void *priv2; /* lchan from msgb */ + uint8_t chan_nr; /* chan_nr for abis rsl hdr */ + uint8_t error; +} __attribute__ ((packed)); + +int filter_init(void *tall_ctx, void *priv, int port); +int filter_is_active(); +int filter_send_msg(struct msgb *msg, int msg_type); + +extern int _abis_rsl_sendmsg(struct msgb *msg); +extern int _abis_rsl_rcvmsg(struct msgb *msg); + + +#endif diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index 805fb1cd9..b778c36a4 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -2,7 +2,8 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) -SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libctrl osmo-nitb osmo-bsc_mgcp utils ipaccess libgb gprs +#SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libctrl osmo-nitb osmo-bsc_mgcp utils ipaccess libgb gprs +SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libctrl osmo-nitb osmo-bsc_mgcp utils libgb gprs # Conditional modules if BUILD_NAT diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am index 4cf1fabdb..eae983259 100644 --- a/openbsc/src/libbsc/Makefile.am +++ b/openbsc/src/libbsc/Makefile.am @@ -7,6 +7,7 @@ noinst_LIBRARIES = libbsc.a libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ abis_om2000.c abis_om2000_vty.c \ abis_rsl.c bsc_rll.c \ + filter_abis.c \ paging.c \ bts_ericsson_rbs2000.c \ bts_ipaccess_nanobts.c \ diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c index b997cf0ee..f89a966bd 100644 --- a/openbsc/src/libbsc/abis_rsl.c +++ b/openbsc/src/libbsc/abis_rsl.c @@ -39,6 +39,7 @@ #include <openbsc/meas_rep.h> #include <openbsc/rtp_proxy.h> #include <osmocom/abis/e1_input.h> +#include <openbsc/filter_abis.h> #include <osmocom/gsm/rsl.h> #include <osmocom/core/talloc.h> @@ -1900,7 +1901,7 @@ static int abis_rsl_rx_ipacc(struct msgb *msg) /* Entry-point where L2 RSL from BTS enters */ -int abis_rsl_rcvmsg(struct msgb *msg) +int _abis_rsl_rcvmsg(struct msgb *msg) { struct abis_rsl_common_hdr *rslh; int rc = 0; @@ -1946,6 +1947,11 @@ int abis_rsl_rcvmsg(struct msgb *msg) return rc; } +int abis_rsl_rcvmsg(struct msgb *msg) +{ + return (filter_is_active() ? filter_send_msg(msg, FILTER_UPLINK_MSG) : _abis_rsl_rcvmsg(msg)); +} + int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, uint8_t cb_command, const uint8_t *data, int len) { diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c index 6d00374e0..e545b3489 100644 --- a/openbsc/src/libbsc/bsc_init.c +++ b/openbsc/src/libbsc/bsc_init.c @@ -35,6 +35,7 @@ #include <openbsc/ipaccess.h> #include <osmocom/gsm/sysinfo.h> #include <openbsc/e1_config.h> +#include <openbsc/filter_abis.h> /* global pointer to the gsm network data structure */ extern struct gsm_network *bsc_gsmnet; @@ -498,6 +499,10 @@ int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), if (rc < 0) return rc; + rc = filter_init(tall_bsc_ctx, bsc_gsmnet, 0xab15); + if (rc < 0) + return rc; + osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); diff --git a/openbsc/src/libbsc/filter_abis.c b/openbsc/src/libbsc/filter_abis.c new file mode 100644 index 000000000..8cb29eb5b --- /dev/null +++ b/openbsc/src/libbsc/filter_abis.c @@ -0,0 +1,395 @@ +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <openbsc/filter_abis.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/signal.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/silent_call.h> + +// copied from abis_rsl.c +#define RSL_ALLOC_SIZE 1024 +#define RSL_ALLOC_HEADROOM 128 + +extern struct gsm_network *bsc_gsmnet; + +static void *tall_filter_ctx; +static struct filter_connection *active_connection = NULL; +static struct llist_head tx_msg_list; + +static int filter_new_connection(struct osmo_fd *fd, unsigned int what); + +static struct osmo_fd server_socket = { + .when = BSC_FD_READ, + .cb = filter_new_connection, + .priv_nr = 0, +}; + +int filter_is_active() { + // LOGP(0, LOGL_DEBUG, "filter %sactive\n", (active_connection ? "" : "in")); + return(active_connection ? 1 : 0); +} + +static int silentcall_cbfn(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct scall_signal_data *sigdata = signal_data; + + LOGP(0, LOGL_DEBUG, "silentcall callback called\n"); + + if(!active_connection) + return 0; + + struct msgb *newmsg = msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, "RSL"); + if (!newmsg) + return -ENOMEM; + + struct filter_head *fh; + struct filter_silentcall_resp *fm; + + fh = (struct filter_head *) newmsg->data; + msgb_put(newmsg, sizeof(*fh)); + fh->len = htons(sizeof(struct filter_silentcall_resp)); + fh->msg_type = FILTER_SILENT_CALL; + + fm = (struct filter_silentcall_resp *) newmsg->tail; + msgb_put(newmsg, sizeof(*fm)); + + switch (signal) { + case S_SCALL_SUCCESS: + fm->priv1 = sigdata->conn->lchan->ts->trx; + fm->priv2 = sigdata->conn->lchan; + fm->chan_nr = lchan2chan_nr(sigdata->conn->lchan); + fm->error = 0; + break; + case S_SCALL_EXPIRED: + fm->priv1 = NULL; + fm->priv2 = NULL; + fm->chan_nr = 0; + fm->error = 1; + break; + } + + msgb_enqueue(&tx_msg_list, newmsg); + active_connection->fd.when |= BSC_FD_WRITE; + + return 0; +} + +int filter_init(void *tall_ctx, void *priv, int port) +{ + struct sockaddr_in sock_addr; + int fd, rc, on = 1; + + tall_filter_ctx = talloc_named_const(tall_ctx, 1, + "filter_connection"); + + fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (fd < 0) { + LOGP(0, LOGL_ERROR, "Filter interface socket creation failed\n"); + return fd; + } + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_port = htons(port); + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + rc = bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (rc < 0) { + LOGP(0, LOGL_ERROR, "Filter interface failed to bind\n"); + close(fd); + return rc; + } + + rc = listen(fd, 0); + if (rc < 0) { + LOGP(0, LOGL_ERROR, "Filter interface failed to listen\n"); + close(fd); + return rc; + } + + server_socket.data = priv; + server_socket.fd = fd; + osmo_fd_register(&server_socket); + + INIT_LLIST_HEAD(&tx_msg_list); + + osmo_signal_register_handler(SS_SCALL, silentcall_cbfn, NULL); + + //LOGP(0, LOGL_DEBUG, "filter_init done\n"); + + return 0; +} + +int filter_send_msg(struct msgb *msg, int msg_type) { + struct msgb *newmsg = msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, "RSL"); + struct filter_head *fh; + struct filter_msg *fm; + int len = msgb_l2len(msg); + + LOGP(0, LOGL_DEBUG, "filter_send_msg called, msg has %d bytes, type %d\n", len, msg_type); + + if (!newmsg) + return -ENOMEM; + + if (len < 0 || RSL_ALLOC_SIZE < len + sizeof(*fh)) { + LOGP(0, LOGL_ERROR, "Can not send this packet. %d avail\n", RSL_ALLOC_SIZE); + msgb_free(msg); + msgb_free(newmsg); + return -EIO; + } + + fh = (struct filter_head *) newmsg->data; + msgb_put(newmsg, sizeof(*fh)); + + fh->len = htons(len + sizeof(struct filter_msg)); + fh->msg_type = msg_type; + + fm = (struct filter_msg *) newmsg->tail; + msgb_put(newmsg, sizeof(*fm)); + fm->priv1 = msg->trx; + fm->priv2 = msg->lchan; + + newmsg->l2h = newmsg->tail; + + memcpy(newmsg->l2h, msg->l2h, len); + msgb_put(newmsg, len); + + msgb_free(msg); + + msgb_enqueue(&tx_msg_list, newmsg); + active_connection->fd.when |= BSC_FD_WRITE; + + return 0; +} + +static int filter_close_client() +{ + //LOGP(0, LOGL_DEBUG, "filter_close_client called\n"); + + if(active_connection) { + + struct filter_connection *conn = active_connection; + struct osmo_fd *fd = &conn->fd; + + LOGP(0, LOGL_DEBUG, "freeing resources\n"); + + active_connection = NULL; + + close(fd->fd); + osmo_fd_unregister(fd); + + talloc_free(conn); + + } + return 0; +} + +static struct msgb *filter_read_msg_from_socket(struct osmo_fd *bfd, int *error) +{ + struct msgb *msg = msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, "RSL"); + struct filter_head *fh; + int len, ret = 0; + + LOGP(0, LOGL_DEBUG, "filter_read_msg_from_socket called\n"); + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + /* first read our header */ + fh = (struct filter_head *) msg->data; + ret = recv(bfd->fd, msg->data, sizeof(*fh), 0); + if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret != sizeof(*fh)) { + if (errno != EAGAIN) + LOGP(0, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } + + msgb_put(msg, ret); + + /* then read the length as specified in header */ + len = ntohs(fh->len); + + //LOGP(0, LOGL_DEBUG, "msg has %d bytes\n", len); + + if (len < 0 || RSL_ALLOC_SIZE < len + sizeof(*fh)) { + LOGP(0, LOGL_ERROR, "Can not read this packet. %d avail\n", RSL_ALLOC_SIZE); + msgb_free(msg); + *error = -EIO; + return NULL; + } + + ret = recv(bfd->fd, msg->tail, len, 0); + if (ret < len) { + LOGP(0, LOGL_ERROR, "short read! Got %d from %d\n", ret, len); + msgb_free(msg); + *error = -EIO; + return NULL; + } + + msgb_put(msg, ret); + + return msg; +} + +static int process_silentcall_message(struct filter_silentcall_req *sc_req, struct osmo_fd *fd) { + int rc = -1; + + LOGP(0, LOGL_DEBUG, "rcvd FILTER_SILENT_CALL msg for ext %s\n", (char *) sc_req->subscr_id); + + struct gsm_subscriber *subscr = subscr_get_by_extension(bsc_gsmnet, (char *) sc_req->subscr_id); + if (!subscr) { + LOGP(0, LOGL_ERROR, "subscriber for ext %s not found\n", (char *) sc_req->subscr_id); + return rc; + } + + if(sc_req->activate) { + LOGP(0, LOGL_DEBUG, "starting silent call\n"); + rc = gsm_silent_call_start(subscr, (void *) fd, sc_req->channel_type); + } else { + LOGP(0, LOGL_DEBUG, "stopping silent call\n"); + rc = gsm_silent_call_stop(subscr); + } + + subscr_put(subscr); + + if (rc <= 0) { + LOGP(0, LOGL_DEBUG, "silent call failed: %d\n", rc); + return rc; + } + + return rc; +} + +static int route_message(struct msgb *msg) { + struct filter_head *fh = (struct filter_head *) msg->data; + int rc = -1; + + msgb_pull(msg, sizeof(struct filter_head)); + struct filter_msg *fm = (struct filter_msg *) msg->data; + msg->trx = fm->priv1; + msg->lchan = fm->priv2; + + // abis_rsl_sendmsg expects msgb->data == msgb->l2h + msgb_pull(msg, sizeof(struct filter_msg)); + msg->l2h = msg->data; + + if(fh->msg_type == FILTER_UPLINK_MSG) { + LOGP(0, LOGL_DEBUG, "routing uplink msg to _abis_rsl_rcvmsg\n"); + rc = _abis_rsl_rcvmsg(msg); + } else if(fh->msg_type == FILTER_DOWNLINK_MSG) { + LOGP(0, LOGL_DEBUG, "routing downlink msg to _abis_rsl_sendmsg\n"); + rc = _abis_rsl_sendmsg(msg); + } else { + LOGP(0, LOGL_ERROR, "unknown msg type %d\n", fh->msg_type); + msgb_free(msg); + } + + return rc; +} + +static int client_data(struct osmo_fd *fd, unsigned int what) +{ + int rc = 0; + + LOGP(0, LOGL_DEBUG, "client_data called (%08x)\n", what); + + // new data from external application + if (what & BSC_FD_READ) { + + struct msgb *msg = filter_read_msg_from_socket(fd, &rc); + + if(msg) { + struct filter_head *fh = (struct filter_head *) msg->data; + if(fh->msg_type == FILTER_SILENT_CALL) { + msgb_pull(msg, sizeof(struct filter_head)); + struct filter_silentcall_req *sc_req = (struct filter_silentcall_req *) msg->data; + msgb_pull(msg, sizeof(struct filter_silentcall_req)); + *(char *)msg->tail = 0; + rc = process_silentcall_message(sc_req, fd); + msgb_free(msg); + } else { + rc = route_message(msg); + } + // if we didn't get a msg _and_ there was an error, + // assume the connection has been closed + } else { + // try to route all msgs that might have been + // enqueued for sending already to their + // original recipient + LOGP(0, LOGL_INFO, "filter app went away, freeing resources\n"); + while((msg = msgb_dequeue(&tx_msg_list))) { + route_message(msg); + } + filter_close_client(); + } + } + + // send msgs to external app as soon as we can write + if (what & BSC_FD_WRITE) { + + struct msgb *msg = msgb_dequeue(&tx_msg_list); + + if(msg) { + //LOGP(0, LOGL_DEBUG, "sending new msg to filter app (len: %d)\n", msg->len); + rc = send(fd->fd, msg->data, msg->len, 0); + msgb_free(msg); + if(rc > 0) + rc = 0; + } else { + //LOGP(0, LOGL_DEBUG, "no more messages to send\n"); + fd->when &= ~BSC_FD_WRITE; + } + + } + + return rc; +} + +static int filter_new_connection(struct osmo_fd *fd, unsigned int what) +{ + struct filter_connection *connection; + struct sockaddr_in sockaddr; + socklen_t len = sizeof(sockaddr); + int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len); + + LOGP(0, LOGL_DEBUG, "new filter connection\n"); + + if (new_connection < 0) { + LOGP(0, LOGL_ERROR, "filter accept failed\n"); + return new_connection; + } + + filter_close_client(); + + connection = talloc_zero(tall_filter_ctx, struct filter_connection); + connection->priv = fd->data; + connection->fd.data = connection; + connection->fd.fd = new_connection; + connection->fd.when = BSC_FD_READ | BSC_FD_WRITE; + connection->fd.cb = client_data; + osmo_fd_register(&connection->fd); + + active_connection = connection; + + return 0; +} diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c index 3adabbc08..499151f36 100644 --- a/openbsc/src/libmsc/vty_interface_layer3.c +++ b/openbsc/src/libmsc/vty_interface_layer3.c @@ -831,7 +831,7 @@ DEFUN(mnccint_def_codec_h, int bsc_vty_init_extra(void) { - osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL); + //osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL); install_element_ve(&show_subscr_cmd); install_element_ve(&show_subscr_cache_cmd); diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index 9722b1458..4bbd212a6 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -1,4 +1,5 @@ -SUBDIRS = debug gsm0408 db channel mgcp gprs +#SUBDIRS = debug gsm0408 db channel mgcp gprs +SUBDIRS = debug gsm0408 channel mgcp gprs if BUILD_NAT SUBDIRS += bsc-nat |