diff options
-rw-r--r-- | openbsc/include/openbsc/gsm_data.h | 8 | ||||
-rw-r--r-- | openbsc/src/libmsc/gsm_04_11.c | 26 | ||||
-rw-r--r-- | openbsc/src/libmsc/smpp_openbsc.c | 93 | ||||
-rw-r--r-- | openbsc/src/libmsc/smpp_smsc.c | 397 | ||||
-rw-r--r-- | openbsc/src/libmsc/smpp_smsc.h | 39 | ||||
-rw-r--r-- | openbsc/src/libmsc/smpp_vty.c | 146 |
6 files changed, 585 insertions, 124 deletions
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index c6a40e38a..3b0f248b2 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -282,10 +282,18 @@ enum gsm_sms_source_id { #define SMS_HDR_SIZE 128 #define SMS_TEXT_SIZE 256 + +struct gsm_sms_addr { + uint8_t ton; + uint8_t npi; + char addr[21+1]; +}; + struct gsm_sms { unsigned long long id; struct gsm_subscriber *sender; struct gsm_subscriber *receiver; + struct gsm_sms_addr destination; enum gsm_sms_source_id source; struct { diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c index 7ef92a8c6..5beae5301 100644 --- a/openbsc/src/libmsc/gsm_04_11.c +++ b/openbsc/src/libmsc/gsm_04_11.c @@ -56,6 +56,11 @@ #include <openbsc/chan_alloc.h> #include <openbsc/bsc_api.h> +#ifdef BUILD_SMPP +#include "smpp_smsc.h" +extern int smpp_try_deliver(struct gsm_sms *sms); +#endif + #define GSM411_ALLOC_SIZE 1024 #define GSM411_ALLOC_HEADROOM 128 @@ -377,13 +382,21 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m LOGP(DLSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n"); rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; goto out; + } else if (da_len_bytes < 4) { + LOGP(DLSMS, LOGL_ERROR, "Destination Address < 4 bytes ?!?\n"); + rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; + goto out; } memset(address_lv, 0, sizeof(address_lv)); memcpy(address_lv, smsp, da_len_bytes); /* mangle first byte to reflect length in bytes, not digits */ address_lv[0] = da_len_bytes - 1; + + gsms->destination.ton = (address_lv[1] >> 4) & 7; + gsms->destination.npi = address_lv[1] & 0xF; /* convert to real number */ - gsm48_decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1); + gsm48_decode_bcd_number(gsms->destination.addr, + sizeof(gsms->destination.addr), address_lv, 1); smsp += da_len_bytes; gsms->protocol_id = *smsp++; @@ -449,8 +462,19 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m /* determine gsms->receiver based on dialled number */ gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dest_addr); if (!gsms->receiver) { +#ifdef BUILD_SMPP + rc = smpp_try_deliver(gsms); + if (rc == 1) { + rc = 1; /* cause 1: unknown subscriber */ + osmo_counter_inc(conn->bts->network->stats.sms.no_receiver); + } else if (rc < 0) { + rc = 21; /* cause 21: short message transfer rejected */ + /* FIXME: handle the error somehow? */ + } +#else rc = 1; /* cause 1: unknown subscriber */ osmo_counter_inc(conn->bts->network->stats.sms.no_receiver); +#endif goto out; } diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c index d8dde2993..b3b8d3697 100644 --- a/openbsc/src/libmsc/smpp_openbsc.c +++ b/openbsc/src/libmsc/smpp_openbsc.c @@ -66,7 +66,7 @@ static struct gsm_subscriber *subscr_by_dst(struct gsm_network *net, } /*! \brief find a TLV with given tag in list of libsmpp34 TLVs */ -struct tlv_t *find_tlv(struct tlv_t *head, uint16_t tag) +static struct tlv_t *find_tlv(struct tlv_t *head, uint16_t tag) { struct tlv_t *t; @@ -217,7 +217,6 @@ int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, static int smpp_sms_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { - struct gsm_network *network = handler_data; struct sms_signal_data *sig_sms = signal_data; struct gsm_sms *sms = sig_sms->sms; int rc = 0; @@ -288,8 +287,98 @@ static int smpp_subscr_cb(unsigned int subsys, unsigned int signal, return 0; } +static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms) +{ + struct deliver_sm_t deliver; + uint8_t dcs; + + memset(&deliver, 0, sizeof(deliver)); + deliver.command_length = 0; + deliver.command_id = DELIVER_SM; + deliver.command_status = ESME_ROK; + + strcpy((char *)deliver.service_type, "CMT"); + if (esme->acl && esme->acl->deliver_src_imsi) { + deliver.source_addr_ton = TON_Subscriber_Number; + deliver.source_addr_npi = NPI_Land_Mobile_E212; + snprintf((char *)deliver.source_addr, + sizeof(deliver.source_addr), "%s", + sms->sender->imsi); + } else { + deliver.source_addr_ton = TON_Network_Specific; + deliver.source_addr_npi = NPI_ISDN_E163_E164; + snprintf((char *)deliver.source_addr, + sizeof(deliver.source_addr), "%s", + sms->sender->extension); + } + + deliver.dest_addr_ton = sms->destination.ton; + deliver.dest_addr_npi = sms->destination.npi; + memcpy(deliver.destination_addr, sms->destination.addr, + sizeof(deliver.destination_addr)); + + deliver.esm_class = 1; /* datagram mode */ + if (sms->ud_hdr_ind) + deliver.esm_class |= 0x40; + if (sms->reply_path_req) + deliver.esm_class |= 0x80; + + deliver.protocol_id = sms->protocol_id; + deliver.priority_flag = 0; + deliver.registered_delivery = 0; + + dcs = sms->data_coding_scheme; + if (dcs == GSM338_DCS_1111_7BIT || + ((dcs & 0xE0000000) == 0 && (dcs & 0xC) == 0)) { + uint8_t *src = sms->user_data; + uint8_t *dst = deliver.short_message; + uint8_t src_byte_len = sms->user_data_len; + + /* SMPP has this strange notion of putting 7bit SMS in + * an octet-aligned mode */ + deliver.data_coding = 0x01; + if (sms->ud_hdr_ind) { + uint8_t udh_len = sms->user_data[0]; + src += udh_len + 1; + dst += udh_len + 1; + src_byte_len -= udh_len + 1; + memcpy(dst, sms->user_data, udh_len + 1); + deliver.sm_length = udh_len + 1; + } + deliver.sm_length += gsm_7bit_decode((char *)dst, src, src_byte_len); + } else if (dcs == GSM338_DCS_1111_8BIT_DATA || + ((dcs & 0xE0000000) == 0 && (dcs & 0xC) == 4)) { + deliver.data_coding = 0x02; + deliver.sm_length = sms->user_data_len; + memcpy(deliver.short_message, sms->user_data, deliver.sm_length); + } else if ((dcs & 0xE0000000) == 0 && (dcs & 0xC) == 8) { + deliver.data_coding = 0x08; /* UCS-2 */ + deliver.sm_length = sms->user_data_len; + memcpy(deliver.short_message, sms->user_data, deliver.sm_length); + } + + return smpp_tx_deliver(esme, &deliver); +} + static struct smsc *g_smsc; +int smpp_try_deliver(struct gsm_sms *sms) +{ + struct osmo_esme *esme; + struct osmo_smpp_addr dst; + + memset(&dst, 0, sizeof(dst)); + dst.ton = sms->destination.ton; + dst.npi = sms->destination.npi; + memcpy(dst.addr, sms->destination.addr, sizeof(dst.addr)); + + esme = smpp_route(g_smsc, &dst); + if (!esme) + return 1; /* unknown subscriber */ + + return deliver_to_esme(esme, sms); +} + struct smsc *smsc_from_vty(struct vty *v) { /* FIXME: this is ugly */ diff --git a/openbsc/src/libmsc/smpp_smsc.c b/openbsc/src/libmsc/smpp_smsc.c index 493d07973..f9e517b2b 100644 --- a/openbsc/src/libmsc/smpp_smsc.c +++ b/openbsc/src/libmsc/smpp_smsc.c @@ -51,6 +51,71 @@ enum emse_bind { ESME_BIND_TX = 0x02, }; +const struct value_string smpp_status_strs[] = { + { ESME_ROK, "No Error" }, + { ESME_RINVMSGLEN, "Message Length is invalid" }, + { ESME_RINVCMDLEN, "Command Length is invalid" }, + { ESME_RINVCMDID, "Invalid Command ID" }, + { ESME_RINVBNDSTS, "Incorrect BIND Status for given command" }, + { ESME_RALYBND, "ESME Already in Bound State" }, + { ESME_RINVPRTFLG, "Invalid Priority Flag" }, + { ESME_RINVREGDLVFLG, "Invalid Registered Delivery Flag" }, + { ESME_RSYSERR, "System Error" }, + { ESME_RINVSRCADR, "Invalid Source Address" }, + { ESME_RINVDSTADR, "Invalid Destination Address" }, + { ESME_RINVMSGID, "Message ID is invalid" }, + { ESME_RBINDFAIL, "Bind failed" }, + { ESME_RINVPASWD, "Invalid Password" }, + { ESME_RINVSYSID, "Invalid System ID" }, + { ESME_RCANCELFAIL, "Cancel SM Failed" }, + { ESME_RREPLACEFAIL, "Replace SM Failed" }, + { ESME_RMSGQFUL, "Message Queue Full" }, + { ESME_RINVSERTYP, "Invalid Service Type" }, + { ESME_RINVNUMDESTS, "Invalid number of destinations" }, + { ESME_RINVDLNAME, "Invalid Distribution List name" }, + { ESME_RINVDESTFLAG, "Destination flag is invalid" }, + { ESME_RINVSUBREP, "Invalid submit with replace request" }, + { ESME_RINVESMCLASS, "Invalid esm_class field data" }, + { ESME_RCNTSUBDL, "Cannot Submit to Distribution List" }, + { ESME_RSUBMITFAIL, "submit_sm or submit_multi failed" }, + { ESME_RINVSRCTON, "Invalid Source address TON" }, + { ESME_RINVSRCNPI, "Invalid Sourec address NPI" }, + { ESME_RINVDSTTON, "Invalid Destination address TON" }, + { ESME_RINVDSTNPI, "Invalid Desetination address NPI" }, + { ESME_RINVSYSTYP, "Invalid system_type field" }, + { ESME_RINVREPFLAG, "Invalid replace_if_present field" }, + { ESME_RINVNUMMSGS, "Invalid number of messages" }, + { ESME_RTHROTTLED, "Throttling error (ESME has exceeded message limits)" }, + { ESME_RINVSCHED, "Invalid Scheduled Delivery Time" }, + { ESME_RINVEXPIRY, "Invalid message validity period (Expiry time)" }, + { ESME_RINVDFTMSGID, "Predefined Message Invalid or Not Found" }, + { ESME_RX_T_APPN, "ESME Receiver Temporary App Error Code" }, + { ESME_RX_P_APPN, "ESME Receiver Permanent App Error Code" }, + { ESME_RX_R_APPN, "ESME Receiver Reject Message Error Code" }, + { ESME_RQUERYFAIL, "query_sm request failed" }, + { ESME_RINVOPTPARSTREAM,"Error in the optional part of the PDU Body" }, + { ESME_ROPTPARNOTALLWD, "Optional Parameter not allowed" }, + { ESME_RINVPARLEN, "Invalid Parameter Length" }, + { ESME_RMISSINGOPTPARAM,"Expected Optional Parameter missing" }, + { ESME_RINVOPTPARAMVAL, "Invalid Optional Parameter Value" }, + { ESME_RDELIVERYFAILURE,"Delivery Failure (used for data_sm_resp)" }, + { ESME_RUNKNOWNERR, "Unknown Error" }, + { 0, NULL } +}; + +/*! \brief compare if two SMPP addresses are equal */ +int smpp_addr_eq(const struct osmo_smpp_addr *a, + const struct osmo_smpp_addr *b) +{ + if (a->ton == b->ton && + a->npi == b->npi && + !strcmp(a->addr, b->addr)) + return 1; + + return 0; +} + + struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, const char *sys_id) { @@ -80,28 +145,91 @@ struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id) acl->smsc = smsc; strcpy(acl->system_id, sys_id); + INIT_LLIST_HEAD(&acl->route_list); - llist_add(&acl->list, &smsc->acl_list); + llist_add_tail(&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; + struct osmo_smpp_route *r, *r2; 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 */ - } + /* kill any active ESMEs */ + if (acl->esme) { + struct osmo_esme *esme = acl->esme; + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; + esme->acl = NULL; + smpp_esme_put(esme); + } + + /* delete all routes for this ACL */ + llist_for_each_entry_safe(r, r2, &acl->route_list, list) { + llist_del(&r->list); + llist_del(&r->global_list); + talloc_free(r); } talloc_free(acl); } +static struct osmo_smpp_route *route_alloc(struct osmo_smpp_acl *acl) +{ + struct osmo_smpp_route *r; + + r = talloc_zero(acl, struct osmo_smpp_route); + if (!r) + return NULL; + + llist_add_tail(&r->list, &acl->route_list); + llist_add_tail(&r->global_list, &acl->smsc->route_list); + + return r; +} + +int smpp_route_pfx_add(struct osmo_smpp_acl *acl, + const struct osmo_smpp_addr *pfx) +{ + struct osmo_smpp_route *r; + + llist_for_each_entry(r, &acl->route_list, list) { + if (r->type == SMPP_ROUTE_PREFIX && + smpp_addr_eq(&r->u.prefix, pfx)) + return -EEXIST; + } + + r = route_alloc(acl); + if (!r) + return -ENOMEM; + r->type = SMPP_ROUTE_PREFIX; + r->acl = acl; + memcpy(&r->u.prefix, pfx, sizeof(r->u.prefix)); + + return 0; +} + +int smpp_route_pfx_del(struct osmo_smpp_acl *acl, + const struct osmo_smpp_addr *pfx) +{ + struct osmo_smpp_route *r, *r2; + + llist_for_each_entry_safe(r, r2, &acl->route_list, list) { + if (r->type == SMPP_ROUTE_PREFIX && + smpp_addr_eq(&r->u.prefix, pfx)) { + llist_del(&r->list); + talloc_free(r); + return 0; + } + } + + return -ENODEV; +} + /*! \brief increaes the use/reference count */ void smpp_esme_get(struct osmo_esme *esme) @@ -120,6 +248,15 @@ static void esme_destroy(struct osmo_esme *esme) talloc_free(esme); } +static uint32_t esme_inc_seq_nr(struct osmo_esme *esme) +{ + esme->own_seq_nr++; + if (esme->own_seq_nr > 0x7fffffff) + esme->own_seq_nr = 1; + + return esme->own_seq_nr; +} + /*! \brief decrease the use/reference count, free if it is 0 */ void smpp_esme_put(struct osmo_esme *esme) { @@ -140,6 +277,62 @@ esme_by_system_id(const struct smsc *smsc, char *system_id) return NULL; } +/*! \brief try to find a SMPP route (ESME) for given destination */ +struct osmo_esme * +smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest) +{ + struct osmo_smpp_route *r; + struct osmo_smpp_acl *acl = NULL; + + DEBUGP(DSMPP, "Looking up route for (%u/%u/%s)\n", + dest->ton, dest->npi, dest->addr); + + /* search for a specific route */ + llist_for_each_entry(r, &smsc->route_list, global_list) { + switch (r->type) { + case SMPP_ROUTE_PREFIX: + DEBUGP(DSMPP, "Checking prefix route (%u/%u/%s)->%s\n", + r->u.prefix.ton, r->u.prefix.npi, r->u.prefix.addr, + r->acl->system_id); + if (r->u.prefix.ton == dest->ton && + r->u.prefix.npi == dest->npi && + !strncmp(r->u.prefix.addr, dest->addr, + strlen(r->u.prefix.addr))) { + DEBUGP(DSMPP, "Found prefix route ACL\n"); + acl = r->acl; + } + break; + default: + break; + } + + if (acl) + break; + } + + if (!acl) { + /* check for default route */ + if (smsc->def_route) { + DEBUGP(DSMPP, "Using existing default route\n"); + acl = smsc->def_route; + } + } + + if (acl && acl->esme) { + struct osmo_esme *esme; + DEBUGP(DSMPP, "ACL even has ESME, we can route to it!\n"); + esme = acl->esme; + if (esme->bind_flags & ESME_BIND_RX) + return esme; + else + LOGP(DSMPP, LOGL_NOTICE, "[%s] is matching route, " + "but not bound for Rx, discarding MO SMS\n", + esme->system_id); + } + + return NULL; +} + /*! \brief initialize the libsmpp34 data structure for a response */ #define INIT_RESP(type, resp, req) { \ @@ -223,12 +416,49 @@ static int smpp_handle_gen_nack(struct osmo_esme *esme, struct msgb *msg) return 0; } +static int _process_bind(struct osmo_esme *esme, uint8_t if_version, + uint32_t bind_flags, const char *sys_id, + const char *passwd) +{ + struct osmo_smpp_acl *acl; + + if (if_version != SMPP_VERSION) + return ESME_RSYSERR; + + if (esme->bind_flags) + return ESME_RALYBND; + + esme->smpp_version = if_version; + snprintf(esme->system_id, sizeof(esme->system_id), "%s", sys_id); + + acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); + if (!esme->smsc->accept_all) { + if (!acl) { + /* This system is unknown */ + return ESME_RINVSYSID; + } else { + if (strlen(acl->passwd) && + strcmp(acl->passwd, passwd)) { + return ESME_RINVPASWD; + } + } + } + if (acl) { + esme->acl = acl; + acl->esme = esme; + } + + esme->bind_flags = bind_flags; + + return ESME_ROK; +} + + /*! \brief handle an incoming SMPP BIND RECEIVER */ 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), @@ -244,41 +474,11 @@ static int smpp_handle_bind_rx(struct osmo_esme *esme, struct msgb *msg) LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Rx from (Version %02x)\n", bind.system_id, bind.interface_version); - if (bind.interface_version != SMPP_VERSION) { - bind_r.command_status = ESME_RSYSERR; - goto err; - } - - if (esme->bind_flags) { - bind_r.command_status = ESME_RALYBND; - goto err; - } - - 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; - } - } + rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX, + (const char *)bind.system_id, (const char *)bind.password); + bind_r.command_status = rc; - esme->bind_flags = ESME_BIND_RX; -err: - return 0; + return PACK_AND_SEND(esme, &bind_r); } /*! \brief handle an incoming SMPP BIND TRANSMITTER */ @@ -286,7 +486,6 @@ 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; @@ -303,36 +502,9 @@ static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Tx (Version %02x)\n", bind.system_id, bind.interface_version); - if (bind.interface_version != SMPP_VERSION) { - bind_r.command_status = ESME_RSYSERR; - goto err; - } - - if (esme->bind_flags) { - bind_r.command_status = ESME_RALYBND; - goto err; - } - - 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; + rc = _process_bind(esme, bind.interface_version, ESME_BIND_TX, + (const char *)bind.system_id, (const char *)bind.password); + bind_r.command_status = rc; /* build response */ snprintf((char *)bind_r.system_id, sizeof(bind_r.system_id), "%s", @@ -344,7 +516,6 @@ static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) tlv.value.val16 = esme->smpp_version; build_tlv(&bind_r.tlv, &tlv); -err: return PACK_AND_SEND(esme, &bind_r); } @@ -353,7 +524,6 @@ 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), @@ -369,41 +539,11 @@ static int smpp_handle_bind_trx(struct osmo_esme *esme, struct msgb *msg) LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Trx (Version %02x)\n", bind.system_id, bind.interface_version); - if (bind.interface_version != SMPP_VERSION) { - bind_r.command_status = ESME_RSYSERR; - goto err; - } - - if (esme->bind_flags) { - bind_r.command_status = ESME_RALYBND; - goto err; - } - - esme->smpp_version = bind.interface_version; - snprintf(esme->system_id, sizeof(esme->system_id), "%s", bind.system_id); + rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX|ESME_BIND_TX, + (const char *)bind.system_id, (const char *)bind.password); + bind_r.command_status = rc; - 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; - -err: - return 0; + return PACK_AND_SEND(esme, &bind_r); } /*! \brief handle an incoming SMPP UNBIND */ @@ -431,8 +571,6 @@ 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); } @@ -495,7 +633,7 @@ int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, alert.command_length = 0; alert.command_id = ALERT_NOTIFICATION; alert.command_status = ESME_ROK; - alert.sequence_number = esme->own_seq_nr++; + alert.sequence_number = esme_inc_seq_nr(esme); alert.source_addr_ton = ton; alert.source_addr_npi = npi; snprintf((char *)alert.source_addr, sizeof(alert.source_addr), "%s", addr); @@ -513,6 +651,36 @@ int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, return PACK_AND_SEND(esme, &alert); } +/* \brief send a DELIVER-SM message to given ESME */ +int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver) +{ + deliver->sequence_number = esme_inc_seq_nr(esme); + + return PACK_AND_SEND(esme, deliver); +} + +/*! \brief handle an incoming SMPP DELIVER-SM RESPONSE */ +static int smpp_handle_deliver_resp(struct osmo_esme *esme, struct msgb *msg) +{ + struct deliver_sm_resp_t deliver_r; + int rc; + + memset(&deliver_r, 0, sizeof(deliver_r)); + SMPP34_UNPACK(rc, DELIVER_SM_RESP, &deliver_r, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + LOGP(DSMPP, LOGL_INFO, "[%s] Rx DELIVER-SM RESP (%s)\n", + esme->system_id, get_value_string(smpp_status_strs, + deliver_r.command_status)); + + return 0; +} + /*! \brief handle an incoming SMPP SUBMIT-SM */ static int smpp_handle_submit(struct osmo_esme *esme, struct msgb *msg) { @@ -580,6 +748,9 @@ static int smpp_pdu_rx(struct osmo_esme *esme, struct msgb *msg) case SUBMIT_SM: rc = smpp_handle_submit(esme, msg); break; + case DELIVER_SM_RESP: + rc = smpp_handle_deliver_resp(esme, msg); + break; case DELIVER_SM: break; case DATA_SM: @@ -698,6 +869,7 @@ static int link_accept_cb(struct smsc *smsc, int fd, smpp_esme_get(esme); esme->own_seq_nr = rand(); + esme_inc_seq_nr(esme); esme->smsc = smsc; osmo_wqueue_init(&esme->wqueue, 10); esme->wqueue.bfd.fd = fd; @@ -746,6 +918,7 @@ int smpp_smsc_init(struct smsc *smsc, uint16_t port) if (smsc->listen_ofd.fd <= 0) { INIT_LLIST_HEAD(&smsc->esme_list); INIT_LLIST_HEAD(&smsc->acl_list); + INIT_LLIST_HEAD(&smsc->route_list); smsc->listen_ofd.data = smsc; smsc->listen_ofd.cb = smsc_fd_cb; } else { diff --git a/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h index 4b5bd85be..be72a0c7c 100644 --- a/openbsc/src/libmsc/smpp_smsc.h +++ b/openbsc/src/libmsc/smpp_smsc.h @@ -22,6 +22,12 @@ enum esme_read_state { struct osmo_smpp_acl; +struct osmo_smpp_addr { + uint8_t ton; + uint8_t npi; + char addr[21+1]; +}; + struct osmo_esme { struct llist_head list; struct smsc *smsc; @@ -48,29 +54,54 @@ struct osmo_esme { struct osmo_smpp_acl { struct llist_head list; struct smsc *smsc; + struct osmo_esme *esme; char *description; char system_id[SMPP_SYS_ID_LEN+1]; char passwd[SMPP_PASSWD_LEN+1]; int default_route; + int deliver_src_imsi; + struct llist_head route_list; +}; + +enum osmo_smpp_rtype { + SMPP_ROUTE_NONE, + SMPP_ROUTE_PREFIX, +}; + +struct osmo_smpp_route { + struct llist_head list; /*!< in acl.route_list */ + struct llist_head global_list; /*!< in smsc->route_list */ + struct osmo_smpp_acl *acl; + enum osmo_smpp_rtype type; + union { + struct osmo_smpp_addr prefix; + } u; }; + struct smsc { struct osmo_fd listen_ofd; struct llist_head esme_list; struct llist_head acl_list; + struct llist_head route_list; uint16_t listen_port; char system_id[SMPP_SYS_ID_LEN+1]; int accept_all; - struct osmo_esme *def_route; + struct osmo_smpp_acl *def_route; void *priv; }; +int smpp_addr_eq(const struct osmo_smpp_addr *a, + const struct osmo_smpp_addr *b); 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_esme * +smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest); + 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); @@ -82,7 +113,13 @@ int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, const char *addr, uint8_t avail_status); +int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver); + int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, struct submit_sm_resp_t *submit_r); +int smpp_route_pfx_add(struct osmo_smpp_acl *acl, + const struct osmo_smpp_addr *pfx); +int smpp_route_pfx_del(struct osmo_smpp_acl *acl, + const struct osmo_smpp_addr *pfx); #endif diff --git a/openbsc/src/libmsc/smpp_vty.c b/openbsc/src/libmsc/smpp_vty.c index ad496da79..c6bb74ddc 100644 --- a/openbsc/src/libmsc/smpp_vty.c +++ b/openbsc/src/libmsc/smpp_vty.c @@ -18,7 +18,9 @@ * */ +#include <ctype.h> #include <string.h> +#include <errno.h> #include <netdb.h> #include <sys/socket.h> @@ -195,18 +197,117 @@ DEFUN(cfg_esme_no_passwd, cfg_esme_no_passwd_cmd, 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") +static int osmo_is_digits(const char *str) +{ + int i; + for (i = 0; i < strlen(str); i++) { + if (!isdigit(str[i])) + return 0; + } + return 1; +} + +static const struct value_string route_errstr[] = { + { -EEXIST, "Route already exists" }, + { -ENODEV, "Route does not exist" }, + { -ENOMEM, "No memory" }, + { -EINVAL, "Invalid" }, + { 0, NULL } +}; + +static const struct value_string smpp_ton_str_short[] = { + { TON_Unknown, "unknown" }, + { TON_International, "international" }, + { TON_National, "national" }, + { TON_Network_Specific, "network" }, + { TON_Subscriber_Number,"subscriber" }, + { TON_Alphanumeric, "alpha" }, + { TON_Abbreviated, "abbrev" }, + { 0, NULL } +}; + +static const struct value_string smpp_npi_str_short[] = { + { NPI_Unknown, "unknown" }, + { NPI_ISDN_E163_E164, "isdn" }, + { NPI_Data_X121, "x121" }, + { NPI_Telex_F69, "f69" }, + { NPI_Land_Mobile_E212, "e212" }, + { NPI_National, "national" }, + { NPI_Private, "private" }, + { NPI_ERMES, "ermes" }, + { NPI_Internet_IP, "ip" }, + { NPI_WAP_Client_Id, "wap" }, + { 0, NULL } +}; + + +#define SMPP_ROUTE_STR "Configure a route for MO-SMS to be sent to this ESME\n" +#define SMPP_ROUTE_P_STR "Prefix-match route\n" +#define SMPP_PREFIX_STR "Destination number prefix\n" + +#define TON_CMD "(unknown|international|national|network|subscriber|alpha|abbrev)" +#define NPI_CMD "(unknown|isdn|x121|f69|e212|national|private|ermes|ip|wap)" +#define TON_STR "FIXME" +#define NPI_STR "FIXME" + +DEFUN(cfg_esme_route_pfx, cfg_esme_route_pfx_cmd, + "route prefix " TON_CMD " " NPI_CMD " PREFIX", + SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR) { struct osmo_smpp_acl *acl = vty->index; + struct osmo_smpp_addr pfx; + int rc; + + /* check if DESTINATION is all-digits */ + if (!osmo_is_digits(argv[2])) { + vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE); + return CMD_WARNING; + } - /* FIXME: check if DESTINATION is all-digits */ + pfx.ton = get_string_value(smpp_ton_str_short, argv[0]); + pfx.npi = get_string_value(smpp_npi_str_short, argv[1]); + snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]); + + rc = smpp_route_pfx_add(acl, &pfx); + if (rc < 0) { + vty_out(vty, "%% error adding prefix route: %s%s", + get_value_string(route_errstr, rc), VTY_NEWLINE); + return CMD_WARNING; + } return CMD_SUCCESS; } +DEFUN(cfg_esme_no_route_pfx, cfg_esme_no_route_pfx_cmd, + "no route prefix " TON_CMD " " NPI_CMD " PREFIX", + NO_STR SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR) +{ + struct osmo_smpp_acl *acl = vty->index; + struct osmo_smpp_addr pfx; + int rc; + + /* check if DESTINATION is all-digits */ + if (!osmo_is_digits(argv[2])) { + vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE); + return CMD_WARNING; + } + + pfx.ton = get_string_value(smpp_ton_str_short, argv[0]); + pfx.npi = get_string_value(smpp_npi_str_short, argv[1]); + snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]); + + rc = smpp_route_pfx_del(acl, &pfx); + if (rc < 0) { + vty_out(vty, "%% error removing prefix route: %s%s", + get_value_string(route_errstr, rc), VTY_NEWLINE); + return CMD_WARNING; + } + + 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") @@ -215,6 +316,9 @@ DEFUN(cfg_esme_defaultroute, cfg_esme_defaultroute_cmd, acl->default_route = 1; + if (!acl->smsc->def_route) + acl->smsc->def_route = acl; + return CMD_SUCCESS; } @@ -228,7 +332,7 @@ DEFUN(cfg_no_esme_defaultroute, cfg_esme_no_defaultroute_cmd, /* remove currently active default route, if it was created by * this ACL */ - if (acl->smsc->def_route && acl->smsc->def_route->acl == acl) + if (acl->smsc->def_route && acl->smsc->def_route == acl) acl->smsc->def_route = NULL; return CMD_SUCCESS; @@ -244,8 +348,11 @@ static void dump_one_esme(struct vty *vty, struct osmo_esme *esme) 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); + esme->system_id, esme->acl ? esme->acl->passwd : "", + esme->smpp_version, VTY_NEWLINE); vty_out(vty, " Connected from: %s:%s%s", host, serv, VTY_NEWLINE); + if (esme->smsc->def_route == esme->acl) + vty_out(vty, " Is current default route%s", VTY_NEWLINE); } DEFUN(show_esme, show_esme_cmd, @@ -261,13 +368,35 @@ DEFUN(show_esme, show_esme_cmd, return CMD_SUCCESS; } +static void write_esme_route_single(struct vty *vty, struct osmo_smpp_route *r) +{ + switch (r->type) { + case SMPP_ROUTE_PREFIX: + vty_out(vty, " route prefix %s ", + get_value_string(smpp_ton_str_short, r->u.prefix.ton)); + vty_out(vty, "%s %s%s", + get_value_string(smpp_npi_str_short, r->u.prefix.npi), + r->u.prefix.addr, VTY_NEWLINE); + break; + case SMPP_ROUTE_NONE: + break; + } +} + static void config_write_esme_single(struct vty *vty, struct osmo_smpp_acl *acl) { + struct osmo_smpp_route *r; + 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); + if (acl->deliver_src_imsi) + vty_out(vty, " deliver-src-imsi%s", VTY_NEWLINE); + + llist_for_each_entry(r, &acl->route_list, list) + write_esme_route_single(vty, r); } static int config_write_esme(struct vty *v) @@ -297,7 +426,8 @@ int smpp_vty_init(void) 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_route_pfx_cmd); + install_element(SMPP_ESME_NODE, &cfg_esme_no_route_pfx_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); |