diff options
Diffstat (limited to 'openbsc/src/libmsc')
-rw-r--r-- | openbsc/src/libmsc/Makefile.am | 2 | ||||
-rw-r--r-- | openbsc/src/libmsc/smpp_openbsc.c | 12 | ||||
-rw-r--r-- | openbsc/src/libmsc/smpp_smsc.c | 145 | ||||
-rw-r--r-- | openbsc/src/libmsc/smpp_smsc.h | 29 | ||||
-rw-r--r-- | openbsc/src/libmsc/smpp_vty.c | 308 |
5 files changed, 485 insertions, 11 deletions
diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am index 89fee9189..64afedb62 100644 --- a/openbsc/src/libmsc/Makefile.am +++ b/openbsc/src/libmsc/Makefile.am @@ -19,5 +19,5 @@ libmsc_a_SOURCES = auth.c \ osmo_msc.c if BUILD_SMPP -libmsc_a_SOURCES += smpp_smsc.c smpp_openbsc.c +libmsc_a_SOURCES += smpp_smsc.c smpp_openbsc.c smpp_vty.c endif diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c index 4a54e4780..d8dde2993 100644 --- a/openbsc/src/libmsc/smpp_openbsc.c +++ b/openbsc/src/libmsc/smpp_openbsc.c @@ -288,6 +288,14 @@ static int smpp_subscr_cb(unsigned int subsys, unsigned int signal, return 0; } +static struct smsc *g_smsc; + +struct smsc *smsc_from_vty(struct vty *v) +{ + /* FIXME: this is ugly */ + return g_smsc; +} + /*! \brief Initialize the OpenBSC SMPP interface */ int smpp_openbsc_init(struct gsm_network *net, uint16_t port) { @@ -303,5 +311,9 @@ int smpp_openbsc_init(struct gsm_network *net, uint16_t port) osmo_signal_register_handler(SS_SMS, smpp_sms_cb, net); osmo_signal_register_handler(SS_SUBSCR, smpp_subscr_cb, smsc); + g_smsc = smsc; + + smpp_vty_init(); + return rc; } diff --git a/openbsc/src/libmsc/smpp_smsc.c b/openbsc/src/libmsc/smpp_smsc.c index 8957c8ee5..493d07973 100644 --- a/openbsc/src/libmsc/smpp_smsc.c +++ b/openbsc/src/libmsc/smpp_smsc.c @@ -51,6 +51,58 @@ enum emse_bind { ESME_BIND_TX = 0x02, }; +struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, + const char *sys_id) +{ + struct osmo_smpp_acl *acl; + + llist_for_each_entry(acl, &smsc->acl_list, list) { + if (!strcmp(acl->system_id, sys_id)) + return acl; + } + + return NULL; +} + +struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id) +{ + struct osmo_smpp_acl *acl; + + if (strlen(sys_id) > SMPP_SYS_ID_LEN) + return NULL; + + if (smpp_acl_by_system_id(smsc, sys_id)) + return NULL; + + acl = talloc_zero(smsc, struct osmo_smpp_acl); + if (!acl) + return NULL; + + acl->smsc = smsc; + strcpy(acl->system_id, sys_id); + + llist_add(&acl->list, &smsc->acl_list); + + return acl; +} + +void smpp_acl_delete(struct osmo_smpp_acl *acl) +{ + struct osmo_esme *esme, *e2; + struct smsc *smsc = acl->smsc; + + llist_del(&acl->list); + + llist_for_each_entry_safe(esme, e2, &smsc->esme_list, list) { + if (!strcmp(acl->system_id, esme->system_id)) { + /* FIXME: drop connection */ + } + } + + talloc_free(acl); +} + + /*! \brief increaes the use/reference count */ void smpp_esme_get(struct osmo_esme *esme) { @@ -176,6 +228,7 @@ static int smpp_handle_bind_rx(struct osmo_esme *esme, struct msgb *msg) { struct bind_receiver_t bind; struct bind_receiver_resp_t bind_r; + struct osmo_smpp_acl *acl; int rc; SMPP34_UNPACK(rc, BIND_RECEIVER, &bind, msgb_data(msg), @@ -204,9 +257,26 @@ static int smpp_handle_bind_rx(struct osmo_esme *esme, struct msgb *msg) esme->smpp_version = bind.interface_version; snprintf(esme->system_id, sizeof(esme->system_id), "%s", bind.system_id); - esme->bind_flags = ESME_BIND_RX; - /* FIXME */ + acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); + if (!esme->smsc->accept_all) { + if (!acl) { + /* This system is unknown */ + bind_r.command_status = ESME_RINVSYSID; + goto err; + } else { + if (strlen(acl->passwd) && + strcmp(acl->passwd, (char *)bind.password)) { + bind_r.command_status = ESME_RINVPASWD; + goto err; + } + esme->acl = acl; + if (acl->default_route) + esme->smsc->def_route = esme; + } + } + + esme->bind_flags = ESME_BIND_RX; err: return 0; } @@ -216,6 +286,7 @@ static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) { struct bind_transmitter_t bind; struct bind_transmitter_resp_t bind_r; + struct osmo_smpp_acl *acl; struct tlv_t tlv; int rc; @@ -244,6 +315,23 @@ static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) esme->smpp_version = bind.interface_version; snprintf(esme->system_id, sizeof(esme->system_id), "%s", bind.system_id); + + acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); + if (!esme->smsc->accept_all) { + if (!acl) { + /* This system is unknown */ + bind_r.command_status = ESME_RINVSYSID; + goto err; + } else { + if (strlen(acl->passwd) && + strcmp(acl->passwd, (char *)bind.password)) { + bind_r.command_status = ESME_RINVPASWD; + goto err; + } + esme->acl = acl; + } + } + esme->bind_flags = ESME_BIND_TX; /* build response */ @@ -265,6 +353,7 @@ static int smpp_handle_bind_trx(struct osmo_esme *esme, struct msgb *msg) { struct bind_transceiver_t bind; struct bind_transceiver_resp_t bind_r; + struct osmo_smpp_acl *acl; int rc; SMPP34_UNPACK(rc, BIND_TRANSCEIVER, &bind, msgb_data(msg), @@ -292,9 +381,27 @@ static int smpp_handle_bind_trx(struct osmo_esme *esme, struct msgb *msg) esme->smpp_version = bind.interface_version; snprintf(esme->system_id, sizeof(esme->system_id), "%s", bind.system_id); + + acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); + if (!esme->smsc->accept_all) { + if (!acl) { + /* This system is unknown */ + bind_r.command_status = ESME_RINVSYSID; + goto err; + } else { + if (strlen(acl->passwd) && + strcmp(acl->passwd, (char *)bind.password)) { + bind_r.command_status = ESME_RINVPASWD; + goto err; + } + esme->acl = acl; + if (acl->default_route) + esme->smsc->def_route = esme; + } + } + esme->bind_flags |= ESME_BIND_TX | ESME_BIND_RX; - /* FIXME */ err: return 0; } @@ -324,6 +431,8 @@ static int smpp_handle_unbind(struct osmo_esme *esme, struct msgb *msg) } esme->bind_flags = 0; + if (esme->smsc->def_route == esme) + esme->smsc->def_route = NULL; err: return PACK_AND_SEND(esme, &unbind_r); } @@ -628,11 +737,33 @@ int smpp_smsc_init(struct smsc *smsc, uint16_t port) { int rc; - INIT_LLIST_HEAD(&smsc->esme_list); - smsc->listen_ofd.data = smsc; - smsc->listen_ofd.cb = smsc_fd_cb; + /* default port for SMPP */ + if (port == 0) + port = 2775; + + /* This will not work if we were to actually ever use FD 0 + * (stdin) for this ... */ + if (smsc->listen_ofd.fd <= 0) { + INIT_LLIST_HEAD(&smsc->esme_list); + INIT_LLIST_HEAD(&smsc->acl_list); + smsc->listen_ofd.data = smsc; + smsc->listen_ofd.cb = smsc_fd_cb; + } else { + close(smsc->listen_ofd.fd); + osmo_fd_unregister(&smsc->listen_ofd); + } + rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, SOCK_STREAM, - IPPROTO_TCP, NULL, port, OSMO_SOCK_F_BIND); + IPPROTO_TCP, NULL, port, + OSMO_SOCK_F_BIND); + + /* if there is an error, try to re-bind to the old port */ + if (rc < 0) { + rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, + SOCK_STREAM, IPPROTO_TCP, NULL, + smsc->listen_port, OSMO_SOCK_F_BIND); + } else + smsc->listen_port = port; return rc; } diff --git a/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h index b1617e600..4b5bd85be 100644 --- a/openbsc/src/libmsc/smpp_smsc.h +++ b/openbsc/src/libmsc/smpp_smsc.h @@ -12,15 +12,20 @@ #include <smpp34_structs.h> #include <smpp34_params.h> +#define SMPP_SYS_ID_LEN 16 +#define SMPP_PASSWD_LEN 16 + enum esme_read_state { READ_ST_IN_LEN = 0, READ_ST_IN_MSG = 1, }; +struct osmo_smpp_acl; + struct osmo_esme { struct llist_head list; struct smsc *smsc; - + struct osmo_smpp_acl *acl; int use; uint32_t own_seq_nr; @@ -35,15 +40,28 @@ struct osmo_esme { struct msgb *read_msg; uint8_t smpp_version; - char system_id[16+1]; + char system_id[SMPP_SYS_ID_LEN+1]; uint8_t bind_flags; }; +struct osmo_smpp_acl { + struct llist_head list; + struct smsc *smsc; + char *description; + char system_id[SMPP_SYS_ID_LEN+1]; + char passwd[SMPP_PASSWD_LEN+1]; + int default_route; +}; + struct smsc { struct osmo_fd listen_ofd; struct llist_head esme_list; - char system_id[16+1]; + struct llist_head acl_list; + uint16_t listen_port; + char system_id[SMPP_SYS_ID_LEN+1]; + int accept_all; + struct osmo_esme *def_route; void *priv; }; @@ -53,6 +71,11 @@ int smpp_smsc_init(struct smsc *smsc, uint16_t port); void smpp_esme_get(struct osmo_esme *esme); void smpp_esme_put(struct osmo_esme *esme); +struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id); +struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, + const char *sys_id); +void smpp_acl_delete(struct osmo_smpp_acl *acl); + int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, uint32_t command_status, char *msg_id); diff --git a/openbsc/src/libmsc/smpp_vty.c b/openbsc/src/libmsc/smpp_vty.c new file mode 100644 index 000000000..ad496da79 --- /dev/null +++ b/openbsc/src/libmsc/smpp_vty.c @@ -0,0 +1,308 @@ +/* SMPP vty interface */ + +/* (C) 2012 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 Affero 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 <string.h> +#include <netdb.h> +#include <sys/socket.h> + +#include <osmocom/vty/command.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/vty.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> + +#include <openbsc/vty.h> + +#include "smpp_smsc.h" + +struct smsc *smsc_from_vty(struct vty *v); + +static struct cmd_node smpp_node = { + SMPP_NODE, + "%s(config-smpp)# ", + 1, +}; + +static struct cmd_node esme_node = { + SMPP_ESME_NODE, + "%s(config-smpp-esme)# ", + 1, +}; + +DEFUN(cfg_smpp, cfg_smpp_cmd, + "smpp", "Configure SMPP SMS Interface") +{ + vty->node = SMPP_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_smpp_port, cfg_smpp_port_cmd, + "local-tcp-port <1-65535>", + "Set the local TCP port on which we listen for SMPP\n" + "TCP port number") +{ + struct smsc *smsc = smsc_from_vty(vty); + uint16_t port = atoi(argv[0]); + int rc; + + rc = smpp_smsc_init(smsc, port); + if (rc < 0) { + vty_out(vty, "%% Cannot bind to new port %u nor to " + "old port %u%s", port, smsc->listen_port, VTY_NEWLINE); + return CMD_WARNING; + } + + if (port != smsc->listen_port) { + vty_out(vty, "%% Cannot bind to new port %u, staying on old" + "port %u%s", port, smsc->listen_port, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_smpp_sys_id, cfg_smpp_sys_id_cmd, + "system-id ID", "Set the System ID of this SMSC") +{ + struct smsc *smsc = smsc_from_vty(vty); + + if (strlen(argv[0])+1 > sizeof(smsc->system_id)) + return CMD_WARNING; + + strcpy(smsc->system_id, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_smpp_policy, cfg_smpp_policy_cmd, + "policy (accept-all|closed)", + "Set the authentication policy of this SMSC\n" + "Accept all SMPP connections independeint of system ID / passwd\n" + "Accept only SMPP connections from ESMEs explicitly configured") +{ + struct smsc *smsc = smsc_from_vty(vty); + + if (!strcmp(argv[0], "accept-all")) + smsc->accept_all = 1; + else + smsc->accept_all = 0; + + return CMD_SUCCESS; +} + + +static int config_write_smpp(struct vty *vty) +{ + struct smsc *smsc = smsc_from_vty(vty); + + vty_out(vty, "smpp%s", VTY_NEWLINE); + vty_out(vty, " local-tcp-port %u%s", smsc->listen_port, VTY_NEWLINE); + vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE); + vty_out(vty, " policy %s%s", + smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_esme, cfg_esme_cmd, + "esme NAME", "Configure a particular ESME") +{ + struct smsc *smsc = smsc_from_vty(vty); + struct osmo_smpp_acl *acl; + const char *id = argv[0]; + + if (strlen(id) > 16) { + vty_out(vty, "%% System ID cannot be more than 16 " + "characters long%s", VTY_NEWLINE); + return CMD_WARNING; + } + acl = smpp_acl_by_system_id(smsc, id); + if (!acl) { + acl = smpp_acl_alloc(smsc, id); + if (!acl) + return CMD_WARNING; + } + + vty->index = acl; + vty->index_sub = &acl->description; + vty->node = SMPP_ESME_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_esme, cfg_no_esme_cmd, + "no esme NAME", NO_STR "Remove ESME configuration") +{ + struct smsc *smsc = smsc_from_vty(vty); + struct osmo_smpp_acl *acl; + const char *id = argv[0]; + + acl = smpp_acl_by_system_id(smsc, id); + if (!acl) { + vty_out(vty, "%% ESME with system id '%s' unknown%s", + id, VTY_NEWLINE); + return CMD_WARNING; + } + + /* FIXME: close the connection, free data structure, etc. */ + + smpp_acl_delete(acl); + + return CMD_SUCCESS; +} + + +DEFUN(cfg_esme_passwd, cfg_esme_passwd_cmd, + "password PASSWORD", "Set the password for this ESME") +{ + struct osmo_smpp_acl *acl = vty->index; + + if (strlen(argv[0])+1 > sizeof(acl->passwd)) + return CMD_WARNING; + + strcpy(acl->passwd, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_esme_no_passwd, cfg_esme_no_passwd_cmd, + "no password", NO_STR "Set the password for this ESME") +{ + struct osmo_smpp_acl *acl = vty->index; + + memset(acl->passwd, 0, sizeof(acl->passwd)); + + return CMD_SUCCESS; +} + +DEFUN(cfg_esme_route, cfg_esme_route_cmd, + "route DESTINATION", + "Configure a route for MO-SMS to be sent to this ESME\n" + "Destination phone number") +{ + struct osmo_smpp_acl *acl = vty->index; + + /* FIXME: check if DESTINATION is all-digits */ + + return CMD_SUCCESS; +} + +DEFUN(cfg_esme_defaultroute, cfg_esme_defaultroute_cmd, + "default-route", + "Set this ESME as default-route for all SMS to unknown destinations") +{ + struct osmo_smpp_acl *acl = vty->index; + + acl->default_route = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_esme_defaultroute, cfg_esme_no_defaultroute_cmd, + "no default-route", NO_STR + "Set this ESME as default-route for all SMS to unknown destinations") +{ + struct osmo_smpp_acl *acl = vty->index; + + acl->default_route = 0; + + /* remove currently active default route, if it was created by + * this ACL */ + if (acl->smsc->def_route && acl->smsc->def_route->acl == acl) + acl->smsc->def_route = NULL; + + return CMD_SUCCESS; +} + +static void dump_one_esme(struct vty *vty, struct osmo_esme *esme) +{ + char host[128], serv[128]; + + host[0] = 0; + serv[0] = 0; + getnameinfo((const struct sockaddr *) &esme->sa, esme->sa_len, + host, sizeof(host), serv, sizeof(serv), NI_NUMERICSERV); + + vty_out(vty, "ESME System ID: %s, Password: %s, SMPP Version %02x%s", + esme->system_id, esme->acl->passwd, esme->smpp_version, VTY_NEWLINE); + vty_out(vty, " Connected from: %s:%s%s", host, serv, VTY_NEWLINE); +} + +DEFUN(show_esme, show_esme_cmd, + "show smpp esme", + SHOW_STR "SMPP Interface\n" "SMPP Extrenal SMS Entity\n") +{ + struct smsc *smsc = smsc_from_vty(vty); + struct osmo_esme *esme; + + llist_for_each_entry(esme, &smsc->esme_list, list) + dump_one_esme(vty, esme); + + return CMD_SUCCESS; +} + +static void config_write_esme_single(struct vty *vty, struct osmo_smpp_acl *acl) +{ + vty_out(vty, " esme %s%s", acl->system_id, VTY_NEWLINE); + if (strlen(acl->passwd)) + vty_out(vty, " password %s%s", acl->passwd, VTY_NEWLINE); + if (acl->default_route) + vty_out(vty, " default-route%s", VTY_NEWLINE); +} + +static int config_write_esme(struct vty *v) +{ + struct smsc *smsc = smsc_from_vty(v); + struct osmo_smpp_acl *acl; + + llist_for_each_entry(acl, &smsc->acl_list, list) + config_write_esme_single(v, acl); + + return CMD_SUCCESS; +} + +int smpp_vty_init(void) +{ + install_node(&smpp_node, config_write_smpp); + install_default(SMPP_NODE); + install_element(CONFIG_NODE, &cfg_smpp_cmd); + + install_element(SMPP_NODE, &cfg_smpp_port_cmd); + install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd); + install_element(SMPP_NODE, &cfg_smpp_policy_cmd); + install_element(SMPP_NODE, &cfg_esme_cmd); + install_element(SMPP_NODE, &cfg_no_esme_cmd); + + install_node(&esme_node, config_write_esme); + install_default(SMPP_ESME_NODE); + install_element(SMPP_ESME_NODE, &cfg_esme_passwd_cmd); + install_element(SMPP_ESME_NODE, &cfg_esme_no_passwd_cmd); + install_element(SMPP_ESME_NODE, &cfg_esme_route_cmd); + install_element(SMPP_ESME_NODE, &cfg_esme_defaultroute_cmd); + install_element(SMPP_ESME_NODE, &cfg_esme_no_defaultroute_cmd); + install_element(SMPP_ESME_NODE, &ournode_exit_cmd); + + install_element_ve(&show_esme_cmd); + + return 0; +} |