diff --git a/include/internal.h b/include/internal.h index 9d16722..f19f006 100644 --- a/include/internal.h +++ b/include/internal.h @@ -1,6 +1,9 @@ #ifndef _INTERNAL_H_ #define _INTERNAL_H_ +/* XXX: fix this in libosmocore, we need some reserved range */ +#define IPA_NODE _LAST_OSMOVTY_NODE + 100 + /* talloc context for libosmo-abis. */ extern void *libosmo_abis_ctx; diff --git a/include/osmocom/abis/Makefile.am b/include/osmocom/abis/Makefile.am index 3093ee5..6cf3510 100644 --- a/include/osmocom/abis/Makefile.am +++ b/include/osmocom/abis/Makefile.am @@ -1,3 +1,8 @@ -osmoabis_HEADERS = abis.h e1_input.h subchan_demux.h ipaccess.h trau_frame.h +osmoabis_HEADERS = abis.h \ + e1_input.h \ + subchan_demux.h \ + ipa_proxy.h \ + ipaccess.h \ + trau_frame.h osmoabisdir = $(includedir)/osmocom/gsm/abis diff --git a/include/osmocom/abis/ipa_proxy.h b/include/osmocom/abis/ipa_proxy.h new file mode 100644 index 0000000..9b4efa9 --- /dev/null +++ b/include/osmocom/abis/ipa_proxy.h @@ -0,0 +1,6 @@ +#ifndef _IPA_PROXY_H_ +#define _IPA_PROXY_H_ + +void ipa_proxy_vty_init(void); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index d6f8340..d81a6c3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,12 +6,13 @@ LIBVERSION=0:0:0 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) $(COVERAGE_LDFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS) lib_LTLIBRARIES = libosmoabis.la libosmoabis_la_LIBADD = input/libosmoabis-input.la libosmoabis_la_SOURCES = init.c \ e1_input.c \ + ipa_proxy.c \ subchan_demux.c \ trau_frame.c diff --git a/src/ipa_proxy.c b/src/ipa_proxy.c new file mode 100644 index 0000000..980e3c7 --- /dev/null +++ b/src/ipa_proxy.c @@ -0,0 +1,688 @@ +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void *tall_ipa_proxy_ctx; + +/* + * data structures used by the IPA VTY commands + */ +static LLIST_HEAD(ipa_instance_list); + +enum ipa_proxy_instance_net_type { + IPA_INSTANCE_T_NONE, + IPA_INSTANCE_T_BIND, + IPA_INSTANCE_T_CONNECT, + IPA_INSTANCE_T_MAX +}; + +struct ipa_proxy_instance_net { + char *addr; + uint16_t port; + enum ipa_proxy_instance_net_type type; +}; + +struct ipa_proxy_instance { + struct llist_head head; +#define IPA_INSTANCE_NAME 16 + char name[IPA_INSTANCE_NAME]; + struct ipa_proxy_instance_net net; + int refcnt; +}; + +static LLIST_HEAD(ipa_proxy_route_list); + +/* Several routes pointing to the same instances share this. */ +struct ipa_proxy_route_shared { + int refcnt; + + /* this file descriptor is used to accept() new connections. */ + struct osmo_fd bfd; + + struct { + struct ipa_proxy_instance *inst; + struct bitvec streamid_map; + uint8_t streamid_map_data[(0xff+1)/8]; + uint8_t streamid[0xff]; + } src; + struct { + struct ipa_proxy_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_proxy_route { + struct llist_head head; + + struct { + uint8_t streamid; + } src; + struct { + uint8_t streamid; + } dst; + + struct ipa_proxy_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_proxy_conn { + struct llist_head head; + + struct ipa_server_peer *src; + struct ipa_client_link *dst; + struct ipa_proxy_route *route; +}; + +/* + * socket callbacks used by IPA VTY commands + */ +static int ipa_sock_dst_cb(struct ipa_client_link *link, struct msgb *msg) +{ + struct ipaccess_head *hh; + struct ipa_proxy_conn *conn = link->data; + + LOGP(DINP, LOGL_NOTICE, "received message from client side\n"); + + 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; + } + /* mangle message, if required. */ + hh->proto = conn->route->shared->src.streamid[hh->proto]; + + ipa_server_peer_send(conn->src, msg); + return 0; +} + +static int ipa_sock_src_cb(struct ipa_server_peer *peer, struct msgb *msg) +{ + struct ipaccess_head *hh; + struct ipa_proxy_conn *conn = peer->data; + + LOGP(DINP, LOGL_NOTICE, "received message from server side\n"); + + 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; + } + /* mangle message, if required. */ + hh->proto = conn->route->shared->dst.streamid[hh->proto]; + + ipa_client_link_send(conn->dst, msg); + return 0; +} + +static int +ipa_sock_src_accept_cb(struct ipa_server_link *link, int fd) +{ + int ret; + struct ipa_proxy_route *route = link->data; + struct ipa_proxy_conn *conn; + + conn = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_conn); + if (conn == NULL) { + LOGP(DINP, LOGL_ERROR, "cannot allocate memory for " + "origin IPA\n"); + close(fd); + return ret; + } + conn->route = route; + + conn->src = ipa_server_peer_create(tall_ipa_proxy_ctx, link, fd, + ipa_sock_src_cb, conn); + if (conn->src == NULL) { + LOGP(DINP, LOGL_ERROR, "could not create server peer: %s\n", + strerror(errno)); + return -ENOMEM; + } + + LOGP(DINP, LOGL_NOTICE, "now trying to connect to destination\n"); + + conn->dst = ipa_client_link_create(NULL, NULL, + route->shared->dst.inst->net.addr, + route->shared->dst.inst->net.port, + ipa_sock_dst_cb, conn); + if (conn->dst == NULL) { + LOGP(DINP, LOGL_ERROR, "could not create client: %s\n", + strerror(errno)); + return -ENOMEM; + } + if (ipa_client_link_open(conn->dst) < 0) { + LOGP(DINP, LOGL_ERROR, "could not start client: %s\n", + strerror(errno)); + return -ENOMEM; + } + 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_proxy_instance *ipi; + enum ipa_proxy_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_ipa_proxy_ctx, struct ipa_proxy_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; + ipi->net.addr = talloc_strdup(tall_ipa_proxy_ctx, argv[2]); + ipi->net.port = 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, + "no ipa instance NAME", + "Delete instance to address and port") +{ + struct ipa_proxy_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_proxy_instance *this; + + llist_for_each_entry(this, &ipa_instance_list, head) { + vty_out(vty, "instance %s %s %s tcp port %u%s", + this->name, this->net.addr, + this->net.type == IPA_INSTANCE_T_BIND ? + "bind" : "connect", + this->net.port, VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +static int __ipa_route_add(struct vty *vty, int argc, const char *argv[]) +{ + struct ipa_proxy_instance *ipi = vty->index; + struct ipa_proxy_instance *src = NULL, *dst = NULL; + uint32_t src_streamid, dst_streamid; + struct ipa_proxy_route *route, *matching_route = NULL; + struct ipa_proxy_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_proxy_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_ipa_proxy_ctx, struct ipa_proxy_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 { + struct ipa_server_link *link; + + /* this is a brand new route, allocate shared routing info. */ + shared = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_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; + + link = ipa_server_link_create(tall_ipa_proxy_ctx, NULL, + "0.0.0.0", + src->net.port, + ipa_sock_src_accept_cb, route); + if (link == NULL) { + vty_out(vty, "%% can't bind instance `%s' to port%s", + src->name, VTY_NEWLINE); + return CMD_WARNING; + } + if (ipa_server_link_open(link) < 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_proxy_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, + "no ipa route instance NAME streamid HEXNUM " + "instance NAME streamid HEXNUM", "Delete IPA route") +{ + struct ipa_proxy_instance *ipi = vty->index; + struct ipa_proxy_instance *src = NULL, *dst = NULL; + uint32_t src_streamid, dst_streamid; + struct ipa_proxy_route *route, *matching_route = NULL; + struct ipa_proxy_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_proxy_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) { + ipa_server_peer_destroy(conn->src); + llist_del(&conn->head); + talloc_free(conn); + } + osmo_fd_unregister(&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_proxy_route *this; + + llist_for_each_entry(this, &ipa_proxy_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_proxy_instance *inst; + struct ipa_proxy_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", + inst->net.addr, + inst->net.port, VTY_NEWLINE); + } + llist_for_each_entry(route, &ipa_proxy_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; +} + +DEFUN(ournode_exit, + ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n") +{ + switch (vty->node) { + case IPA_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + } + return CMD_SUCCESS; +} + +DEFUN(ournode_end, + ournode_end_cmd, "end", "End current mode and change to enable mode.\n") +{ + switch (vty->node) { + case IPA_NODE: + break; + } + return CMD_SUCCESS; +} + +void ipa_proxy_vty_init(void) +{ + tall_ipa_proxy_ctx = + talloc_named_const(libosmo_abis_ctx, 1, "ipa_proxy"); + + 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); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index c27342d..5cb6447 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,7 +2,9 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) -noinst_PROGRAMS = e1inp_ipa_bsc_test e1inp_ipa_bts_test +noinst_PROGRAMS = e1inp_ipa_bsc_test \ + e1inp_ipa_bts_test \ + ipa_proxy_test e1inp_ipa_bsc_test_SOURCES = e1inp_ipa_bsc_test.c e1inp_ipa_bsc_test_LDADD = $(top_builddir)/src/libosmoabis.la \ @@ -11,3 +13,7 @@ e1inp_ipa_bsc_test_LDADD = $(top_builddir)/src/libosmoabis.la \ e1inp_ipa_bts_test_SOURCES = e1inp_ipa_bts_test.c e1inp_ipa_bts_test_LDADD = $(top_builddir)/src/libosmoabis.la \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) + +ipa_proxy_test_SOURCES = ipa_proxy_test.c +ipa_proxy_test_LDADD = $(top_builddir)/src/libosmoabis.la \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) diff --git a/tests/ipa_proxy_test.c b/tests/ipa_proxy_test.c new file mode 100644 index 0000000..cea8e2b --- /dev/null +++ b/tests/ipa_proxy_test.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" +#include "config.h" + +static void *tall_test; + +#define DIPA_PROXY_TEST OSMO_LOG_SS_APPS + +struct log_info_cat ipa_proxy_test_cat[] = { + [DIPA_PROXY_TEST] = { + .name = "DINP_IPA_PROXY_TEST", + .description = "IPA proxy test", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, +}; + +const struct log_info ipa_proxy_test_log_info = { + .filter_fn = NULL, + .cat = ipa_proxy_test_cat, + .num_cat = ARRAY_SIZE(ipa_proxy_test_cat), +}; + +static int bsc_vty_is_config_node(struct vty *vty, int node) +{ + switch(node) { + case IPA_NODE: + return 1; + break; + } + return 0; +} + +static enum node_type bsc_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + case IPA_NODE: + vty->node = VIEW_NODE; + break; + } + return vty->node; +} + +static struct vty_app_info vty_info = { + .name = "ipa-proxy-test", + .version = PACKAGE_VERSION, + .go_parent_cb = bsc_vty_go_parent, + .is_config_node = bsc_vty_is_config_node, +}; + +#define IPA_PROXY_TEST_TELNET_PORT 4260 + +int main(void) +{ + tall_test = talloc_named_const(NULL, 1, "ipa proxy test"); + libosmo_abis_init(tall_test); + + osmo_init_logging(&ipa_proxy_test_log_info); + + vty_init(&vty_info); + ipa_proxy_vty_init(); + + telnet_init(tall_test, NULL, IPA_PROXY_TEST_TELNET_PORT); + + LOGP(DIPA_PROXY_TEST, LOGL_NOTICE, "entering main loop\n"); + + while (1) { + osmo_select_main(0); + } +}