aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;
+}