aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHolger Freyther <zecke@selfish.org>2009-01-02 22:04:43 +0000
committerHolger Freyther <zecke@selfish.org>2009-01-02 22:04:43 +0000
commit219518d06492d867909d91f1512f50d3235e1cc8 (patch)
tree4858a9076f11fcef9b84636eef539b4ecbfa4cd5 /src
parentc6880a46adea65e5efd9dddb6dcd2cf5caa4966a (diff)
Add simplistic telnet control interface
This might turn into a complete wire protocol with special client software. For now it will be a simple client interface that you can use with telnet to do certain things. This is using flex to implement the parsing. Implementation and more commands will follow.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/bsc_hack.c3
-rw-r--r--src/telnet_interface.c193
-rw-r--r--src/telnet_parser.l180
4 files changed, 377 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 62afff2e4..b0e29ee21 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -5,5 +5,5 @@ sbin_PROGRAMS = bsc_hack
bsc_hack_SOURCES = bsc_hack.c misdn.c abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \
gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \
- gsm_04_11.c
+ gsm_04_11.c telnet_interface.c telnet_parser.l
bsc_hack_LDADD = -ldl -ldbi
diff --git a/src/bsc_hack.c b/src/bsc_hack.c
index c59024a90..a3aefc664 100644
--- a/src/bsc_hack.c
+++ b/src/bsc_hack.c
@@ -1,6 +1,7 @@
/* A hackish minimal BSC (+MSC +HLR) implementation */
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -42,6 +43,7 @@
#include <openbsc/abis_nm.h>
#include <openbsc/debug.h>
#include <openbsc/misdn.h>
+#include <openbsc/telnet_interface.h>
/* global pointer to the gsm network data structure */
static struct gsm_network *gsmnet;
@@ -685,6 +687,7 @@ static int bootstrap_network(void)
gsmnet->call_state_changed = bsc_hack_call_state_changed;
#endif
+ telnet_init(gsmnet, 4242);
if (mi_setup(bts, 0, mi_cb) < 0)
return -EIO;
diff --git a/src/telnet_interface.c b/src/telnet_interface.c
new file mode 100644
index 000000000..b0a8e4334
--- /dev/null
+++ b/src/telnet_interface.c
@@ -0,0 +1,193 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openbsc/telnet_interface.h>
+
+extern void telnet_parse(struct telnet_connection *connection, char *line);
+
+/* per connection data */
+LLIST_HEAD(active_connections);
+
+/* per network data */
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what);
+static struct bsc_fd server_socket = {
+ .when = BSC_FD_READ,
+ .cb = telnet_new_connection,
+ .priv_nr = 0,
+};
+
+void telnet_init(struct gsm_network *network, int port) {
+ struct sockaddr_in sock_addr;
+ int fd;
+
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd < 0) {
+ perror("Telnet interface socket creation failed");
+ return;
+ }
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(port);
+ sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) {
+ perror("Telnet interface failed to bind");
+ return;
+ }
+
+ if (listen(fd, 0) < 0) {
+ perror("Telnet interface failed to listen");
+ return;
+ }
+
+ server_socket.data = network;
+ server_socket.fd = fd;
+ bsc_register_fd(&server_socket);
+}
+
+void telnet_write_help(int fd) {
+ int ret;
+ static char *msg =
+ "Help for the ad-hoc telnet command line interface\n"
+ "The generic pattern is CMD LEN DATA\\n or just CMD\n"
+ "where CMD is one of the following:\n"
+ "help\n"
+ "page IMSI (type)\n"
+ "call IMSI (number)\n"
+ "get_channel IMSI Add use count on an active channel\n"
+ "put_channel IMSI Remove use count on an active channel\n"
+ "48 IMSI 0xAB 0xEF...Send GSM 04.08\n"
+ "11 IMSI 0xAB 0xEF...Send GSM 04.11\n";
+
+ ret = write(fd, msg, strlen(msg));
+}
+
+static void print_welcome(int fd) {
+ int ret;
+ static char *msg =
+ "Welcome to the OpenBSC Control interface\n"
+ "Copyright (C) 2008, 2009 Harald Welte\n"
+ "Contributions by Daniel Willmann, Jan Lübbe, "
+ "Stefan Schmidt, Holger Freyther\n\n"
+ "License GPLv2+: GNU GPL version 2 or later "
+ "<http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change "
+ "and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted "
+ "by law.\nType \"help\" to get a short introduction.\n";
+
+ ret = write(fd, msg, strlen(msg));
+}
+
+int telnet_close_client(struct bsc_fd *fd) {
+ struct telnet_connection *conn = (struct telnet_connection*)fd->data;
+
+ close(fd->fd);
+ bsc_unregister_fd(fd);
+ llist_del(&conn->entry);
+ free(conn);
+ return 0;
+}
+
+void telnet_error_client(int fd) {
+ int ret;
+ static char *msg = "Something went wrong. Please try again.\n";
+
+ printf("Error\n");
+ ret = write(fd, msg, strlen(msg));
+}
+
+void telnet_page(struct telnet_connection *connection, const char *imsi, int page) {
+ printf("going to page: '%s' %d\n", imsi, page);
+}
+
+void telnet_put_channel(struct telnet_connection *connection, const char *imsi) {
+ printf("put_channel: '%s'\n", imsi);
+}
+
+void telnet_get_channel(struct telnet_connection *connection, const char *imsi) {
+ printf("get_channel: '%s'\n", imsi);
+}
+
+void telnet_call(struct telnet_connection *connection, const char* imsi,
+ const char *origin) {
+ printf("calling: '%s' from: '%s'\n", imsi, origin);
+}
+
+void telnet_send_gsm_48(struct telnet_connection *connection) {
+ printf("sending gsm04.08 message\n");
+}
+
+void telnet_send_gsm_11(struct telnet_connection *connection) {
+ printf("sending gsm04.11 message\n");
+}
+
+static int client_data(struct bsc_fd *fd, unsigned int what) {
+ char buf[4096];
+ int ret;
+
+ ret = read(fd->fd, buf, sizeof(buf)-1);
+ buf[ret] = '\0';
+
+ /* connection is gone */
+ if (ret <= 0)
+ return telnet_close_client(fd);
+
+ /* time to parse. This code assumes that the input is line based */
+ telnet_parse((struct telnet_connection*)fd->data, buf);
+
+ return 0;
+}
+
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
+ struct telnet_connection *connection;
+ struct sockaddr_in sockaddr;
+ socklen_t len = sizeof(sockaddr);
+ int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
+
+ if (new_connection < 0) {
+ perror("telnet accept failed");
+ return -1;
+ }
+
+
+ connection = (struct telnet_connection*)malloc(sizeof(*connection));
+ memset(connection, 0, sizeof(*connection));
+ connection->network = (struct gsm_network*)fd->data;
+ connection->fd.data = connection;
+ connection->fd.fd = new_connection;
+ connection->fd.when = BSC_FD_READ;
+ connection->fd.cb = client_data;
+ bsc_register_fd(&connection->fd);
+ llist_add_tail(&connection->entry, &active_connections);
+
+ print_welcome(new_connection);
+
+ return 0;
+}
diff --git a/src/telnet_parser.l b/src/telnet_parser.l
new file mode 100644
index 000000000..3af0d5818
--- /dev/null
+++ b/src/telnet_parser.l
@@ -0,0 +1,180 @@
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+/*
+ * I'm lazy and will not introduce lemon to this game. Our telnet
+ * interface is matching line based so we can have a pattern that
+ * is matching a line and everyone will be happy.
+ */
+
+%option never-interactive
+%option noyywrap
+%option reentrant
+
+%{
+#include <string.h>
+#include <openbsc/telnet_interface.h>
+
+extern char *strndup(const char *s, size_t n);
+extern void telnet_write_help(int);
+extern void telnet_close_client(struct bsc_fd*);
+extern void telnet_error_client(int fd);
+extern void telnet_page(struct telnet_connection *con, const char *imsi, int page);
+extern void telnet_call(struct telnet_connection *con, const char *imsi,
+ const char* origin);
+extern void telnet_put_channel(struct telnet_connection*, const char *imsi);
+extern void telnet_get_channel(struct telnet_connection*, const char *imsi);
+extern void telnet_send_gsm_48(struct telnet_connection*);
+extern void telnet_send_gsm_11(struct telnet_connection*);
+
+static const int PAGE_LEN = 5; /* "page " */
+static const int CALL_LEN = 5; /* "call " */
+static const int PUT_LEN = 12; /* "put_channel " */
+static const int GET_LEN = 12; /* "get_channel " */
+static const int NET_LEN = 3; /* "48 " "11 " */
+
+#define YY_EXTRA_TYPE struct telnet_connection*
+
+/* the string is null terminated */
+static int parse_hex(char *hex)
+{
+ int byte;
+ sscanf(hex, "%x", &byte);
+ return byte;
+}
+
+#define PREPARE_STRING(len) \
+ yytext[yyleng-1] = '\0'; \
+ char *str = yytext + len; \
+ char *pag = strstr(str, "\r"); \
+ if (pag) pag[0] = '\0'; \
+ pag = strstr(str, "\n"); \
+ if (pag) pag[0] = '\0';
+
+%}
+
+CMD_HELP "help"
+CMD_EXIT "exit"
+CMD_CLOSE "close"
+CMD_PAGE "page"
+CMD_GET_CHANNEL "get_channel"
+CMD_PUT_CHANNEL "put_channel"
+CMD_CALL "call"
+CMD_48 "48"
+CMD_11 "11"
+
+LINE_END \n|\r\n
+HEX [0][x][0-9a-zA-Z][0-9a-zA-Z]
+
+%s READ_HEX_BYTES
+
+%%
+{CMD_HELP}{LINE_END} {telnet_write_help(yyextra->fd.fd); yyterminate();}
+{CMD_EXIT}{LINE_END} {telnet_close_client(&yyextra->fd); yyterminate();}
+{CMD_CLOSE}{LINE_END} {telnet_close_client(&yyextra->fd); yyterminate();}
+{CMD_PAGE}[ ][0-9]+{LINE_END} {
+ PREPARE_STRING(PAGE_LEN)
+ telnet_page(yyextra, str, 0);
+ yyterminate();
+ }
+{CMD_PAGE}[ ][0-9]+[ ][0-2]{LINE_END} {
+ PREPARE_STRING(PAGE_LEN)
+ char *sp = strstr(str, " ");
+ sp[0] = '\0';
+ telnet_page(yyextra, str, atoi(sp+1));
+ yyterminate();
+ }
+{CMD_PUT_CHANNEL}[ ][0-9]+{LINE_END} {
+ PREPARE_STRING(PUT_LEN)
+ telnet_put_channel(yyextra, str);
+ yyterminate();
+ }
+{CMD_GET_CHANNEL}[ ][0-9]+{LINE_END} {
+ PREPARE_STRING(GET_LEN)
+ telnet_get_channel(yyextra, str);
+ yyterminate();
+ }
+{CMD_CALL}[ ][0-9]+[ ][0-9]+{LINE_END} {
+ PREPARE_STRING(CALL_LEN)
+ char *sp = strstr(str, " ");
+ sp[0] = '\0';
+ telnet_call(yyextra, str, sp+1);
+ yyterminate();
+ }
+{CMD_CALL}[ ][0-9]+{LINE_END} {
+ PREPARE_STRING(CALL_LEN)
+ telnet_call(yyextra, str, NULL);
+ yyterminate();
+ }
+<READ_HEX_BYTES>{HEX} {
+ if (yyextra->read >= sizeof(yyextra->commands)) {
+ yyterminate();
+ }
+ yytext[yyleng] = '\0';
+ yyextra->commands[yyextra->read++] = parse_hex(yytext+2);
+ }
+<READ_HEX_BYTES>{LINE_END} {
+ if (yyextra->command == TELNET_COMMAND_11) {
+ telnet_send_gsm_11(yyextra);
+ } else if (yyextra->command == TELNET_COMMAND_48) {
+ telnet_send_gsm_48(yyextra);
+ }
+
+ if (yyextra->imsi) {
+ free(yyextra->imsi);
+ yyextra->imsi = NULL;
+ }
+ yyterminate();
+ }
+<INITIAL>{CMD_48}[ ][0-9]+ {
+ BEGIN READ_HEX_BYTES;
+ yyextra->read = 0;
+ yyextra->command = TELNET_COMMAND_48;
+ yytext[yyleng-1] = '\0';
+ yyextra->imsi = strdup(yytext);
+ }
+
+<INITIAL>{CMD_11}[ ][0-9]+ {
+ BEGIN READ_HEX_BYTES;
+ yyextra->read = 0;
+ yyextra->command = TELNET_COMMAND_11;
+ yytext[yyleng-1] = '\0';
+ yyextra->imsi = strdup(yytext);
+ }
+
+
+
+[ \t\r\n] /* Ommit */
+. { telnet_error_client(yyextra->fd.fd); yyterminate(); }
+
+%%
+
+void telnet_parse(struct telnet_connection *conn, char *buf)
+{
+ yyscan_t scanner;
+ yylex_init(&scanner);
+ yyset_extra(conn, scanner);
+ yy_scan_string(buf, scanner);
+ yylex(scanner);
+ yylex_destroy(scanner);
+
+ if (conn->imsi) {
+ free(conn->imsi);
+ conn->imsi = NULL;
+ }
+}