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.
This commit is contained in:
Holger Freyther 2009-01-02 22:04:43 +00:00
parent c6880a46ad
commit 219518d064
7 changed files with 424 additions and 2 deletions

View File

@ -7,6 +7,7 @@ dnl checks for programs
AC_PROG_MAKE_SET
AC_PROG_CC
AC_PROG_INSTALL
AM_PROG_LEX
dnl checks for libraries

View File

@ -1,3 +1,3 @@
noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
gsm_subscriber.h linuxlist.h msgb.h select.h tlv.h gsm_04_11.h \
timer.h misdn.h chan_alloc.h
timer.h misdn.h chan_alloc.h telnet_interface.h

View File

@ -0,0 +1,45 @@
/* 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.
*
*/
#ifndef TELNET_INTERFACE_H
#define TELNET_INTERFACE_H
#include "gsm_data.h"
#include "linuxlist.h"
#include "select.h"
#define TELNET_COMMAND_48 1
#define TELNET_COMMAND_11 2
struct telnet_connection {
struct llist_head entry;
struct gsm_network *network;
struct bsc_fd fd;
int command;
char *imsi;
char commands[1024];
int read;
};
void telnet_init(struct gsm_network *network, int port);
#endif

View File

@ -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

View File

@ -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;

193
src/telnet_interface.c Normal file
View File

@ -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;
}

180
src/telnet_parser.l Normal file
View File

@ -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;
}
}