aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2014-08-17 22:01:45 +0200
committerHarald Welte <laforge@gnumonks.org>2014-08-24 17:28:47 +0200
commit85c7e90f434192e4fd6713d219bc33e0a47f6cde (patch)
treea115b37eebf16cfcbba0aaf6278d64b5a3f49101
parentaf066bab9dac8f52efad4d924b6dcc3bc3c64626 (diff)
A dedicated OML router program (osmobts-omlrouter)
The idea of this OML router is to be the entity that connects the OML link to the BSC. osmo-bts as well as other programs like sysmobts-mgr and possibly more will then connect to the OML-router rather than the BSC. The point is that those "OML clients" can then register for certain OML messages (particularly the managed objects like TRX, etc.) which then get routed to them. This is particularly useful in the context of 'stacked' multi-TRX BTSs, where we will have two osmo-bts processes, one on each of the TRX, both connecting OML to the oml-router. Through their respective subscription of the baseband transceiver OML object, they will get routed the IPA CONNECT RSL message from the BSC, and both osmo-bts processes will establish independent RSL connections to the BSC.
-rw-r--r--.gitignore1
-rw-r--r--include/osmo-bts/Makefile.am2
-rw-r--r--include/osmo-bts/oml_router_ctrl.h30
-rw-r--r--include/osmo-bts/oml_routing.h51
-rw-r--r--include/osmo-bts/vty.h1
-rw-r--r--src/common/Makefile.am6
-rw-r--r--src/common/msg_utils.c18
-rw-r--r--src/common/oml_router.c875
-rw-r--r--src/common/oml_router_ctrl.c95
-rw-r--r--src/common/oml_routing.c294
10 files changed, 1364 insertions, 9 deletions
diff --git a/.gitignore b/.gitignore
index 83513493..5d656c0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@ core.*
contrib/sysmobts-calib/sysmobts-calib
+src/common/osmobts-omlrouter
src/osmo-bts-sysmo/l1fwd-proxy
src/osmo-bts-sysmo/sysmobts
src/osmo-bts-sysmo/sysmobts-remote
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index b2941444..7fb14f7f 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -1,3 +1,3 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
- handover.h msg_utils.h tx_power.h
+ handover.h msg_utils.h tx_power.h oml_routing.h oml_router_ctrl.h
diff --git a/include/osmo-bts/oml_router_ctrl.h b/include/osmo-bts/oml_router_ctrl.h
new file mode 100644
index 00000000..7fe71dff
--- /dev/null
+++ b/include/osmo-bts/oml_router_ctrl.h
@@ -0,0 +1,30 @@
+#ifndef OML_ROUTER_CTRL_H
+#define OML_ROUTER_CTRL_H
+
+enum osmo_omlrctrl_msgtype {
+ OSMO_ORC_MSGT_REGISTER_REQ = 1,
+ OSMO_ORC_MSGT_REGISTER_ACK = 2,
+ OSMO_ORC_MSGT_REGISTER_NACK = 3,
+
+ OSMO_ORC_MSGT_ROUTE_ADD_REQ = 4,
+ OSMO_ORC_MSGT_ROUTE_ADD_ACK = 5,
+ OSMO_ORC_MSGT_ROUTE_ADD_NACK = 6,
+
+ OSMO_ORC_MSGT_ROUTE_DEL_REQ = 7,
+ OSMO_ORC_MSGT_ROUTE_DEL_ACK = 8,
+ OSMO_ORC_MSGT_ROUTE_DEL_NACK = 9,
+};
+
+struct osmo_omlrctrl_hdr {
+ uint8_t version;
+ uint8_t msg_type;
+ uint16_t data_len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct osmo_omlrctrl_register_req {
+ uint8_t name_len;
+ char name[0];
+} __attribute__((packed));
+
+#endif
diff --git a/include/osmo-bts/oml_routing.h b/include/osmo-bts/oml_routing.h
new file mode 100644
index 00000000..9d50881f
--- /dev/null
+++ b/include/osmo-bts/oml_routing.h
@@ -0,0 +1,51 @@
+#ifndef OML_ROUTING_H
+#define OML_ROUTING_H
+
+#include <stdint.h>
+#include <osmocom/gsm/abis_nm.h>
+
+enum oml_routing_flags {
+ OML_RTF_MDISC = 0x00000001,
+ OML_RTF_OBJ_CLASS = 0x00000002,
+ OML_RTF_BTS_NR = 0x00000004,
+ OML_RTF_TRX_NR = 0x00000008,
+ OML_RTF_TS_NR = 0x00000010,
+ OML_RTF_VENDOR = 0x00000020,
+};
+
+struct oml_routing_key {
+ uint8_t mdisc; /* abis_om_hdr.mdisc */
+ uint8_t obj_class; /* abis_om_fom_hdr.obj_class */
+ struct abis_om_obj_inst obj_inst; /* abis_om_fom_hdr.obj_inst */
+ uint8_t vendor_lv[64]; /* vendor length-value */
+} __attribute__ ((packed));
+
+struct oml_route {
+ uint32_t flags; /* bitmask of oml_routing_flags */
+ struct oml_routing_key key;
+} __attribute ((packed));
+
+struct oml_routing_inst;
+struct oml_client;
+
+/* an OML route as it is used internally */
+struct oml_route_entry {
+ struct llist_head list;
+ struct oml_route route;
+ struct oml_client *client;
+};
+
+int oml_route_add(struct oml_routing_inst *inst, const struct oml_route *route,
+ struct oml_client *client);
+
+int oml_route_del(struct oml_routing_inst *inst, const struct oml_route *route);
+int oml_route_del_client(struct oml_routing_inst *inst, const struct oml_client *client);
+
+void *oml_route(struct oml_routing_inst *inst,
+ const struct oml_routing_key *key);
+
+struct oml_routing_inst *oml_route_init(void *ctx, void *priv);
+
+char *oml_route_client_name(struct oml_client *client);
+
+#endif
diff --git a/include/osmo-bts/vty.h b/include/osmo-bts/vty.h
index 1bc7e718..48303d45 100644
--- a/include/osmo-bts/vty.h
+++ b/include/osmo-bts/vty.h
@@ -7,6 +7,7 @@
enum bts_vty_node {
BTS_NODE = _LAST_OSMOVTY_NODE + 1,
TRX_NODE,
+ OMLR_NODE,
};
extern struct cmd_element ournode_exit_cmd;
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 10627e2a..c9173158 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -8,3 +8,9 @@ libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c
+
+bin_PROGRAMS = osmobts-omlrouter
+
+osmobts_omlrouter_SOURCES = oml_router.c msg_utils.c oml_routing.c
+osmobts_omlrouter_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOGSM_LIBS)
+
diff --git a/src/common/msg_utils.c b/src/common/msg_utils.c
index f3cba090..22a3012a 100644
--- a/src/common/msg_utils.c
+++ b/src/common/msg_utils.c
@@ -103,13 +103,6 @@ int msg_verify_ipa_structure(struct msgb *msg)
hh = (struct ipaccess_head *) msg->l1h;
- if (hh->proto != IPAC_PROTO_OML) {
- LOGP(DL1C, LOGL_ERROR,
- "Incorrect ipa header protocol 0x%x 0x%x\n",
- hh->proto, IPAC_PROTO_OML);
- return -1;
- }
-
if (ntohs(hh->len) != msgb_l1len(msg) - sizeof(struct ipaccess_head)) {
LOGP(DL1C, LOGL_ERROR,
"Incorrect ipa header msg size %d %d\n",
@@ -117,7 +110,16 @@ int msg_verify_ipa_structure(struct msgb *msg)
return -1;
}
- msg->l2h = hh->data;
+ if (hh->proto == IPAC_PROTO_OSMO) {
+ struct ipaccess_head_ext *hh_ext = (struct ipaccess_head_ext *) hh->data;
+ if (ntohs(hh->len) < sizeof(*hh_ext)) {
+ LOGP(DL1C, LOGL_ERROR, "IPA length shorter than OSMO header\n");
+ return -1;
+ }
+ msg->l2h = hh_ext->data;
+ } else
+ msg->l2h = hh->data;
+
return 0;
}
diff --git a/src/common/oml_router.c b/src/common/oml_router.c
new file mode 100644
index 00000000..b9dbdb1b
--- /dev/null
+++ b/src/common/oml_router.c
@@ -0,0 +1,875 @@
+/* OML Message Router (server side) */
+
+/* (C) 2014 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/macaddr.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/abis/abis.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/ipa.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+#include <osmocom/gsm/ipa.h>
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/telnet_interface.h>
+
+#include <osmo-bts/logging.h>
+#include <osmo-bts/msg_utils.h>
+#include <osmo-bts/oml_routing.h>
+#include <osmo-bts/oml_router_ctrl.h>
+#include <osmo-bts/vty.h>
+
+#include "../../btsconfig.h"
+
+static const char *config_file = "osmobts-omlrouter.cfg";
+static int daemonize = 0;
+
+enum bsc_link_state {
+ BSC_LS_WAIT_RECONNECT,
+ BSC_LS_CONNECTING,
+ BSC_LS_CONNECTED,
+};
+
+static const struct value_string bsc_ls_names[] = {
+ { BSC_LS_WAIT_RECONNECT, "waiting for reconnect" },
+ { BSC_LS_CONNECTING, "attempting to connect" },
+ { BSC_LS_CONNECTED, "connected" },
+ { 0, NULL }
+};
+
+struct oml_router {
+ /* config */
+ char *bsc_oml_host;
+ uint16_t bsc_oml_port;
+ char *listen_host;
+ uint16_t listen_port;
+ unsigned int bsc_reconnect_secs;
+
+ /* state */
+
+ struct oml_routing_inst *routing;
+ /* state of the BSC connection */
+ enum bsc_link_state bsc_link_state;
+ /* BSC connection */
+ struct ipa_client_conn *bsc_conn;
+ /* BSC re-connection timer */
+ struct osmo_timer_list bsc_recon_timer;
+ /* server listening for clients */
+ struct ipa_server_link *server_link;
+ /* list of clients connected to us */
+ struct llist_head clients;
+};
+
+struct oml_client {
+ struct llist_head list;
+ struct ipa_server_conn *conn;
+ struct oml_router *router;
+ struct ipaccess_unit *unit_data;
+ char *name;
+};
+
+char *oml_route_client_name(struct oml_client *cl)
+{
+ struct ipaccess_unit *ud = cl->unit_data;
+ static char outbuf[256];
+
+ snprintf(outbuf, sizeof(outbuf)-1, "%d/%d/%d(%s)",
+ ud->site_id, ud->bts_id, ud->trx_id,
+ cl->name ? cl->name : "NULL");
+ outbuf[sizeof(outbuf)-1] = '\0';
+
+ return outbuf;
+}
+
+char *oml_route_client_addr(struct oml_client *cl)
+{
+ static char outbuf[256];
+ struct sockaddr_in sin;
+ socklen_t slen = sizeof(sin);
+
+ getpeername(cl->conn->ofd.fd, (struct sockaddr *) &sin, &slen);
+ snprintf(outbuf, sizeof(outbuf)-1, "%s:%u",
+ inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ outbuf[sizeof(outbuf)-1] = '\0';
+
+ return outbuf;
+}
+
+static struct ipaccess_unit bts_dev_info = {
+ .unit_name = "sysmoBTS",
+ .equipvers = "", /* FIXME: read this from hw */
+ .swversion = PACKAGE_VERSION,
+ .location1 = "",
+ .location2 = "",
+ .serno = "", /* FIXME: read this from hw */
+};
+
+/* We shouldn't need this, but there's no 'private data' inside an e1inp_line */
+static struct oml_router *g_inst;
+
+#define ORC_HEADROOM 20
+
+static struct msgb *orc_msgb_alloc(void)
+{
+ unsigned int headroom;
+
+ headroom = ORC_HEADROOM + sizeof(struct ipaccess_head) +
+ sizeof(struct ipaccess_head_ext);
+
+ return msgb_alloc_headroom(1200 + headroom, headroom, "OML Router Ctrl");
+}
+
+/* push ipa_ext and ipa header */
+static void orc_push_header(struct msgb *msg)
+{
+ struct ipaccess_head_ext *he;
+
+ he = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*he));
+ he->proto = IPAC_PROTO_EXT_ORC;
+
+ ipa_msg_push_header(msg, IPAC_PROTO_OSMO);
+}
+
+/* push ipa_ext and ipa header + send to client */
+static void orc_client_send(struct oml_client *client, struct msgb *msg)
+{
+ orc_push_header(msg);
+ ipa_server_conn_send(client->conn, msg);
+}
+
+/* Send ORC ACK to client */
+static void client_send_ack(struct oml_client *client,
+ enum osmo_omlrctrl_msgtype type)
+{
+ struct msgb *msg = orc_msgb_alloc();
+ struct osmo_omlrctrl_hdr *oh;
+
+ oh = (struct osmo_omlrctrl_hdr *) msgb_put(msg, sizeof(*oh));
+ oh->version = 0;
+ oh->msg_type = type;
+ oh->data_len = 0;
+
+ orc_client_send(client, msg);
+}
+
+/* Send ORC NACK to client */
+static void client_send_nack(struct oml_client *client,
+ enum osmo_omlrctrl_msgtype type,
+ uint8_t cause, char *diag)
+{
+ struct msgb *msg = orc_msgb_alloc();
+ struct osmo_omlrctrl_hdr *oh;
+ unsigned int d_len = 0, payload_len = 1;
+
+ /* optional diagnostics LV at end of message */
+ if (diag) {
+ d_len = strlen(diag);
+ payload_len += d_len + 2; /* NULL + Length */
+ }
+
+ oh = (struct osmo_omlrctrl_hdr *)
+ msgb_put(msg, sizeof(*oh)+payload_len);
+ oh->version = 0;
+ oh->msg_type = type;
+ oh->data_len = payload_len;
+ /* first byte is cause */
+ oh->data[0] = cause;
+ /* followed by optional diagnostics string as LV */
+ if (d_len) {
+ oh->data[1] = d_len;
+ memcpy(oh->data+2, diag, d_len);
+ }
+
+ orc_client_send(client, msg);
+}
+
+/* process incoming ORC REGISTER recquest from client */
+static int client_rx_orc_register(struct oml_client *client,
+ struct msgb *msg)
+{
+ struct osmo_omlrctrl_register_req *rr =
+ (struct osmo_omlrctrl_register_req *) msgb_l3(msg);
+
+ if (msgb_l3len(msg) < sizeof(*rr)) {
+ client_send_nack(client, OSMO_ORC_MSGT_REGISTER_NACK,
+ 0xff, "Short header");
+ return -1;
+ }
+
+ if (rr->name_len && rr->name[rr->name_len-1] != '\0') {
+ client_send_nack(client, OSMO_ORC_MSGT_REGISTER_NACK,
+ 0xff, "Name without NULL termination");
+ return -1;
+ }
+
+ /* store client identity parameters */
+ if (rr->name_len) {
+ talloc_free(client->name);
+ client->name = talloc_strndup(client, rr->name, rr->name_len);
+ }
+
+ LOGP(DOML, LOGL_INFO, "Client %s registered as %s\n",
+ oml_route_client_addr(client),
+ oml_route_client_name(client));
+
+ client_send_ack(client, OSMO_ORC_MSGT_REGISTER_ACK);
+
+ return 0;
+}
+
+/* process incoming ORC message from client */
+static int ipa_omlrouter_rcvmsg(struct oml_client *client, struct msgb *msg)
+{
+ struct osmo_omlrctrl_hdr *oh =
+ (struct osmo_omlrctrl_hdr *) msgb_l2(msg);
+ struct oml_route *rt;
+ int rc;
+
+ /* FIXME: size / consistency checks */
+ msg->l3h = oh->data;
+
+ switch (oh->msg_type) {
+ case OSMO_ORC_MSGT_REGISTER_REQ:
+ rc = client_rx_orc_register(client, msg);
+ break;
+ case OSMO_ORC_MSGT_ROUTE_ADD_REQ:
+ DEBUGP(DOML, "route add request\n");
+ if (oh->data_len < sizeof(*rt)) {
+ LOGP(DOML, LOGL_ERROR, "route too small (%u < %lu)\n",
+ oh->data_len, sizeof(*rt));
+ client_send_nack(client, OSMO_ORC_MSGT_ROUTE_ADD_NACK,
+ 0, "Route size mismatch");
+ goto err;
+ }
+ rt = (struct oml_route *) &oh->data;
+ rc = oml_route_add(client->router->routing, rt, client);
+ if (rc < 0)
+ client_send_nack(client, OSMO_ORC_MSGT_ROUTE_ADD_NACK,
+ -rc, "Could not add route");
+ else
+ client_send_ack(client, OSMO_ORC_MSGT_ROUTE_ADD_ACK);
+ break;
+ case OSMO_ORC_MSGT_ROUTE_DEL_REQ:
+ if (oh->data_len <= sizeof(*rt))
+ goto err;
+ rt = (struct oml_route *) &oh->data;
+ rc = oml_route_del(client->router->routing, rt);
+ if (rc < 0)
+ client_send_nack(client, OSMO_ORC_MSGT_ROUTE_DEL_NACK,
+ -rc, "Could not delete route");
+ else
+ client_send_ack(client, OSMO_ORC_MSGT_ROUTE_DEL_ACK);
+ break;
+ }
+
+err:
+ msgb_free(msg);
+ return rc;
+}
+
+static int client_rx_ipa_ccm(struct oml_client *client, struct msgb *msg)
+{
+ struct tlv_parsed tp;
+ uint8_t msg_type = *(msg->l2h);
+ int rc;
+
+ /* deal with PING/PONG/ACK */
+ rc = ipa_ccm_rcvmsg_base(msg, &client->conn->ofd);
+ if (rc == 1) {
+ /* message was handled by rcvmsg_base */
+ return 0;
+ }
+
+ /* messages that we have to handle locally */
+ switch (msg_type) {
+ case IPAC_MSGT_ID_RESP:
+ rc = ipa_ccm_idtag_parse(&tp, msgb_l2(msg)+2,
+ msgb_l2len(msg)-2);
+ if (rc < 0)
+ return rc;
+
+ /* store result in client structure */
+ rc = ipa_ccm_tlv_to_unitdata(client->unit_data, &tp);
+ if (rc < 0)
+ return rc;
+
+ if (client->unit_data->site_id != bts_dev_info.site_id) {
+ LOGP(DOML, LOGL_ERROR, "Client %s Wrong Site ID "
+ "(client %u != our %u); Closing\n",
+ oml_route_client_addr(client),
+ client->unit_data->site_id, bts_dev_info.site_id);
+ ipa_server_conn_destroy(client->conn);
+ return -1;
+ }
+ break;
+ default:
+ LOGP(DOML, LOGL_NOTICE, "Not handled IPA CCM from client\n");
+ return -1;
+ }
+
+ return rc;
+}
+
+/* one of the clients has sent data to us */
+static int client_data_cb(struct ipa_server_conn *conn, struct msgb *msg)
+{
+ struct oml_client *client = conn->data;
+ struct ipaccess_head *hh = (struct ipaccess_head *) msgb_l1(msg);
+ struct ipaccess_head_ext *hh_ext;
+ int rc;
+
+ DEBUGP(DOML, "Received data from client: %s\n",
+ osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+
+ /* regular message handling */
+ rc = msg_verify_ipa_structure(msg);
+ if (rc < 0) {
+ LOGP(DOML, LOGL_ERROR,
+ "Invalid IPA message from client (rc=%d)\n", rc);
+ goto err;
+ }
+
+ switch (hh->proto) {
+ case IPAC_PROTO_IPACCESS:
+ rc = client_rx_ipa_ccm(client, msg);
+ msgb_free(msg);
+ break;
+ case IPAC_PROTO_OSMO:
+ hh_ext = (struct ipaccess_head_ext *) hh->data;
+ switch (hh_ext->proto) {
+ case IPAC_PROTO_EXT_ORC:
+ rc = ipa_omlrouter_rcvmsg(client, msg);
+ break;
+ default:
+ LOGP(DOML, LOGL_NOTICE,
+ "Unknown IPA-EXT protocol %u\n", hh_ext->proto);
+ msgb_free(msg);
+ break;
+ }
+ break;
+ case IPAC_PROTO_OML:
+ rc = msg_verify_oml_structure(msg);
+ if (rc < 0) {
+ LOGP(DOML, LOGL_ERROR,
+ "Invalid OML message from Client (rc=%d)\n", rc);
+ goto err;
+ }
+ /* forward message to BSC */
+ ipa_client_conn_send(client->router->bsc_conn, msg);
+ break;
+ default:
+ LOGP(DOML, LOGL_NOTICE, "Unhandled stream ID %u from client\n", hh->proto);
+ msgb_free(msg);
+ break;
+ }
+
+ return 0;
+err:
+ msgb_free(msg);
+ return -1;
+}
+
+/* a client connection was lost */
+static int client_closed_cb(struct ipa_server_conn *conn)
+{
+ struct oml_client *client = conn->data;
+
+ oml_route_del_client(client->router->routing, client);
+
+ llist_del(&client->list);
+ talloc_free(client);
+
+ return 0;
+}
+
+/* a new client has connected to our server */
+static int server_accept_cb(struct ipa_server_link *link, int fd)
+{
+ struct oml_router *inst = link->data;
+ struct oml_client *client = talloc_zero(link->data, struct oml_client);
+
+ client->unit_data = talloc_zero(client, struct ipaccess_unit);
+ client->router = inst;
+
+ client->conn = ipa_server_conn_create(link, link, fd, client_data_cb,
+ client_closed_cb, client);
+ if (!client->conn) {
+ talloc_free(client);
+ return -1;
+ }
+
+ llist_add_tail(&client->list, &inst->clients);
+
+ /* send IPA ID REQ */
+ ipa_ccm_send_id_req(fd);
+
+ return 0;
+}
+
+/* schedule a re-connect towards the BSC */
+static void reschedule_bsc_connect(struct oml_router *inst)
+{
+ DEBUGP(DOML, "Re-scheduling BSC connect\n");
+ osmo_timer_schedule(&inst->bsc_recon_timer, inst->bsc_reconnect_secs, 0);
+ inst->bsc_link_state = BSC_LS_CONNECTING;
+}
+
+/* BSC re-connect timer call-back */
+static void bsc_recon_timer_cb(void *data)
+{
+ struct oml_router *inst = data;
+
+ if (ipa_client_conn_open(inst->bsc_conn) < 0) {
+ LOGP(DOML, LOGL_NOTICE, "failed to connect to BSC\n");
+ reschedule_bsc_connect(inst);
+ }
+}
+
+
+/* link to BSC has gone up or down */
+static void bsc_updown_cb(struct ipa_client_conn *link, int up)
+{
+ struct oml_router *inst = link->data;
+
+ LOGP(DOML, LOGL_INFO, "BSC connection %s\n", up ? "up" : "down");
+
+ if (up) {
+ inst->bsc_link_state = BSC_LS_CONNECTED;
+ /* FIXME */
+ } else {
+ reschedule_bsc_connect(link->data);
+ inst->bsc_link_state = BSC_LS_WAIT_RECONNECT;
+ }
+}
+
+/* derive routing key from OML message */
+static void key_from_omlmsg(struct oml_routing_key *key, const struct msgb *msg)
+{
+ struct abis_om_hdr *oh = (struct abis_om_hdr *) msgb_l2(msg);
+ struct abis_om_fom_hdr *foh;
+
+ memset(key, 0, sizeof(*key));
+
+ key->mdisc = oh->mdisc;
+ switch (oh->mdisc) {
+ case ABIS_OM_MDISC_FOM:
+ foh = (struct abis_om_fom_hdr *) oh->data;
+ key->obj_class = foh->obj_class;
+ memcpy(&key->obj_inst, &foh->obj_inst, sizeof(key->obj_inst));
+ break;
+ case ABIS_OM_MDISC_MANUF:
+ break;
+ }
+}
+
+/* incoming data from the BSC */
+static int bsc_read_cb(struct ipa_client_conn *link, struct msgb *msg)
+{
+ int rc;
+ struct ipaccess_head *hh = (struct ipaccess_head *) msgb_l1(msg);
+ struct oml_router *inst = link->data;
+ struct oml_client *client;
+ struct oml_routing_key key;
+
+ DEBUGP(DOML, "Received data from BSC: %s\n",
+ osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+
+ /* regular message handling */
+ rc = msg_verify_ipa_structure(msg);
+ if (rc < 0) {
+ LOGP(DOML, LOGL_ERROR,
+ "Invalid IPA message from BSC (rc=%d)\n", rc);
+ goto err;
+ }
+
+ switch (hh->proto) {
+ case IPAC_PROTO_IPACCESS:
+ /* handle the core IPA CCM messages in libosmoabis */
+ rc = ipaccess_bts_handle_ccm(link, &bts_dev_info, msg);
+ if (rc == 0)
+ LOGP(DOML, LOGL_NOTICE, "Not handled IPA CCM from BSC\n");
+ else if (rc < 0)
+ LOGP(DOML, LOGL_ERROR, "Error handling IPA CCM from BSC\n");
+ msgb_free(msg);
+ break;
+ case IPAC_PROTO_OML:
+ rc = msg_verify_oml_structure(msg);
+ if (rc < 0) {
+ LOGP(DOML, LOGL_ERROR,
+ "Invalid OML message from BSC (rc=%d)\n", rc);
+ goto err;
+ }
+
+ /* find route */
+ key_from_omlmsg(&key, msg);
+ client = oml_route(inst->routing, &key);
+ if (!client) {
+ LOGP(DOML, LOGL_ERROR, "Cannot find route!\n");
+ goto err;
+ }
+ /* send OML message to respective client */
+ DEBUGP(DOML, "Routed to client %s\n",
+ oml_route_client_name(client));
+ ipa_server_conn_send(client->conn, msg);
+ break;
+ default:
+ LOGP(DOML, LOGL_NOTICE,
+ "Unhandled stream ID %u from BSC\n", hh->proto);
+ msgb_free(msg);
+ break;
+ }
+
+ return 0;
+err:
+ msgb_free(msg);
+ return -1;
+}
+
+DEFUN(show_bsc_link, show_bsc_link_cmd,
+ "show bsc-link",
+ SHOW_STR "Display information about our link to the BSC\n")
+{
+ vty_out(vty, "BSC OML destination at %s:%u, State: %s%s",
+ g_inst->bsc_oml_host, g_inst->bsc_oml_port,
+ get_value_string(bsc_ls_names, g_inst->bsc_link_state),
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_clients, show_clients_cmd,
+ "show oml-clients",
+ SHOW_STR "Display information about OML clients\n")
+{
+ struct oml_client *cl;
+
+ llist_for_each_entry(cl, &g_inst->clients, list) {
+ struct ipaccess_unit *ud = cl->unit_data;
+ vty_out(vty, "Client '%s' from %s%s",
+ oml_route_client_name(cl),
+ oml_route_client_addr(cl), VTY_NEWLINE);
+ vty_out(vty, " Name '%s', Serno '%s'%s",
+ ud->unit_name, ud->serno, VTY_NEWLINE);
+ vty_out(vty, " CCM Equipment Version: '%s', SW Version: '%s'%s",
+ ud->equipvers, ud->swversion, VTY_NEWLINE);
+ vty_out(vty, " CCM Location: '%s' / '%s'%s",
+ ud->location1, ud->location2, VTY_NEWLINE);
+
+ }
+
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node omlr_node = {
+ OMLR_NODE,
+ "%s(oml-router)# ",
+ 1,
+};
+
+DEFUN(cfg_omlr, cfg_omlr_cmd,
+ "oml-router",
+ "Configure the OML Router\n")
+{
+ vty->index = g_inst;
+ vty->node = OMLR_NODE;
+
+ return CMD_SUCCESS;
+}
+
+#define REMOTE_STR "Configuration of outbound OML connection\n"
+#define LOCAL_STR "Configuration of OML server for inbound connections\n"
+
+DEFUN(cfg_oml_remote_ip, cfg_oml_remote_ip_cmd,
+ "remote connect-ip A.B.C.D", REMOTE_STR
+ "Configure remote (BSC) IP address for OML\n"
+ "IP address of BSC\n")
+{
+ struct oml_router *inst = vty->index;
+
+ if (inst->bsc_oml_host)
+ talloc_free(inst->bsc_oml_host);
+ inst->bsc_oml_host = talloc_strdup(inst, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_oml_remote_port, cfg_oml_remote_port_cmd,
+ "remote connect-port <0-65535>", REMOTE_STR
+ "Configure remote (BSC) TCP port for OML\n")
+{
+ struct oml_router *inst = vty->index;
+
+ inst->bsc_oml_port = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_oml_reconnect_secs, cfg_oml_reconnect_secs_cmd,
+ "remote reconnect-timer <0-65535>", REMOTE_STR
+ "Reconnect interval for OML connection to BSC\n")
+{
+ struct oml_router *inst = vty->index;
+
+ inst->bsc_reconnect_secs = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN(cfg_oml_local_ip, cfg_oml_local_ip_cmd,
+ "local listen-ip A.B.C.D", LOCAL_STR
+ "Configure local listen IP address for incoming OML connections\n"
+ "local IP address for incoming OML connections\n")
+{
+ struct oml_router *inst = vty->index;
+
+ if (inst->listen_host)
+ talloc_free(inst->listen_host);
+ inst->listen_host = talloc_strdup(inst, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_oml_local_port, cfg_oml_local_port_cmd,
+ "local listen-port <0-65535>", LOCAL_STR
+ "Configure local listen TCP port for incoming OML connections\n"
+ "local TCP port for incoming OML connections\n")
+{
+ struct oml_router *inst = vty->index;
+
+ inst->listen_port = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ipa_unit_id, cfg_ipa_unit_id_cmd,
+ "ipa unit-id <0-65535> <0-255>",
+ "Configure IPA parameters\n"
+ "Site ID for this OML router\n"
+ "BTS ID for this OML router\n")
+{
+ bts_dev_info.site_id = atoi(argv[0]);
+ bts_dev_info.bts_id = atoi(argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_omlr(struct vty *vty)
+{
+ struct oml_router *inst = g_inst;
+
+ vty_out(vty, "oml-router%s", VTY_NEWLINE);
+ vty_out(vty, " ipa unit-id %u %u%s", bts_dev_info.site_id,
+ bts_dev_info.bts_id, VTY_NEWLINE);
+ vty_out(vty, " local listen-ip %s%s",
+ inst->listen_host, VTY_NEWLINE);
+ vty_out(vty, " local listen-port %u%s",
+ inst->listen_port, VTY_NEWLINE);
+ vty_out(vty, " remote connect-ip %s%s",
+ inst->bsc_oml_host, VTY_NEWLINE);
+ vty_out(vty, " remote connect-port %u%s",
+ inst->bsc_oml_port, VTY_NEWLINE);
+ vty_out(vty, " remote reconnect-timer %u%s",
+ inst->bsc_reconnect_secs, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+static void *router_ctx;
+
+struct log_info_cat router_cat[] = {
+ [DOML] = {
+ .name = "OML",
+ .description = "A-bis OML (TS 12.21)",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+};
+
+static struct log_info router_log_info = {
+ .filter_fn = NULL,
+ .cat = router_cat,
+ .num_cat = ARRAY_SIZE(router_cat),
+};
+
+static enum node_type omlr_vty_go_parent(struct vty *vty)
+{
+ switch (vty->node) {
+ case OMLR_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ default:
+ vty->node = CONFIG_NODE;
+ }
+ return vty->node;
+}
+
+static int omlr_vty_is_config_node(struct vty *vty, int node)
+{
+ switch (node) {
+ case OMLR_NODE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const char copyright[] =
+ "Copyright (C) 2014 by Harald Welte\r\n"
+ "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+
+static struct vty_app_info vty_info = {
+ .name = "OsmoBTS-OMLrouter",
+ .version = PACKAGE_VERSION,
+ .copyright = copyright,
+ .go_parent_cb = omlr_vty_go_parent,
+ .is_config_node = omlr_vty_is_config_node,
+};
+
+static void signal_handler(int signal)
+{
+ fprintf(stderr, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGUSR1:
+ case SIGUSR2:
+ talloc_report_full(router_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct oml_router *inst;
+ int rc;
+
+ router_ctx = talloc_named_const(NULL, 1, "OML router");
+
+ g_inst = inst = talloc_zero(router_ctx, struct oml_router);
+
+ osmo_get_macaddr(bts_dev_info.mac_addr, "eth0");
+
+ inst->bsc_oml_host = talloc_strdup(inst, "");
+ inst->bsc_oml_port = 3002;
+ inst->listen_host = talloc_strdup(inst, "0.0.0.0");
+ inst->listen_port = 3002;
+ inst->bsc_reconnect_secs = 10;
+
+ INIT_LLIST_HEAD(&inst->clients);
+ inst->bsc_recon_timer.cb = bsc_recon_timer_cb;
+ inst->bsc_recon_timer.data = inst;
+
+ libosmo_abis_init(router_ctx);
+
+ osmo_init_logging(&router_log_info);
+
+ //vty_info.tall_ctx = router_ctx;
+ vty_init(&vty_info);
+
+ install_node(&omlr_node, config_write_omlr);
+ install_element(CONFIG_NODE, &cfg_omlr_cmd);
+ install_element(OMLR_NODE, &cfg_oml_remote_ip_cmd);
+ install_element(OMLR_NODE, &cfg_oml_remote_port_cmd);
+ install_element(OMLR_NODE, &cfg_oml_reconnect_secs_cmd);
+ install_element(OMLR_NODE, &cfg_oml_local_ip_cmd);
+ install_element(OMLR_NODE, &cfg_oml_local_port_cmd);
+ install_element(OMLR_NODE, &cfg_ipa_unit_id_cmd);
+
+ install_element_ve(&show_bsc_link_cmd);
+ install_element_ve(&show_clients_cmd);
+ telnet_init(router_ctx, NULL, 4230);
+
+ rc = vty_read_config_file(config_file, NULL);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n",
+ config_file);
+ exit(1);
+ }
+
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGUSR2, &signal_handler);
+ osmo_init_ignore_signals();
+
+ inst->routing = oml_route_init(g_inst, g_inst);
+
+ inst->server_link = ipa_server_link_create(router_ctx, NULL,
+ inst->listen_host, inst->listen_port,
+ server_accept_cb, inst);
+ if (!inst->server_link) {
+ LOGP(DOML, LOGL_ERROR, "Cannot create server instance\n");
+ return -1;
+ }
+
+
+ if (ipa_server_link_open(inst->server_link) < 0) {
+ LOGP(DOML, LOGL_ERROR, "Cannot bind server to TCP port %u\n",
+ inst->listen_port);
+ return -1;
+ }
+
+ inst->bsc_conn = ipa_client_conn_create(router_ctx, NULL, 0,
+ inst->bsc_oml_host,
+ inst->bsc_oml_port,
+ bsc_updown_cb, bsc_read_cb,
+ NULL, inst);
+ if (!inst->bsc_conn) {
+ LOGP(DOML, LOGL_ERROR, "Cannot create client instance\n");
+ return -1;
+ }
+
+ /* attempt tp connect and re-schedule connect if needed */
+ bsc_recon_timer_cb(inst);
+
+ if (daemonize) {
+ rc = osmo_daemonize();
+ if (rc < 0) {
+ perror("Error during daemonize");
+ exit(1);
+ }
+ }
+
+ LOGP(DOML, LOGL_NOTICE, "entering main loop\n");
+
+ while (1) {
+ log_reset_context();
+ osmo_select_main(0);
+ }
+
+ return 0;
+}
diff --git a/src/common/oml_router_ctrl.c b/src/common/oml_router_ctrl.c
new file mode 100644
index 00000000..018c5041
--- /dev/null
+++ b/src/common/oml_router_ctrl.c
@@ -0,0 +1,95 @@
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+
+#include <osmo-bts/oml_routing.h>
+#include <osmo-bts/oml_router_ctrl.h>
+#include <osmo-bts/gsm_data.h>
+
+int abis_orc_sendmsg(struct msgb *msg)
+{
+ struct gsm_bts *bts = msg->trx->bts;
+ struct ipaccess_head_ext *he;
+
+ he = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*he));
+ he->proto = IPAC_PROTO_EXT_ORC;
+
+ /* osmo-bts uses msg->trx internally, but libosmo-abis uses the
+ * signalling like at msg->dst */
+ msg->dst = bts->osmo_link;
+ return abis_sendmsg(msg);
+}
+
+static struct msgb *orc_msgb_alloc(void)
+{
+ return msgb_alloc_headroom(1000+40, 40, "OML Router Ctrl");
+}
+
+static struct msgb *__gen_orc_route(const struct oml_route *rt_in, int del)
+{
+ struct msgb *msg = orc_msgb_alloc();
+ struct osmo_omlrctrl_hdr *oh;
+ struct oml_route *rt;
+
+ oh = (struct osmo_omlrctrl_hdr *) msgb_put(msg, sizeof(*oh));
+ rt = (struct oml_route *) msgb_put(msg, sizeof(*rt));
+
+ oh->version = 0;
+ if (del)
+ oh->msg_type = OSMO_ORC_MSGT_ROUTE_DEL_REQ;
+ else
+ oh->msg_type = OSMO_ORC_MSGT_ROUTE_ADD_REQ;
+ oh->data_len = sizeof(*rt);
+
+ memcpy(rt, rt_in, sizeof (*rt));
+
+ return msg;
+}
+
+struct msgb *gen_orc_route_add(const struct oml_route *rt_in)
+{
+ return __gen_orc_route(rt_in, 0);
+}
+
+struct msgb *gen_orc_route_del(const struct oml_route *rt_in)
+{
+ return __gen_orc_route(rt_in, 1);
+}
+
+int orc_add_route_mo(struct gsm_abis_mo *mo)
+{
+ struct oml_route rt;
+ struct msgb *msg;
+
+ rt.flags = OML_RTF_MDISC | OML_RTF_OBJ_CLASS;
+ rt.key.mdisc = ABIS_OM_MDISC_FOM;
+ rt.key.obj_class = mo->obj_class;
+
+ memcpy(&rt.key.obj_inst, &mo->obj_inst, sizeof(rt.key.obj_inst));
+
+ /* do w really need this? why not simply register for 0xFF ... */
+ switch (mo->obj_class) {
+ case NM_OC_SITE_MANAGER:
+ break;
+ case NM_OC_BTS:
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ rt.flags |= OML_RTF_BTS_NR;
+ break;
+ case NM_OC_RADIO_CARRIER:
+ case NM_OC_BASEB_TRANSC:
+ case NM_OC_GPRS_NSVC:
+ rt.flags |= OML_RTF_BTS_NR | OML_RTF_TRX_NR;
+ break;
+ case NM_OC_CHANNEL:
+ rt.flags |= OML_RTF_BTS_NR | OML_RTF_TRX_NR | OML_RTF_TS_NR;
+ break;
+ }
+
+ msg = gen_orc_route_add(&rt);
+ if (!msg)
+ return -1;
+
+ msg->trx = mo->bts->c0;
+ return abis_orc_sendmsg(msg);
+}
diff --git a/src/common/oml_routing.c b/src/common/oml_routing.c
new file mode 100644
index 00000000..3cab76ee
--- /dev/null
+++ b/src/common/oml_routing.c
@@ -0,0 +1,294 @@
+/* OML Message routing for osmo-bts */
+
+/* (C) 2014 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gsm/abis_nm.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+
+#include <osmo-bts/logging.h>
+#include <osmo-bts/oml_routing.h>
+
+
+/* an OML router instance */
+struct oml_routing_inst {
+ struct llist_head routes;
+ void *priv;
+};
+
+/* FIXME: This must go! */
+static struct oml_routing_inst *g_inst;
+
+/* match given routing key against given route */
+static int oml_route_match(const struct oml_routing_key *key,
+ const struct oml_route *route)
+{
+ if (route->flags & OML_RTF_MDISC)
+ if (route->key.mdisc != key->mdisc)
+ return 0;
+
+ if (route->flags & OML_RTF_OBJ_CLASS) {
+ if (route->key.obj_class != key->obj_class)
+ return 0;
+ if (route->key.obj_class == ABIS_OM_MDISC_MANUF &&
+ route->flags & OML_RTF_VENDOR) {
+ if (route->key.vendor_lv[0] != key->vendor_lv[0])
+ return 0;
+ if (memcmp(route->key.vendor_lv+1, key->vendor_lv+1,
+ OSMO_MIN(sizeof(route->key.vendor_lv)-1,
+ route->key.vendor_lv[0])))
+ return 0;
+ }
+ }
+
+ if (route->flags & OML_RTF_BTS_NR)
+ if (route->key.obj_inst.bts_nr != key->obj_inst.bts_nr)
+ return 0;
+
+ if (route->flags & OML_RTF_TRX_NR)
+ if (route->key.obj_inst.trx_nr != key->obj_inst.trx_nr)
+ return 0;
+
+ if (route->flags & OML_RTF_TS_NR)
+ if (route->key.obj_inst.ts_nr != key->obj_inst.ts_nr)
+ return 0;
+
+ return 1;
+}
+
+/* are two given routes identical? */
+static int oml_route_ident(const struct oml_route *a, const struct oml_route *b)
+{
+ if (a->flags != b->flags)
+ return 0;
+
+ return oml_route_match(&a->key, b);
+}
+
+/* add a route from a router instance */
+int oml_route_add(struct oml_routing_inst *inst, const struct oml_route *route,
+ struct oml_client *client)
+{
+ struct oml_route_entry *e;
+
+ llist_for_each_entry(e, &inst->routes, list) {
+ if (oml_route_ident(route, &e->route))
+ return -EEXIST;
+ }
+
+ e = talloc_zero(inst, struct oml_route_entry);
+ memcpy(&e->route, route, sizeof(e->route));
+ e->client = client;
+ /* FIXME: insert in order of integer of routing_flags */
+ llist_add(&e->list, &inst->routes);
+
+ return 0;
+}
+
+/* delete a route from a router instance */
+int oml_route_del(struct oml_routing_inst *inst, const struct oml_route *route)
+{
+ struct oml_route_entry *e;
+
+ /* no safe iteration needed as we stop at first match */
+ llist_for_each_entry(e, &inst->routes, list) {
+ if (oml_route_ident(route, &e->route)) {
+ llist_del(&e->list);
+ talloc_free(e);
+ return 1;
+ }
+ }
+
+ return -ENODEV;
+}
+
+int oml_route_del_client(struct oml_routing_inst *inst,
+ const struct oml_client *client)
+{
+ struct oml_route_entry *e, *f;
+ int num = 0;
+
+ /* no safe iteration needed as we stop at first match */
+ llist_for_each_entry_safe(e, f, &inst->routes, list) {
+ if (e->client == client) {
+ llist_del(&e->list);
+ talloc_free(e);
+ num++;
+ }
+ }
+ return num;
+}
+
+
+/* perform routing of 'key' against router instance */
+void *oml_route(struct oml_routing_inst *inst,
+ const struct oml_routing_key *key)
+{
+ struct oml_route_entry *e;
+
+ llist_for_each_entry(e, &inst->routes, list) {
+ if (oml_route_match(key, &e->route))
+ return e->client;
+ }
+
+ return NULL;
+}
+
+DEFUN(show_oml_routing, show_oml_routing_cmd,
+ "show oml-routes",
+ SHOW_STR "Show the currently configured OML routing")
+{
+ struct oml_route_entry *e;
+
+ llist_for_each_entry(e, &g_inst->routes, list) {
+ struct oml_route *rt = &e->route;
+
+ vty_out(vty, " %s OC=%s INST=(%02x/%02x/%02x) -> %s/%s%s",
+ rt->flags & OML_RTF_MDISC ?
+ get_value_string(abis_nm_msg_disc_names,
+ rt->key.mdisc) : "ANY",
+ rt->flags & OML_RTF_OBJ_CLASS ?
+ get_value_string(abis_nm_obj_class_names,
+ rt->key.obj_class) : "ANY",
+ rt->flags & OML_RTF_BTS_NR ? rt->key.obj_inst.bts_nr : 255,
+ rt->flags & OML_RTF_TRX_NR ? rt->key.obj_inst.trx_nr : 255,
+ rt->flags & OML_RTF_TS_NR ? rt->key.obj_inst.ts_nr : 255,
+ oml_route_client_addr(e->client),
+ oml_route_client_name(e->client), VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
+#define MDISC_STR "(any|fom|mmi|trau|manuf|<0-255>)"
+#define CLASS_STR "(any|site-manager|bts|radio-carrier|baseband-transceiver|channel|gprs-nse|gprs-cell|gprs-nsvc|<0-255>)"
+
+#define OMLROUTE_CMD "oml-route (add|del) mdisc "MDISC_STR" class "CLASS_STR" bts (any|<0-255>) trx (any|<0-255>]) ts (any|<0-255>) DEST"
+
+#define OMLROUTE_HELP \
+ "OML-Routing\n" \
+ "Add a route\n" \
+ "Delete a route\n" \
+ "Message Discriminator\n" \
+ "Message Discriminator: Match any message discriminator\n" \
+ "Message Discriminator: Formatted OML messages\n" \
+ "Message Discriminator: Man-Machine-Interface OML messages\n" \
+ "Message Discriminator: Transcoder OML Message\n" \
+ "Message Discriminator: Manufacturer-specific OML messages\n" \
+ "Message Discriminator: Specific numeric message discrimniator\n" \
+ "Object Class\n" \
+ "Object Class: Match any object class\n" \
+ "Object Class: Site Manager\n" \
+ "Object Class: Base Transceiver Station\n" \
+ "Object Class: Radio Carrier\n" \
+ "Object Class: Baseband Transceiver\n" \
+ "Object Class: Channel (Um Timeslot)\n" \
+ "Object Class: GPRS NS Entity\n" \
+ "Object Class: GPRS Cell\n" \
+ "Object Class: GPRS NS Virtual Circuit\n" \
+ "Object Class: Specific numeric object class\n" \
+ "Object Instance(BTS)\n" \
+ "Object Instance(BTS): Any BTS number\n" \
+ "Object Instance(BTS): Specific BTS number\n" \
+ "Object Instance(TRX)\n" \
+ "Object Instance(TRX): Any TRX number\n" \
+ "Object Instance(TRX): Specific TRX number\n" \
+ "Object Instance(TS)\n" \
+ "Object Instance(TS): Any TS number\n" \
+ "Object Instance(TS): Specific TS number\n" \
+ "Name of destination for this route\n"
+
+DEFUN(cfg_omlr_route, cfg_omlr_route_cmd,
+ OMLROUTE_CMD, OMLROUTE_HELP)
+{
+ struct oml_route _r, *r = &_r;
+ int rc;
+
+ memset(r, 0, sizeof(*r));
+
+ if (strcmp(argv[1], "any")) {
+ r->flags |= OML_RTF_MDISC;
+ rc = get_string_value(abis_nm_msg_disc_names, argv[1]);
+ if (rc < 0)
+ rc = atoi(argv[1]);
+ r->key.mdisc = rc;
+ }
+
+ if (strcmp(argv[2], "any")) {
+ r->flags |= OML_RTF_OBJ_CLASS;
+ rc = get_string_value(abis_nm_obj_class_names, argv[2]);
+ if (rc < 0)
+ rc = atoi(argv[2]);
+ r->key.obj_class = rc;
+ }
+
+ if (strcmp(argv[3], "any")) {
+ r->flags |= OML_RTF_BTS_NR;
+ r->key.obj_inst.bts_nr = atoi(argv[3]);
+ };
+
+ if (strcmp(argv[4], "any")) {
+ r->flags |= OML_RTF_TRX_NR;
+ r->key.obj_inst.trx_nr = atoi(argv[4]);
+ };
+
+ if (strcmp(argv[5], "any")) {
+ r->flags |= OML_RTF_TS_NR;
+ r->key.obj_inst.ts_nr = atoi(argv[5]);
+ };
+
+ if (!strcmp(argv[0], "add"))
+ rc = oml_route_add(g_inst, r, NULL);
+ else
+ rc = oml_route_del(g_inst, r);
+
+ if (rc < 0) {
+ vty_out(vty, "Error %sing route: %d%s",
+ !strcmp(argv[0], "add") ? "add" : "delet",
+ rc, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+struct oml_routing_inst *oml_route_init(void *ctx, void *priv)
+{
+ g_inst = talloc_zero(ctx, struct oml_routing_inst);
+
+ INIT_LLIST_HEAD(&g_inst->routes);
+
+ g_inst->priv = priv;
+
+ /* FIXME: add separate node, as this is the only way to make settings
+ * persistent via the node-specific save callback */
+ install_element_ve(&show_oml_routing_cmd);
+ install_element(ENABLE_NODE, &cfg_omlr_route_cmd);
+
+ return g_inst;
+}