msc: Allow to listen for incoming connections.
This is mostly a hack to allow IPA/SCCP routing to SCTP/M2UA/MTP3/SCCP without going through the full stack. The proper way of doing this requires another round of abstraction for the mtp_link_set class.zecke/m3ua
parent
d93c84f96e
commit
a5a17fa80a
|
@ -30,14 +30,22 @@
|
|||
struct bsc_data;
|
||||
struct ss7_application;
|
||||
|
||||
enum msc_mode {
|
||||
MSC_MODE_CLIENT,
|
||||
MSC_MODE_SERVER,
|
||||
};
|
||||
|
||||
struct msc_connection {
|
||||
/* management */
|
||||
struct llist_head entry;
|
||||
int nr;
|
||||
char *name;
|
||||
enum msc_mode mode;
|
||||
int auth;
|
||||
|
||||
/* ip management */
|
||||
int dscp;
|
||||
int port;
|
||||
char *ip;
|
||||
char *token;
|
||||
|
||||
|
@ -62,6 +70,9 @@ struct msc_connection {
|
|||
|
||||
/* application pointer */
|
||||
struct ss7_application *app;
|
||||
|
||||
/* server functions */
|
||||
struct osmo_fd listen_fd;
|
||||
};
|
||||
|
||||
/* msc related functions */
|
||||
|
@ -80,5 +91,6 @@ void msc_mgcp_reset(struct msc_connection *msc);
|
|||
/* Called by the MSC Connection */
|
||||
void msc_dispatch_sccp(struct msc_connection *msc, struct msgb *msg);
|
||||
|
||||
const char *msc_mode(struct msc_connection *msc);
|
||||
|
||||
#endif
|
||||
|
|
209
src/msc_conn.c
209
src/msc_conn.c
|
@ -1,7 +1,7 @@
|
|||
/* MSC related stuff... */
|
||||
/*
|
||||
* (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010-2012 by On-Waves
|
||||
* (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010-2013 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -29,10 +29,11 @@
|
|||
#include <ss7_application.h>
|
||||
#include <mgcp_patch.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
|
@ -49,6 +50,8 @@
|
|||
static void msc_send_id_response(struct msc_connection *bsc);
|
||||
static void msc_send(struct msc_connection *bsc, struct msgb *msg, int proto);
|
||||
static void msc_schedule_reconnect(struct msc_connection *bsc);
|
||||
static int msc_conn_bind(struct msc_connection *bsc);
|
||||
static void msc_handle_id_response(struct msc_connection *bsc, struct msgb *msg);
|
||||
|
||||
void msc_close_connection(struct msc_connection *fw)
|
||||
{
|
||||
|
@ -157,8 +160,22 @@ static int ipaccess_a_fd_cb(struct osmo_fd *bfd)
|
|||
msc_send_id_response(fw);
|
||||
} else if (msg->l2h[0] == IPAC_MSGT_PONG) {
|
||||
osmo_timer_del(&fw->pong_timeout);
|
||||
} else if (msg->l2h[0] == IPAC_MSGT_ID_RESP) {
|
||||
msc_handle_id_response(fw, msg);
|
||||
}
|
||||
} else if (hh->proto == IPAC_PROTO_SCCP) {
|
||||
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fw->mode == MSC_MODE_SERVER && !fw->auth) {
|
||||
LOGP(DMSC, LOGL_ERROR,
|
||||
"Ignoring non ipa message for unauth user.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hh->proto == IPAC_PROTO_SCCP) {
|
||||
msc_dispatch_sccp(fw, msg);
|
||||
} else if (hh->proto == NAT_MUX) {
|
||||
msg = mgcp_patch(fw->app, msg);
|
||||
|
@ -315,7 +332,7 @@ static void msc_reconnect(void *_data)
|
|||
osmo_timer_del(&fw->reconnect_timer);
|
||||
fw->first_contact = 1;
|
||||
|
||||
rc = connect_to_msc(&fw->msc_connection.bfd, fw->ip, 5000, fw->dscp);
|
||||
rc = connect_to_msc(&fw->msc_connection.bfd, fw->ip, fw->port, fw->dscp);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Opening the MSC connection failed. Trying again\n");
|
||||
osmo_timer_schedule(&fw->reconnect_timer, RECONNECT_TIME);
|
||||
|
@ -329,6 +346,8 @@ static void msc_reconnect(void *_data)
|
|||
|
||||
static void msc_schedule_reconnect(struct msc_connection *fw)
|
||||
{
|
||||
if (fw->mode == MSC_MODE_SERVER)
|
||||
return;
|
||||
osmo_timer_schedule(&fw->reconnect_timer, RECONNECT_TIME);
|
||||
}
|
||||
|
||||
|
@ -399,6 +418,14 @@ void msc_send_reset(struct msc_connection *fw)
|
|||
return;
|
||||
}
|
||||
|
||||
/* start the ping/pong but nothing else */
|
||||
if (fw->mode == MSC_MODE_SERVER) {
|
||||
LOGP(DMSC, LOGL_DEBUG, "Not sending BSSMAP resets in server mode.\n");
|
||||
msc_ping_timeout(fw);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
msg = create_reset();
|
||||
if (!msg)
|
||||
return;
|
||||
|
@ -411,6 +438,12 @@ static void msc_send_id_response(struct msc_connection *fw)
|
|||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (fw->mode == MSC_MODE_SERVER) {
|
||||
LOGP(DMSC, LOGL_DEBUG,
|
||||
"Not sending our token in server mode.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "id resp");
|
||||
msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP);
|
||||
msgb_l16tv_put(msg, strlen(fw->token) + 1,
|
||||
|
@ -434,6 +467,9 @@ struct msc_connection *msc_connection_create(struct bsc_data *bsc, int mgcp)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
msc->mode = MSC_MODE_CLIENT;
|
||||
msc->port = 5000;
|
||||
|
||||
osmo_wqueue_init(&msc->msc_connection, 100);
|
||||
msc->reconnect_timer.cb = msc_reconnect;
|
||||
msc->reconnect_timer.data = msc;
|
||||
|
@ -482,6 +518,169 @@ int msc_connection_start(struct msc_connection *msc)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* bind and wait if we are a server */
|
||||
if (msc->mode == MSC_MODE_SERVER)
|
||||
return msc_conn_bind(msc);
|
||||
|
||||
msc_schedule_reconnect(msc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *msc_mode(struct msc_connection *msc)
|
||||
{
|
||||
switch (msc->mode) {
|
||||
case MSC_MODE_CLIENT:
|
||||
return "client";
|
||||
case MSC_MODE_SERVER:
|
||||
return "server";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
/* Non-clean MSC server socket abstraction.. bind and accept */
|
||||
static int msc_send_auth_req(struct msc_connection *msc)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
static const uint8_t id_req[] = {
|
||||
IPAC_MSGT_ID_GET,
|
||||
0x01, IPAC_IDTAG_UNIT,
|
||||
0x01, IPAC_IDTAG_MACADDR,
|
||||
0x01, IPAC_IDTAG_LOCATION1,
|
||||
0x01, IPAC_IDTAG_LOCATION2,
|
||||
0x01, IPAC_IDTAG_EQUIPVERS,
|
||||
0x01, IPAC_IDTAG_SWVERSION,
|
||||
0x01, IPAC_IDTAG_UNITNAME,
|
||||
0x01, IPAC_IDTAG_SERNR,
|
||||
};
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "auth");
|
||||
if (!msg) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to allocate auth.\n");
|
||||
msc_close_connection(msc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, ARRAY_SIZE(id_req));
|
||||
memcpy(msg->l2h, id_req, ARRAY_SIZE(id_req));
|
||||
|
||||
msc_send(msc, msg, IPAC_PROTO_IPACCESS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msc_handle_id_response(struct msc_connection *msc, struct msgb *msg)
|
||||
{
|
||||
unsigned int len;
|
||||
const char *token;
|
||||
|
||||
/* only for the server */
|
||||
if (msc->mode != MSC_MODE_SERVER) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Unexpected ID response for client.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!msc->token) {
|
||||
LOGP(DMSC, LOGL_ERROR, "No token defined. Giving up.\n");
|
||||
goto clean;
|
||||
}
|
||||
|
||||
if (msgb_l2len(msg) < 4) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Too short message...%u\n",
|
||||
msgb_l2len(msg));
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* in lack of ipaccess_idtag_parse we have a very basic method */
|
||||
if (msg->l2h[3] != IPAC_IDTAG_UNITNAME) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Expected unitname tag got %d\n",
|
||||
msg->l2h[3]);
|
||||
goto clean;
|
||||
}
|
||||
|
||||
token = (const char *) &msg->l2h[4];
|
||||
len = msgb_l2len(msg) - 4;
|
||||
|
||||
if (len != strlen(msc->token)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Wrong length %u vs. %u\n",
|
||||
len, strlen(msc->token));
|
||||
goto clean;
|
||||
}
|
||||
|
||||
if (memcmp(msc->token, token, len) != 0) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Token has the wrong size.\n");
|
||||
goto clean;
|
||||
}
|
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "Authenticated the connection.\n");
|
||||
msc->auth = 1;
|
||||
return;
|
||||
clean:
|
||||
msc_close_connection(msc);
|
||||
}
|
||||
|
||||
static int msc_conn_accept(struct osmo_fd *bsc_fd, unsigned int what)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
struct msc_connection *msc = bsc_fd->data;
|
||||
int ret;
|
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "Going to accept a connection.\n");
|
||||
|
||||
ret = accept(bsc_fd->fd, (struct sockaddr *) &addr, &len);
|
||||
if (ret < 0) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Accept failed with fd(%d) errno(%d)\n",
|
||||
ret, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the previous/current connection.
|
||||
* TODO: switch only once we know it is a valid connection
|
||||
*/
|
||||
msc_close_connection(msc);
|
||||
|
||||
/* re-set the internal state */
|
||||
msc->auth = 0;
|
||||
|
||||
/* adopt the connection */
|
||||
msc->msc_connection.bfd.fd = ret;
|
||||
msc->msc_connection.bfd.when = BSC_FD_READ;
|
||||
ret = osmo_fd_register(&msc->msc_connection.bfd);
|
||||
if (ret < 0) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to register fd.\n");
|
||||
close(msc->msc_connection.bfd.fd);
|
||||
msc->msc_connection.bfd.fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* consider it up and running */
|
||||
msc->msc_link_down = 0;
|
||||
|
||||
/* msc send auth request */
|
||||
msc_send_auth_req(msc);
|
||||
LOGP(DMSC, LOGL_ERROR, "Registered fd %d and waiting for data.\n",
|
||||
msc->msc_connection.bfd.fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msc_conn_bind(struct msc_connection *msc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "Going to bind and wait for connections.\n");
|
||||
|
||||
rc = osmo_sock_init_ofd(&msc->listen_fd, AF_UNSPEC, SOCK_STREAM,
|
||||
IPPROTO_TCP, "127.0.0.1", msc->port, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
LOGP(DMSC, LOGL_NOTICE, "Failed to bind the socket.\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
msc->listen_fd.data = msc;
|
||||
msc->listen_fd.cb = msc_conn_accept;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -241,7 +241,9 @@ static void write_msc(struct vty *vty, struct msc_connection *msc)
|
|||
|
||||
vty_out(vty, " msc %d%s", msc->nr, VTY_NEWLINE);
|
||||
vty_out(vty, " description %s%s", name, VTY_NEWLINE);
|
||||
vty_out(vty, " mode %s%s", msc_mode(msc), VTY_NEWLINE);
|
||||
vty_out(vty, " ip %s%s", msc->ip, VTY_NEWLINE);
|
||||
vty_out(vty, " port %d%s", msc->port, VTY_NEWLINE);
|
||||
vty_out(vty, " token %s%s", msc->token, VTY_NEWLINE);
|
||||
vty_out(vty, " dscp %d%s", msc->dscp, VTY_NEWLINE);
|
||||
vty_out(vty, " timeout ping %d%s", msc->ping_time, VTY_NEWLINE);
|
||||
|
@ -759,6 +761,25 @@ DEFUN(cfg_ss7_msc, cfg_ss7_msc_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_msc_mode, cfg_msc_mode_cmd,
|
||||
"mode (server|client)",
|
||||
"Change the mode of the A-link\n"
|
||||
"Accept incoming connection\n" "Open outgoing connection\n")
|
||||
{
|
||||
struct msc_connection *msc = vty->index;
|
||||
|
||||
switch (argv[0][0]) {
|
||||
case 's':
|
||||
msc->mode = MSC_MODE_SERVER;
|
||||
break;
|
||||
case 'c':
|
||||
msc->mode = MSC_MODE_CLIENT;
|
||||
break;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_msc_ip, cfg_msc_ip_cmd,
|
||||
"ip ADDR",
|
||||
"IP Address of the MSC\n" "Address\n")
|
||||
|
@ -781,6 +802,15 @@ DEFUN(cfg_msc_ip, cfg_msc_ip_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_msc_port, cfg_msc_port_cmd,
|
||||
"port <1-65535>",
|
||||
"Port for the TCP connection\n" "Port Number\n")
|
||||
{
|
||||
struct msc_connection *msc = vty->index;
|
||||
msc->port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_msc_token, cfg_msc_token_cmd,
|
||||
"token TOKEN",
|
||||
"Token for the MSC\n" "The token\n")
|
||||
|
@ -1086,7 +1116,9 @@ void cell_vty_init(void)
|
|||
install_element(SS7_NODE, &cfg_ss7_msc_cmd);
|
||||
install_node(&msc_node, config_write_msc);
|
||||
install_defaults(MSC_NODE);
|
||||
install_element(MSC_NODE, &cfg_msc_mode_cmd);
|
||||
install_element(MSC_NODE, &cfg_msc_ip_cmd);
|
||||
install_element(MSC_NODE, &cfg_msc_port_cmd);
|
||||
install_element(MSC_NODE, &cfg_msc_token_cmd);
|
||||
install_element(MSC_NODE, &cfg_msc_dscp_cmd);
|
||||
install_element(MSC_NODE, &cfg_msc_timeout_ping_cmd);
|
||||
|
|
Reference in New Issue