diff options
author | Holger Hans Peter Freyther <zecke@selfish.org> | 2013-03-19 22:29:41 +0100 |
---|---|---|
committer | Holger Hans Peter Freyther <zecke@selfish.org> | 2013-03-19 22:51:09 +0100 |
commit | a5a17fa80affe0523689c7c7970391f115dccd51 (patch) | |
tree | 57e07a0fcec48cb7583626ea5df6178c1097f3ce | |
parent | d93c84f96e7b7e652ac5a937c1f27df0913fca18 (diff) |
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.
-rw-r--r-- | include/msc_connection.h | 12 | ||||
-rw-r--r-- | src/msc_conn.c | 209 | ||||
-rw-r--r-- | src/vty_interface.c | 32 |
3 files changed, 248 insertions, 5 deletions
diff --git a/include/msc_connection.h b/include/msc_connection.h index 74319b3..826eb3a 100644 --- a/include/msc_connection.h +++ b/include/msc_connection.h @@ -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 diff --git a/src/msc_conn.c b/src/msc_conn.c index b52b901..e895e77 100644 --- a/src/msc_conn.c +++ b/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; +} diff --git a/src/vty_interface.c b/src/vty_interface.c index 001ba44..e9326a9 100644 --- a/src/vty_interface.c +++ b/src/vty_interface.c @@ -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); |