From: Pablo Neira Ayuso pablo@gnumonks.org
Hi!
This patchset adds new VTY commands that allows to route IPA flows.
Patch 3/3 includes one feature that changes the current default behaviour of OpenBSC since you explicity have to make a "ipa setup" to enable OML and RSL links. If you like it, I can make a similar patch for HSL femto, so the only initial open socket is the one at port 4242 for telnet configuration.
BTW, the original command syntax was proposed by Harald.
Successfully tested bootstrapping the nanoBTS. See the patches for more details.
Comments welcome!
Pablo Neira Ayuso (3): libcommon: add make_sock_stream_connect() libabis: add VTY commands to route IPA flows libabis: add IPA VTY command to setup ip.access nanoBTS OML and RSL links
openbsc/include/openbsc/ipaccess.h | 3 + openbsc/include/openbsc/socket.h | 2 + openbsc/include/openbsc/vty.h | 1 + openbsc/src/ipaccess/Makefile.am | 4 +- openbsc/src/libabis/Makefile.am | 2 +- openbsc/src/libabis/e1_input.c | 2 + openbsc/src/libabis/input/ipaccess.c | 962 +++++++++++++++++++++++++++++- openbsc/src/libbsc/bsc_vty.c | 1 + openbsc/src/libcommon/socket.c | 54 ++ openbsc/src/openbsc.cfg.ipa-proxy | 18 + openbsc/src/openbsc.cfg.nanobts | 3 + openbsc/src/openbsc.cfg.nanobts.multitrx | 3 + openbsc/tests/db/Makefile.am | 5 +- 13 files changed, 1043 insertions(+), 17 deletions(-) create mode 100644 openbsc/src/openbsc.cfg.ipa-proxy
From: Pablo Neira Ayuso pablo@gnumonks.org
This patch adds make_sock_stream_connect() which allows to perform a non-blocking connect to some host and port.
This only support TCP by now, but it could be easily extensible to support other stream-based layer 4 protocols. --- openbsc/include/openbsc/socket.h | 2 + openbsc/src/libcommon/socket.c | 54 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 0 deletions(-)
diff --git a/openbsc/include/openbsc/socket.h b/openbsc/include/openbsc/socket.h index 87ef37f..af5f729 100644 --- a/openbsc/include/openbsc/socket.h +++ b/openbsc/include/openbsc/socket.h @@ -11,4 +11,6 @@ int make_sock(struct bsc_fd *bfd, int proto, uint32_t ip, uint16_t port, int priv_nr, int (*cb)(struct bsc_fd *fd, unsigned int what), void *data);
+int make_sock_stream_connect(struct bsc_fd *bfd, int proto, struct in_addr addr, uint16_t port, int priv_nr, int (*cb)(struct bsc_fd *fd, unsigned int what), void *data); + #endif /* _BSC_SOCKET_H */ diff --git a/openbsc/src/libcommon/socket.c b/openbsc/src/libcommon/socket.c index dd25dd7..f4a533d 100644 --- a/openbsc/src/libcommon/socket.c +++ b/openbsc/src/libcommon/socket.c @@ -107,3 +107,57 @@ int make_sock(struct bsc_fd *bfd, int proto, } return 0; } + +/* this function performs a non-blocking stream connect. */ +int make_sock_stream_connect(struct bsc_fd *bfd, int proto, + struct in_addr addr, uint16_t port, int priv_nr, + int (*cb)(struct bsc_fd *fd, unsigned int what), + void *data) +{ + int ret, flags, type; + struct sockaddr_in saddr; + + switch (proto) { + case IPPROTO_TCP: + type = SOCK_STREAM; + break; + default: + return -EINVAL; + } + + bfd->fd = socket(AF_INET, type, proto); + bfd->cb = cb; + /* non-blocking connect() confirms by setting BSC_FD_READ. */ + bfd->when = BSC_FD_READ | BSC_FD_WRITE; + bfd->data = data; + bfd->priv_nr = priv_nr; + + if (bfd->fd < 0) { + LOGP(DINP, LOGL_ERROR, "could not create socket.\n"); + return -EIO; + } + flags = fcntl(bfd->fd, F_GETFL, 0); + if (fcntl(bfd->fd, F_SETFL, flags | O_NONBLOCK) < 0) { + LOGP(DINP, LOGL_ERROR, "could not fcntl.\n"); + return -EIO; + } + memset(&saddr, 0, sizeof(addr)); + saddr.sin_family = AF_INET; + saddr.sin_port = port; + memcpy(&saddr.sin_addr, &addr, sizeof(struct in_addr)); + + ret = connect(bfd->fd, (struct sockaddr *) &saddr, sizeof(saddr)); + if (ret < 0 && errno != EINPROGRESS) { + LOGP(DINP, LOGL_ERROR, "could not connect socket %s\n", + strerror(errno)); + close(bfd->fd); + return -EIO; + } + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register_listen_fd"); + close(bfd->fd); + return ret; + } + return 0; +}
From: Pablo Neira Ayuso pablo@gnumonks.org
This patch adds VTY commands to route IPA flows. This works as it follows:
$ telnet localhost 4242 %% enter ENABLE mode
enable
%% create an input instance that binds to 192.168.1.1:9999 # ipa instance input bind 192.168.1.1 tcp port 9999 %% create an output instance that connects to 192.168.1.2:8888 # ipa instance output connect 192.168.1.2 tcp port 8888 %% create a route between the input and output instance, routing messages whose streamid is 0xff (OML). # ipa route instance input streamid 0xff instance output streamid 0xff
We also allow mangling the streamid in case that the source and the destination streamids are not the same.
This patch includes the following commands:
%% to delete one instance # ipa no instance input
%% to delete one route # ipa no route instance input streamid 0xff instance output streamid 0xff --- openbsc/include/openbsc/ipaccess.h | 2 + openbsc/include/openbsc/vty.h | 1 + openbsc/src/ipaccess/Makefile.am | 4 +- openbsc/src/libabis/Makefile.am | 2 +- openbsc/src/libabis/input/ipaccess.c | 891 ++++++++++++++++++++++++++++++++++ openbsc/src/libbsc/bsc_vty.c | 1 + openbsc/src/openbsc.cfg.ipa-proxy | 18 + openbsc/tests/db/Makefile.am | 5 +- 8 files changed, 919 insertions(+), 5 deletions(-) create mode 100644 openbsc/src/openbsc.cfg.ipa-proxy
diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h index d965aea..28ec49a 100644 --- a/openbsc/include/openbsc/ipaccess.h +++ b/openbsc/include/openbsc/ipaccess.h @@ -86,6 +86,8 @@ int ipaccess_parse_unitid(const char *str, uint16_t *site_id, uint16_t *bts_id, int ipaccess_drop_oml(struct gsm_bts *bts); int ipaccess_drop_rsl(struct gsm_bts_trx *trx);
+void ipaccess_vty_init(void); + /* * Firmware specific header */ diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h index ded2e15..56950b5 100644 --- a/openbsc/include/openbsc/vty.h +++ b/openbsc/include/openbsc/vty.h @@ -35,6 +35,7 @@ enum bsc_vty_node { MSC_NODE, OM2K_NODE, TRUNK_NODE, + IPA_NODE, };
extern int bsc_vty_is_config_node(struct vty *vty, int node); diff --git a/openbsc/src/ipaccess/Makefile.am b/openbsc/src/ipaccess/Makefile.am index 4fe1e37..2ee30e3 100644 --- a/openbsc/src/ipaccess/Makefile.am +++ b/openbsc/src/ipaccess/Makefile.am @@ -1,6 +1,6 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS)
bin_PROGRAMS = ipaccess-find ipaccess-config ipaccess-proxy
diff --git a/openbsc/src/libabis/Makefile.am b/openbsc/src/libabis/Makefile.am index ffaa201..d340a3f 100644 --- a/openbsc/src/libabis/Makefile.am +++ b/openbsc/src/libabis/Makefile.am @@ -1,6 +1,6 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS)
noinst_LIBRARIES = libabis.a
diff --git a/openbsc/src/libabis/input/ipaccess.c b/openbsc/src/libabis/input/ipaccess.c index d2572fb..0cd3b2d 100644 --- a/openbsc/src/libabis/input/ipaccess.c +++ b/openbsc/src/libabis/input/ipaccess.c @@ -31,6 +31,7 @@ #include <sys/socket.h> #include <sys/ioctl.h> #include <arpa/inet.h> +#include <stdbool.h>
#include <osmocom/core/select.h> #include <osmocom/gsm/tlv.h> @@ -45,6 +46,8 @@ #include <openbsc/ipaccess.h> #include <openbsc/socket.h> #include <openbsc/signal.h> +#include <openbsc/vty.h> +#include <openbsc/socket.h>
#define PRIV_OML 1 #define PRIV_RSL 2 @@ -808,6 +811,894 @@ int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) //return e1inp_line_register(line); }
+/* + * data structures used by the IPA VTY commands + */ +static LLIST_HEAD(ipa_instance_list); + +enum ipa_instance_net_type { + IPA_INSTANCE_T_NONE, + IPA_INSTANCE_T_BIND, + IPA_INSTANCE_T_CONNECT, + IPA_INSTANCE_T_MAX +}; + +struct ipa_instance_net { + struct in_addr addr; + uint16_t port; + enum ipa_instance_net_type type; +}; + +struct ipa_instance { + struct llist_head head; +#define IPA_INSTANCE_NAME 16 + char name[IPA_INSTANCE_NAME]; + struct ipa_instance_net net; + int refcnt; +}; + +static LLIST_HEAD(ipa_route_list); + +/* Several routes pointing to the same instances share this. */ +struct ipa_route_shared { + int refcnt; + + /* this file descriptor is used to accept() new connections. */ + struct bsc_fd bfd; + + struct { + struct ipa_instance *inst; + struct bitvec streamid_map; + uint8_t streamid_map_data[(0xff+1)/8]; + uint8_t streamid[0xff]; + } src; + struct { + struct ipa_instance *inst; + struct bitvec streamid_map; + uint8_t streamid_map_data[(0xff+1)/8]; + uint8_t streamid[0xff]; + } dst; + + struct llist_head conn_list; +}; + +/* One route is composed of two instances. */ +struct ipa_route { + struct llist_head head; + + struct { + uint8_t streamid; + } src; + struct { + uint8_t streamid; + } dst; + + struct ipa_route_shared *shared; +}; + +enum ipa_conn_state { + IPA_CONN_S_NONE, + IPA_CONN_S_CONNECTING, + IPA_CONN_S_CONNECTED, + IPA_CONN_S_MAX +}; + +/* One route may forward more than one connection. */ +struct ipa_conn { + struct llist_head head; + + struct { + /* this is the file descriptor that accept() returns. */ + struct bsc_fd bfd; + struct llist_head tx_queue; + /* special handling for segmented message. */ + ssize_t remaining_bytes; + } src; + struct { + /* this is the file descriptor that connect() returns. */ + struct bsc_fd bfd; + struct llist_head tx_queue; + /* special handling for segmented message. */ + ssize_t remaining_bytes; + enum ipa_conn_state state; + struct timer_list reconnect_timer; + } dst; + struct ipa_route *route; +}; + +/* + * socket callbacks used by IPA VTY commands + */ + +/* try to reconnect after 5 seconds, make this configurable in the future. */ +#define IPA_TIMER_RECONNECT 5 + +/* if we use TS1_ALLOC_SIZE, which is 900 bytes, we may truncate messages. */ +#define IPA_PROXY_ALLOC_SIZE 1200 + +static void ipa_reconnect_timer_cb(void *data); + +static void ipa_sock_dst_reconnect(struct bsc_fd *bfd, struct ipa_conn *conn) +{ + if (bsc_timer_pending(&conn->dst.reconnect_timer)) { + LOGP(DINP, LOGL_ERROR, "Report bug, this SHOULD NOT happen " + "reconnection already scheduled\n"); + return; + } + bsc_unregister_fd(&conn->dst.bfd); + close(conn->dst.bfd.fd); + conn->dst.bfd.fd = -1; + + conn->dst.reconnect_timer.cb = ipa_reconnect_timer_cb; + conn->dst.reconnect_timer.data = conn; + bsc_schedule_timer(&conn->dst.reconnect_timer, IPA_TIMER_RECONNECT, 0); + LOGP(DINP, LOGL_NOTICE, "I will try to reconnect to dst " + "IPA peer in %d seconds\n", IPA_TIMER_RECONNECT); +} + +static int ipa_sock_dst_read(struct bsc_fd *connect_bfd, struct ipa_conn *conn) +{ + struct ipaccess_head *hh; + struct msgb *msg; + int ret; + + msg = msgb_alloc(IPA_PROXY_ALLOC_SIZE, "IPA"); + if (msg == NULL) { + LOGP(DINP, LOGL_ERROR, "failed to alloc msg\n"); + return -ENOMEM; + } + ret = recv(connect_bfd->fd, msg->data, + msg->data_len, 0); + if (ret < 0) { + if (errno == EPIPE || errno == ENOTCONN) { + LOGP(DINP, LOGL_NOTICE, + "destination IPA peer has closed " + "the connection\n"); + } else { + LOGP(DINP, LOGL_ERROR, "failed to " + "recv from destination IPA peer, " + "reason=`%s'\n", strerror(errno)); + } + conn->dst.state = IPA_CONN_S_CONNECTING; + ipa_sock_dst_reconnect(connect_bfd, conn); + msgb_free(msg); + return ret; + } else if (ret == 0) { + LOGP(DINP, LOGL_NOTICE, "destination IPA peer " + "has closed the connection\n"); + conn->dst.state = IPA_CONN_S_CONNECTING; + ipa_sock_dst_reconnect(connect_bfd, conn); + msgb_free(msg); + return 0; + } + /* remaining bytes of a segmented message. */ + if (conn->dst.remaining_bytes > 0) { + conn->dst.remaining_bytes -= ret; + msgb_put(msg, ret); + msgb_enqueue(&conn->src.tx_queue, msg); + conn->src.bfd.when |= BSC_FD_WRITE; + return 0; + } + if (ret < sizeof(struct ipaccess_head)) { + LOGP(DINP, LOGL_ERROR, "received too small " + "message from destination IPA\n"); + msgb_free(msg); + return -EINVAL; + } + msgb_put(msg, ret); + hh = (struct ipaccess_head *)msg->data; + /* check if we have a route for this message. */ + if (bitvec_get_bit_pos( + &conn->route->shared->dst.streamid_map, + hh->proto) != ONE) { + LOGP(DINP, LOGL_NOTICE, "we don't have a " + "route for streamid 0x%x\n", hh->proto); + msgb_free(msg); + return 0; + } + /* segmented message, expect remaining bytes. */ + if (ret < ntohs(hh->len)) { + conn->dst.remaining_bytes = + ntohs(hh->len) + sizeof(*hh) - ret; + } + /* mangle message, if required. */ + hh->proto = conn->route->shared->src.streamid[hh->proto]; + msgb_enqueue(&conn->src.tx_queue, msg); + conn->src.bfd.when |= BSC_FD_WRITE; + return 0; +} + +static int ipa_sock_dst_cb(struct bsc_fd *connect_bfd, unsigned int what) +{ + int ret = 0; + struct msgb *msg; + struct llist_head *lh; + struct ipa_conn *conn = connect_bfd->data; + int error; + size_t len = sizeof(error); + + switch(conn->dst.state) { + case IPA_CONN_S_CONNECTING: + ret = getsockopt(connect_bfd->fd, SOL_SOCKET, SO_ERROR, + &error, &len); + if (ret >= 0 && error > 0) { + conn->dst.state = IPA_CONN_S_CONNECTING; + ipa_sock_dst_reconnect(connect_bfd, conn); + return 0; + } + conn->dst.state = IPA_CONN_S_CONNECTED; + LOGP(DINP, LOGL_NOTICE, "connected to destination IPA peer!\n"); + break; + case IPA_CONN_S_CONNECTED: + /* receive messages: destination -> source. */ + if (what & BSC_FD_READ) + ipa_sock_dst_read(connect_bfd, conn); + + /* send messages: source -> destination. */ + if (what & BSC_FD_WRITE) { + if (llist_empty(&conn->dst.tx_queue)) { + connect_bfd->when &= ~BSC_FD_WRITE; + return 0; + } + lh = conn->dst.tx_queue.next; + llist_del(lh); + msg = llist_entry(lh, struct msgb, list); + + ret = send(conn->dst.bfd.fd, msg->data, msg->len, 0); + if (ret < 0) { + if (errno == EPIPE || errno == ENOTCONN) { + conn->dst.state = + IPA_CONN_S_CONNECTING; + ipa_sock_dst_reconnect(connect_bfd, + conn); + } + LOGP(DINP, LOGL_ERROR, "failed to send (%s)\n", + strerror(errno)); + } + msgb_free(msg); + } + break; + default: + LOGP(DINP, LOGL_ERROR, "unknown state type"); + break; + } + return ret; +} + +static void ipa_reconnect_timer_cb(void *data) +{ + struct ipa_conn *conn = data; + int ret; + + ret = make_sock_stream_connect(&conn->dst.bfd, IPPROTO_TCP, + conn->route->shared->dst.inst->net.addr, + conn->route->shared->dst.inst->net.port, + 0, ipa_sock_dst_cb, conn); + if (ret < 0) + LOGP(DINP, LOGL_ERROR, "cannot create destination IPA socket"); +} + +static int ipa_sock_src_read(struct bsc_fd *bfd, struct ipa_conn *conn) +{ + int ret; + struct msgb *msg; + struct ipaccess_head *hh; + + msg = msgb_alloc(IPA_PROXY_ALLOC_SIZE, "IPA"); + if (msg == NULL) { + LOGP(DINP, LOGL_ERROR, "failed to msgb_alloc\n"); + return -ENOMEM; + } + ret = recv(bfd->fd, msg->data, msg->data_len, 0); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "failed to recv from origin " + "IPA peer, reason=`%s'\n", strerror(errno)); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + msgb_free(msg); + return ret; + } else if (ret == 0) { + LOGP(DINP, LOGL_NOTICE, "origin IPA peer has closed " + "the connection\n"); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + bsc_del_timer(&conn->dst.reconnect_timer); + if (conn->dst.bfd.fd != -1) { + bsc_unregister_fd(&conn->dst.bfd); + close(conn->dst.bfd.fd); + conn->dst.bfd.fd = -1; + } + llist_del(&conn->head); + talloc_free(conn); + msgb_free(msg); + return 0; + } + /* remaining bytes of a segmented message. */ + if (conn->src.remaining_bytes > 0) { + conn->src.remaining_bytes -= ret; + msgb_put(msg, ret); + msgb_enqueue(&conn->dst.tx_queue, msg); + conn->dst.bfd.when |= BSC_FD_WRITE; + return 0; + } + if (ret < sizeof(struct ipaccess_head)) { + LOGP(DINP, LOGL_ERROR, "received too small message " + "from origin IPA\n"); + msgb_free(msg); + return -EINVAL; + } + msgb_put(msg, ret); + hh = (struct ipaccess_head *)msg->data; + /* check if we have a route for this message. */ + if (bitvec_get_bit_pos(&conn->route->shared->src.streamid_map, + hh->proto) != ONE) { + LOGP(DINP, LOGL_NOTICE, "we don't have a " + "route for streamid 0x%x\n", hh->proto); + msgb_free(msg); + return 0; + } + /* segmented message, expect remaining bytes. */ + if (ret < ntohs(hh->len)) { + conn->src.remaining_bytes = + ntohs(hh->len) + sizeof(*hh) - ret; + } + /* mangle message, if required. */ + hh->proto = conn->route->shared->dst.streamid[hh->proto]; + msgb_enqueue(&conn->dst.tx_queue, msg); + conn->dst.bfd.when |= BSC_FD_WRITE; + return 0; +} + +static int ipa_sock_src_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct ipa_conn *conn = bfd->data; + struct msgb *msg; + int ret = 0; + + /* receive messages: source -> destination. */ + if (what & BSC_FD_READ) + ipa_sock_src_read(bfd, conn); + + /* send messages: destination -> source. */ + if (what & BSC_FD_WRITE) { + struct llist_head *lh; + + if (llist_empty(&conn->src.tx_queue)) { + bfd->when &= ~BSC_FD_WRITE; + return 0; + } + lh = conn->src.tx_queue.next; + llist_del(lh); + msg = llist_entry(lh, struct msgb, list); + + ret = send(bfd->fd, msg->data, msg->len, 0); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "failed to send (%s)\n", + strerror(errno)); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + msgb_free(msg); + } + } + return ret; +} + +static int +ipa_sock_src_accept_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + int ret; + struct sockaddr_in sa; + struct ipa_route *route = listen_bfd->data; + socklen_t sa_len = sizeof(sa); + struct ipa_conn *conn; + + ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "failed to accept from origin IPA " + "peer, reason=`%s'\n", strerror(errno)); + return ret; + } + LOGP(DINP, LOGL_NOTICE, "accept()ed new link from origin IPA " + "peer: %s\n", inet_ntoa(sa.sin_addr)); + + conn = talloc_zero(tall_bsc_ctx, struct ipa_conn); + if (conn == NULL) { + LOGP(DINP, LOGL_ERROR, "cannot allocate memory for origin IPA " + "peer: %s\n", inet_ntoa(sa.sin_addr)); + close(ret); + return ret; + } + conn->route = route; + INIT_LLIST_HEAD(&conn->src.tx_queue); + INIT_LLIST_HEAD(&conn->dst.tx_queue); + conn->dst.state = IPA_CONN_S_CONNECTING; + + conn->src.bfd.fd = ret; + conn->src.bfd.data = conn; + conn->src.bfd.cb = ipa_sock_src_cb; + conn->src.bfd.when = BSC_FD_READ; + ret = bsc_register_fd(&conn->src.bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(conn->src.bfd.fd); + conn->src.bfd.fd = -1; + return ret; + } + ret = make_sock_stream_connect(&conn->dst.bfd, IPPROTO_TCP, + route->shared->dst.inst->net.addr, + route->shared->dst.inst->net.port, + 0, ipa_sock_dst_cb, conn); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not create client: %s\n", + strerror(errno)); + conn->dst.state = IPA_CONN_S_CONNECTING; + ipa_sock_dst_reconnect(&conn->dst.bfd, conn); + } + llist_add(&conn->head, &route->shared->conn_list); + return ret; +} + +/* + * VTY commands for IPA + */ +DEFUN(ipa_proxy, ipa_cmd, "ipa", "Configure the ipaccess proxy") +{ + vty->index = NULL; + vty->node = IPA_NODE; + return CMD_SUCCESS; +} + +static int __ipa_instance_add(struct vty *vty, int argc, const char *argv[]) +{ + struct ipa_instance *ipi; + enum ipa_instance_net_type type; + struct in_addr addr; + uint16_t port; + + if (argc < 4) + return CMD_ERR_INCOMPLETE; + + llist_for_each_entry(ipi, &ipa_instance_list, head) { + if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) != 0) + continue; + + vty_out(vty, "%% instance `%s' already exists%s", + ipi->name, VTY_NEWLINE); + return CMD_WARNING; + } + if (strncmp(argv[1], "bind", IPA_INSTANCE_NAME) == 0) + type = IPA_INSTANCE_T_BIND; + else if (strncmp(argv[1], "connect", IPA_INSTANCE_NAME) == 0) + type = IPA_INSTANCE_T_CONNECT; + else + return CMD_ERR_INCOMPLETE; + + if (inet_aton(argv[2], &addr) < 0) { + vty_out(vty, "%% invalid address %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + port = atoi(argv[3]); + + ipi = talloc_zero(tall_bsc_ctx, struct ipa_instance); + if (ipi == NULL) { + vty_out(vty, "%% can't allocate memory for new instance%s", + VTY_NEWLINE); + return CMD_WARNING; + } + strncpy(ipi->name, argv[0], IPA_INSTANCE_NAME); + ipi->net.type = type; + memcpy(&ipi->net.addr, &addr, sizeof(struct in_addr)); + ipi->net.port = htons(port); + llist_add_tail(&ipi->head, &ipa_instance_list); + + return CMD_SUCCESS; +} + +DEFUN(ipa_instance_add, ipa_instance_add_cmd, + "ipa instance NAME (bind|connect) IP tcp port PORT", + "Bind or connect instance to address and port") +{ + return __ipa_instance_add(vty, argc, argv); +} + +DEFUN(ipa_instance_del, ipa_instance_del_cmd, + "ipa no instance NAME", + "Delete instance to address and port") +{ + struct ipa_instance *ipi; + + if (argc < 1) + return CMD_ERR_INCOMPLETE; + + llist_for_each_entry(ipi, &ipa_instance_list, head) { + if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) != 0) + continue; + + if (ipi->refcnt > 0) { + vty_out(vty, "%% instance `%s' is in use%s", + ipi->name, VTY_NEWLINE); + return CMD_WARNING; + } + llist_del(&ipi->head); + talloc_free(ipi); + return CMD_SUCCESS; + } + vty_out(vty, "%% instance `%s' does not exist%s", + ipi->name, VTY_NEWLINE); + + return CMD_WARNING; +} + +DEFUN(ipa_instance_show, ipa_instance_show_cmd, + "ipa instance show", "Show existing ipaccess proxy instances") +{ + struct ipa_instance *this; + + llist_for_each_entry(this, &ipa_instance_list, head) { + vty_out(vty, "instance %s %s %s tcp port %u%s", + this->name, inet_ntoa(this->net.addr), + this->net.type == IPA_INSTANCE_T_BIND ? + "bind" : "connect", + ntohs(this->net.port), VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +static int __ipa_route_add(struct vty *vty, int argc, const char *argv[]) +{ + struct ipa_instance *ipi = vty->index; + struct ipa_instance *src = NULL, *dst = NULL; + uint32_t src_streamid, dst_streamid; + struct ipa_route *route, *matching_route = NULL; + struct ipa_route_shared *shared = NULL; + int ret; + + if (argc < 4) + return CMD_ERR_INCOMPLETE; + + llist_for_each_entry(ipi, &ipa_instance_list, head) { + if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) == 0) { + src = ipi; + continue; + } + if (strncmp(ipi->name, argv[2], IPA_INSTANCE_NAME) == 0) { + dst = ipi; + continue; + } + } + if (src == NULL) { + vty_out(vty, "%% instance `%s' does not exists%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (dst == NULL) { + vty_out(vty, "%% instance `%s' does not exists%s", + argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + if (src->net.type != IPA_INSTANCE_T_BIND) { + vty_out(vty, "%% instance `%s' is not of bind type%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (dst->net.type != IPA_INSTANCE_T_CONNECT) { + vty_out(vty, "%% instance `%s' is not of connect type%s", + argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + src_streamid = strtoul(argv[1], NULL, 16); + if (src_streamid > 0xff) { + vty_out(vty, "%% source streamid must be " + ">= 0x00 and <= 0xff%s", VTY_NEWLINE); + return CMD_WARNING; + } + dst_streamid = strtoul(argv[3], NULL, 16); + if (dst_streamid > 0xff) { + vty_out(vty, "%% destination streamid must be " + ">= 0x00 and <= 0xff%s", VTY_NEWLINE); + return CMD_WARNING; + } + llist_for_each_entry(route, &ipa_route_list, head) { + if (route->shared->src.inst == src && + route->shared->dst.inst == dst) { + if (route->src.streamid == src_streamid && + route->dst.streamid == dst_streamid) { + vty_out(vty, "%% this route already exists%s", + VTY_NEWLINE); + return CMD_WARNING; + } + matching_route = route; + break; + } + } + /* new route for this configuration. */ + route = talloc_zero(tall_bsc_ctx, struct ipa_route); + if (route == NULL) { + vty_out(vty, "%% can't allocate memory for new route%s", + VTY_NEWLINE); + return CMD_WARNING; + } + route->src.streamid = src_streamid; + route->dst.streamid = dst_streamid; + + if (matching_route != NULL) { + /* there's already a master route for these configuration. */ + if (matching_route->shared->src.inst != src) { + vty_out(vty, "%% route does not contain " + "source instance `%s'%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (matching_route->shared->dst.inst != dst) { + vty_out(vty, "%% route does not contain " + "destination instance `%s'%s", + argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + /* use already existing shared routing information. */ + shared = matching_route->shared; + } else { + /* this is a brand new route, allocate shared routing info. */ + shared = talloc_zero(tall_bsc_ctx, struct ipa_route_shared); + if (shared == NULL) { + vty_out(vty, "%% can't allocate memory for " + "new route shared%s", VTY_NEWLINE); + return CMD_WARNING; + } + shared->src.streamid_map.data_len = + sizeof(shared->src.streamid_map_data); + shared->src.streamid_map.data = + shared->src.streamid_map_data; + shared->dst.streamid_map.data_len = + sizeof(shared->dst.streamid_map_data); + shared->dst.streamid_map.data = + shared->dst.streamid_map_data; + + ret = make_sock(&shared->bfd, IPPROTO_TCP, INADDR_ANY, + ntohs(src->net.port), 0, + ipa_sock_src_accept_cb, route); + if (ret < 0) { + vty_out(vty, "%% can't bind instance `%s' to port%s", + src->name, VTY_NEWLINE); + return CMD_WARNING; + } + INIT_LLIST_HEAD(&shared->conn_list); + } + route->shared = shared; + src->refcnt++; + route->shared->src.inst = src; + dst->refcnt++; + route->shared->dst.inst = dst; + shared->src.streamid[src_streamid] = dst_streamid; + shared->dst.streamid[dst_streamid] = src_streamid; + ret = bitvec_set_bit_pos(&shared->src.streamid_map, src_streamid, ONE); + if (ret < 0) { + vty_out(vty, "%% bad bitmask (?)%s", VTY_NEWLINE); + return CMD_WARNING; + } + ret = bitvec_set_bit_pos(&shared->dst.streamid_map, dst_streamid, ONE); + if (ret < 0) { + vty_out(vty, "%% bad bitmask (?)%s", VTY_NEWLINE); + return CMD_WARNING; + } + shared->refcnt++; + + llist_add_tail(&route->head, &ipa_route_list); + return CMD_SUCCESS; +} + +DEFUN(ipa_route_add, ipa_route_add_cmd, + "ipa route instance NAME streamid HEXNUM " + "instance NAME streamid HEXNUM", "Add IPA route") +{ + return __ipa_route_add(vty, argc, argv); +} + +DEFUN(ipa_route_del, ipa_route_del_cmd, + "ipa no route instance NAME streamid HEXNUM " + "instance NAME streamid HEXNUM", "Delete IPA route") +{ + struct ipa_instance *ipi = vty->index; + struct ipa_instance *src = NULL, *dst = NULL; + uint32_t src_streamid, dst_streamid; + struct ipa_route *route, *matching_route = NULL; + struct ipa_conn *conn, *tmp; + + if (argc < 4) + return CMD_ERR_INCOMPLETE; + + llist_for_each_entry(ipi, &ipa_instance_list, head) { + if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) == 0) { + src = ipi; + continue; + } + if (strncmp(ipi->name, argv[2], IPA_INSTANCE_NAME) == 0) { + dst = ipi; + continue; + } + } + if (src == NULL) { + vty_out(vty, "%% instance `%s' does not exists%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (dst == NULL) { + vty_out(vty, "%% instance `%s' does not exists%s", + argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + if (src->net.type != IPA_INSTANCE_T_BIND) { + vty_out(vty, "%% instance `%s' is not of bind type%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (dst->net.type != IPA_INSTANCE_T_CONNECT) { + vty_out(vty, "%% instance `%s' is not of connect type%s", + argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + src_streamid = strtoul(argv[1], NULL, 16); + if (src_streamid > 0xff) { + vty_out(vty, "%% source streamid must be " + ">= 0x00 and <= 0xff%s", VTY_NEWLINE); + return CMD_WARNING; + } + dst_streamid = strtoul(argv[3], NULL, 16); + if (dst_streamid > 0xff) { + vty_out(vty, "%% destination streamid must be " + ">= 0x00 and <= 0xff%s", VTY_NEWLINE); + return CMD_WARNING; + } + llist_for_each_entry(route, &ipa_route_list, head) { + if (route->shared->src.inst == src && + route->shared->dst.inst == dst && + route->src.streamid == src_streamid && + route->dst.streamid == dst_streamid) { + matching_route = route; + break; + } + } + if (matching_route == NULL) { + vty_out(vty, "%% no route with that configuration%s", + VTY_NEWLINE); + return CMD_WARNING; + } + /* delete this route from list. */ + llist_del(&matching_route->head); + + if (--matching_route->shared->refcnt == 0) { + /* nobody else using this route, release all resources. */ + llist_for_each_entry_safe(conn, tmp, + &matching_route->shared->conn_list, head) { + bsc_unregister_fd(&conn->src.bfd); + close(conn->src.bfd.fd); + conn->src.bfd.fd = -1; + bsc_del_timer(&conn->dst.reconnect_timer); + if (conn->dst.bfd.fd != -1) { + bsc_unregister_fd(&conn->dst.bfd); + close(conn->dst.bfd.fd); + conn->dst.bfd.fd = -1; + } + llist_del(&conn->head); + talloc_free(conn); + } + bsc_unregister_fd(&route->shared->bfd); + close(route->shared->bfd.fd); + route->shared->bfd.fd = -1; + + talloc_free(route->shared); + } else { + /* otherwise, revert the mapping that this route applies. */ + bitvec_set_bit_pos(&matching_route->shared->src.streamid_map, + src_streamid, ZERO); + bitvec_set_bit_pos(&matching_route->shared->dst.streamid_map, + dst_streamid, ZERO); + matching_route->shared->src.streamid[src_streamid] = 0x00; + matching_route->shared->dst.streamid[dst_streamid] = 0x00; + } + matching_route->shared->src.inst->refcnt--; + matching_route->shared->dst.inst->refcnt--; + talloc_free(matching_route); + + return CMD_SUCCESS; +} + +DEFUN(ipa_route_show, ipa_route_show_cmd, + "ipa route show", "Show existing ipaccess proxy routes") +{ + struct ipa_route *this; + + llist_for_each_entry(this, &ipa_route_list, head) { + vty_out(vty, "route instance %s streamid 0x%.2x " + "instance %s streamid 0x%.2x%s", + this->shared->src.inst->name, this->src.streamid, + this->shared->dst.inst->name, this->dst.streamid, + VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +/* + * Config for ipaccess-proxy + */ +DEFUN(ipa_cfg, ipa_cfg_cmd, "ipa", "Configure the ipaccess proxy") +{ + vty->index = NULL; + vty->node = IPA_NODE; + return CMD_SUCCESS; +} + +/* all these below look like enable commands, but without the ipa prefix. */ +DEFUN(ipa_route_cfg_add, ipa_route_cfg_add_cmd, + "route instance NAME streamid HEXNUM " + "instance NAME streamid HEXNUM", "Add IPA route") +{ + return __ipa_route_add(vty, argc, argv); +} + +DEFUN(ipa_instance_cfg_add, ipa_instance_cfg_add_cmd, + "instance NAME (bind|connect) IP tcp port PORT", + "Bind or connect instance to address and port") +{ + return __ipa_instance_add(vty, argc, argv); +} + +struct cmd_node ipa_node = { + IPA_NODE, + "%s(ipa)#", + 1, +}; + +static int ipa_cfg_write(struct vty *vty) +{ + bool heading = false; + struct ipa_instance *inst; + struct ipa_route *route; + + llist_for_each_entry(inst, &ipa_instance_list, head) { + if (!heading) { + vty_out(vty, "ipa%s", VTY_NEWLINE); + heading = true; + } + vty_out(vty, " instance %s %s %s tcp port %u%s", + inst->name, + inst->net.type == IPA_INSTANCE_T_BIND ? + "bind" : "connect", + inet_ntoa(inst->net.addr), + ntohs(inst->net.port), VTY_NEWLINE); + } + llist_for_each_entry(route, &ipa_route_list, head) { + vty_out(vty, " route instance %s streamid 0x%.2x " + "instance %s streamid 0x%.2x%s", + route->shared->src.inst->name, route->src.streamid, + route->shared->dst.inst->name, route->dst.streamid, + VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +void ipaccess_vty_init(void) +{ + install_element(ENABLE_NODE, &ipa_cmd); + install_element(ENABLE_NODE, &ipa_instance_add_cmd); + install_element(ENABLE_NODE, &ipa_instance_del_cmd); + install_element(ENABLE_NODE, &ipa_instance_show_cmd); + install_element(ENABLE_NODE, &ipa_route_add_cmd); + install_element(ENABLE_NODE, &ipa_route_del_cmd); + install_element(ENABLE_NODE, &ipa_route_show_cmd); + + install_element(CONFIG_NODE, &ipa_cfg_cmd); + install_node(&ipa_node, ipa_cfg_write); + install_default(IPA_NODE); + install_element(IPA_NODE, &ournode_exit_cmd); + install_element(IPA_NODE, &ournode_end_cmd); + install_element(IPA_NODE, &ipa_instance_cfg_add_cmd); + install_element(IPA_NODE, &ipa_route_cfg_add_cmd); +} + int ipaccess_setup(struct gsm_network *gsmnet) { int ret; diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index e052bda..9c1066d 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -2791,6 +2791,7 @@ int bsc_vty_init(const struct log_info *cat) abis_nm_vty_init(); abis_om2k_vty_init(); e1inp_vty_init(); + ipaccess_vty_init();
bsc_vty_init_extra();
diff --git a/openbsc/src/openbsc.cfg.ipa-proxy b/openbsc/src/openbsc.cfg.ipa-proxy new file mode 100644 index 0000000..30615da --- /dev/null +++ b/openbsc/src/openbsc.cfg.ipa-proxy @@ -0,0 +1,18 @@ +! +! Example file to setup an IPA proxy between ip.access nanoBTS and OpenBSC +! +password foo +! +line vty + no login +! +ipa + instance in0 bind 192.168.0.1 tcp port 3002 + instance out0 connect 192.168.0.2 tcp port 3002 + instance in1 bind 192.168.0.1 tcp port 3003 + instance out1 connect 192.168.0.2 tcp port 3003 + route instance in0 streamid 0xff instance out0 streamid 0xff + route instance in0 streamid 0xfe instance out0 streamid 0xfe + route instance in1 streamid 0x00 instance out1 streamid 0x00 + route instance in1 streamid 0xfe instance out1 streamid 0xfe +end diff --git a/openbsc/tests/db/Makefile.am b/openbsc/tests/db/Makefile.am index 98fdccc..2a21c0d 100644 --- a/openbsc/tests/db/Makefile.am +++ b/openbsc/tests/db/Makefile.am @@ -1,5 +1,5 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS) +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS)
noinst_PROGRAMS = db_test @@ -11,5 +11,6 @@ db_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libabis/libabis.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -ldl -ldbi + $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \ + -ldl -ldbi
From: Pablo Neira Ayuso pablo@gnumonks.org
This patch adds the following command:
$ telnet localhost 4242
enable
# ipa setup
To explicitly setup OML and RSL links. You can check that the sockets have been up by means of netstat:
$ netstat -untap tcp 0 0 0.0.0.0:3002 0.0.0.0:* LISTEN 15204/osmo-nitb tcp 0 0 0.0.0.0:3003 0.0.0.0:* LISTEN 15204/osmo-nitb
This changes the current behaviour of OpenBSC since it creates the OML and RSL sockets by default. I think that this is useful to have in case that you have no nanoBTS in your setup (for example, you use HSL femto instead).
The command to disable nanoBTS OML and RSL links in runtime is not yet implemented, it shows the following:
# ipa no setup %% sorry, disabling OML and RSL links in runtime is not yet implemented
The main point of this patch is that we will be able to use openBSC as IPA proxy, instead of the standalone ipaccess-proxy tool. Since the OML and RSL ports for nanoBTS are no used by default, we can add an IPA route rule to bind to OML and RSL ports and forward them to the real BSC.
Still, there are some features in ipaccess-proxy that are not available (like the UDP packet injection) but I expect to include them soon. --- openbsc/include/openbsc/ipaccess.h | 1 + openbsc/src/libabis/e1_input.c | 2 + openbsc/src/libabis/input/ipaccess.c | 83 +++++++++++++++++++++++------- openbsc/src/openbsc.cfg.nanobts | 3 + openbsc/src/openbsc.cfg.nanobts.multitrx | 3 + 5 files changed, 74 insertions(+), 18 deletions(-)
diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h index 28ec49a..a9d31cb 100644 --- a/openbsc/include/openbsc/ipaccess.h +++ b/openbsc/include/openbsc/ipaccess.h @@ -87,6 +87,7 @@ int ipaccess_drop_oml(struct gsm_bts *bts); int ipaccess_drop_rsl(struct gsm_bts_trx *trx);
void ipaccess_vty_init(void); +int e1inp_ipaccess_init(void);
/* * Firmware specific header diff --git a/openbsc/src/libabis/e1_input.c b/openbsc/src/libabis/e1_input.c index 1a79261..8efef13 100644 --- a/openbsc/src/libabis/e1_input.c +++ b/openbsc/src/libabis/e1_input.c @@ -52,6 +52,7 @@ #include <osmocom/core/talloc.h> #include <openbsc/signal.h> #include <openbsc/misdn.h> +#include <openbsc/ipaccess.h>
#include "../../bscconfig.h"
@@ -645,4 +646,5 @@ void e1inp_init(void) #ifdef HAVE_DAHDI_USER_H e1inp_dahdi_init(); #endif + e1inp_ipaccess_init(); } diff --git a/openbsc/src/libabis/input/ipaccess.c b/openbsc/src/libabis/input/ipaccess.c index 0cd3b2d..d117547 100644 --- a/openbsc/src/libabis/input/ipaccess.c +++ b/openbsc/src/libabis/input/ipaccess.c @@ -1621,6 +1621,50 @@ DEFUN(ipa_route_show, ipa_route_show_cmd, return CMD_SUCCESS; }
+static bool ipa_setup_already = false; + +static int __ipa_setup(struct vty *vty, int argc, const char *argv[]) +{ + int ret; + + if (ipa_setup_already) { + vty_out(vty, "%% OML and RSL links has been already set up%s", + VTY_NEWLINE); + return CMD_WARNING; + } + /* Listen for OML connections */ + ret = make_sock(&e1h->listen_fd, IPPROTO_TCP, INADDR_ANY, + IPA_TCP_PORT_OML, 0, listen_fd_cb, NULL); + if (ret < 0) { + vty_out(vty, "%% cannot setup OML link, reason=`%s'%s", + strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + /* Listen for RSL connections */ + ret = make_sock(&e1h->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY, + IPA_TCP_PORT_RSL, 0, rsl_listen_fd_cb, NULL); + if (ret < 0) { + vty_out(vty, "%% cannot setup RSL link, reason=`%s'%s", + strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN(ipa_setup, ipa_setup_cmd, + "ipa setup", "Set up OML and RSL links for ip.access nanoBTS") +{ + return __ipa_setup(vty, argc, argv); +} + +DEFUN(ipa_setup_del, ipa_setup_del_cmd, + "ipa no setup", "Disable OML and RSL links for ip.access nanoBTS") +{ + vty_out(vty, "%% sorry, disabling OML and RSL links in runtime " + "is not yet implemented%s", VTY_NEWLINE); + return CMD_WARNING; +} + /* * Config for ipaccess-proxy */ @@ -1646,6 +1690,12 @@ DEFUN(ipa_instance_cfg_add, ipa_instance_cfg_add_cmd, return __ipa_instance_add(vty, argc, argv); }
+DEFUN(ipa_setup_cfg, ipa_setup_cfg_cmd, + "setup", "Set up OML and RSL links for ip.access nanoBTS") +{ + return __ipa_setup(vty, argc, argv); +} + struct cmd_node ipa_node = { IPA_NODE, "%s(ipa)#", @@ -1677,12 +1727,17 @@ static int ipa_cfg_write(struct vty *vty) route->shared->dst.inst->name, route->dst.streamid, VTY_NEWLINE); } + if (ipa_setup_already) + vty_out(vty, " setup%s", VTY_NEWLINE); + return CMD_SUCCESS; }
void ipaccess_vty_init(void) { install_element(ENABLE_NODE, &ipa_cmd); + install_element(ENABLE_NODE, &ipa_setup_cmd); + install_element(ENABLE_NODE, &ipa_setup_del_cmd); install_element(ENABLE_NODE, &ipa_instance_add_cmd); install_element(ENABLE_NODE, &ipa_instance_del_cmd); install_element(ENABLE_NODE, &ipa_instance_show_cmd); @@ -1695,37 +1750,29 @@ void ipaccess_vty_init(void) install_default(IPA_NODE); install_element(IPA_NODE, &ournode_exit_cmd); install_element(IPA_NODE, &ournode_end_cmd); + install_element(IPA_NODE, &ipa_setup_cfg_cmd); install_element(IPA_NODE, &ipa_instance_cfg_add_cmd); install_element(IPA_NODE, &ipa_route_cfg_add_cmd); }
int ipaccess_setup(struct gsm_network *gsmnet) { + e1h->gsmnet = gsmnet; + return 0; +} + +int e1inp_ipaccess_init(void) +{ int ret;
- /* register the driver with the core */ - /* FIXME: do this in the plugin initializer function */ ret = e1inp_driver_register(&ipaccess_driver); - if (ret) + if (ret < 0) return ret;
+ /* this is initialized via VTY commands and later ipaccess_setup(). */ e1h = talloc_zero(tall_bsc_ctx, struct ia_e1_handle); if (!e1h) return -ENOMEM;
- e1h->gsmnet = gsmnet; - - /* Listen for OML connections */ - ret = make_sock(&e1h->listen_fd, IPPROTO_TCP, INADDR_ANY, - IPA_TCP_PORT_OML, 0, listen_fd_cb, NULL); - if (ret < 0) - return ret; - - /* Listen for RSL connections */ - ret = make_sock(&e1h->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY, - IPA_TCP_PORT_RSL, 0, rsl_listen_fd_cb, NULL); - if (ret < 0) - return ret; - - return ret; + return 0; } diff --git a/openbsc/src/openbsc.cfg.nanobts b/openbsc/src/openbsc.cfg.nanobts index 3e7b932..e132655 100644 --- a/openbsc/src/openbsc.cfg.nanobts +++ b/openbsc/src/openbsc.cfg.nanobts @@ -73,3 +73,6 @@ network phys_chan_config TCH/F timeslot 7 phys_chan_config TCH/F + +ipa + setup diff --git a/openbsc/src/openbsc.cfg.nanobts.multitrx b/openbsc/src/openbsc.cfg.nanobts.multitrx index 9c9108c..969f50d 100644 --- a/openbsc/src/openbsc.cfg.nanobts.multitrx +++ b/openbsc/src/openbsc.cfg.nanobts.multitrx @@ -95,3 +95,6 @@ network phys_chan_config TCH/F timeslot 7 phys_chan_config TCH/F + +ipa + setup
Hi Pablo,
sorry for getting back to this after one month ...
On Tue, Apr 19, 2011 at 07:47:44PM +0200, pablo@gnumonks.org wrote:
This patchset adds new VTY commands that allows to route IPA flows.
can you re-base this patchset against current openbsc and develop it further into the following direction:
* cleanly split the IPA part from openbsc, allowing it to be re-used from other applications. All related VTY code should be part of the IPA code. The application (like osmo-nitb) should only call an initialization function, which then takes care of registering with vty, etc. * I imagine something like a libosmoipa as a final result. initially it can be built as part of openbsc.git, later it should become part of libosmocore.git
The idea behind this is simple: I'd like to re-use the existing IPA code from within osmo-bts, where right now Andreas has created a separate implementation of the same protocol (which I think is not the best style).
Regards, Harald
Hi Harald,
On 22/05/11 22:05, Harald Welte wrote:
Hi Pablo,
sorry for getting back to this after one month ...
Never mind. I'm happy that all those things got merged :-).
On Tue, Apr 19, 2011 at 07:47:44PM +0200, pablo@gnumonks.org wrote:
This patchset adds new VTY commands that allows to route IPA flows.
can you re-base this patchset against current openbsc and develop it further into the following direction:
- cleanly split the IPA part from openbsc, allowing it to be re-used from other applications. All related VTY code should be part of the IPA code. The application (like osmo-nitb) should only call an initialization function, which then takes care of registering with vty, etc.
 
I guess that you refer to all IPA code, not only the code in my patch.
- I imagine something like a libosmoipa as a final result. initially it can be built as part of openbsc.git, later it should become part of libosmocore.git
 
Sure. I'll make it part of libosmocore since the beginning, so we can skip the transition.
The idea behind this is simple: I'd like to re-use the existing IPA code from within osmo-bts, where right now Andreas has created a separate implementation of the same protocol (which I think is not the best style).
Having a look at that code, I guess that it's still quite experimental, but if there's some way to help/test it, let me know. The A-bis interface is the part that I better know at this point, I'll be happy to contribute to that part :-).