From 85d1b36e9d1bc7803e938661f86113b5628d2bff Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 5 Aug 2019 21:41:19 +0200 Subject: [PATCH] foo Change-Id: I266dfb3862e7c9794fc1bbbb143740eb120b3125 --- cbc.schema.json | 2 +- src/Makefile.am | 2 +- src/cbc_data.h | 7 +- src/internal.h | 8 +++ src/message_handling.c | 159 +++++++++++++++++++++++++++++++++++++++++ src/rest_api.c | 116 +++++++++++++++++++++++++++++- 6 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 src/message_handling.c diff --git a/cbc.schema.json b/cbc.schema.json index 1530f58..b33d0eb 100644 --- a/cbc.schema.json +++ b/cbc.schema.json @@ -33,10 +33,10 @@ { "$ref": "#/definitions/scope_plmn" } ] }, - "cbc_message": { "type": "object", "properties": { + "cbe_name": { "type": "string" }, "category": { "$ref": "#/definitions/category" }, "rep_period": { "$ref": "#/definitions/repetition_period" }, "num_of_bcast": { "$ref": "#/definitions/number_of_broadcasts" }, diff --git a/src/Makefile.am b/src/Makefile.am index 9747863..e3ee969 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,7 @@ EXTRA_DIST = cbc_data.h cbsp_server.h charset.h internal.h bin_PROGRAMS = osmo-cbc osmo_cbc_SOURCES = cbc_main.c cbc_data.c cbc_vty.c cbsp_server.c cbsp_server_fsm.c \ - rest_api.c charset.c + rest_api.c charset.c message_handling.c osmo_cbc_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBOSMONETIF_LIBS) \ $(ULFIUS_LIBS) $(JANSSON_LIBS) $(ORCANIA_LIBS) diff --git a/src/cbc_data.h b/src/cbc_data.h index 24f8974..1725a47 100644 --- a/src/cbc_data.h +++ b/src/cbc_data.h @@ -80,13 +80,14 @@ struct cbc_message_peer { /* internal representation of a CBC message */ struct cbc_message { - struct llist_head list; /* global list of currently active CBCs */ + struct llist_head list; /* global list of currently active CBCs */ + const char *cbe_name; /* name of the CBE originating this SMSCB */ enum cbsp_category priority; uint16_t rep_period; /* repetition period (1..4095) */ bool extended_cbch; /* basic (false) or extended (true) CBCH */ - uint16_t warning_period_sec; /* warning period in seconds */ - uint16_t num_bcast; /* number of broadcasts requested */ + uint32_t warning_period_sec; /* warning period in seconds (0xffffffff = unlimited) */ + uint16_t num_bcast; /* number of broadcasts requested (0=unlimited) */ enum cbc_message_scope scope; /* FIXME: data for other scopes than PLMN-wide */ diff --git a/src/internal.h b/src/internal.h index a0d50cb..a8d8a69 100644 --- a/src/internal.h +++ b/src/internal.h @@ -1,8 +1,12 @@ #pragma once +#include + #include #include +#include "cbc_data.h" + enum { DCBSP, DREST, @@ -25,3 +29,7 @@ void rest_api_fin(void); /* cbc_vty.c */ void cbc_vty_init(void); + +/* message_handling.c */ +int cbc_message_new(struct cbc_message *cbcmsg); +struct cbc_message *cbc_message_by_id(uint16_t message_id); diff --git a/src/message_handling.c b/src/message_handling.c new file mode 100644 index 0000000..70008c9 --- /dev/null +++ b/src/message_handling.c @@ -0,0 +1,159 @@ +/* Osmocom CBC (Cell Broacast Centre) */ + +/* (C) 2019 by Harald Welte + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * 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 . + * + */ + + +#include + +#include +#include +#include + +#include "cbc_data.h" +#include "cbsp_server.h" +#include "internal.h" + +/* convert cbc_message to osmo_cbsp_cell_list */ +static int cbcmsg_to_cbsp_cell_list(const void *ctx, struct osmo_cbsp_cell_list *list, + const struct cbc_message *cbcmsg) +{ + struct osmo_cbsp_cell_ent *ent; + + switch (cbcmsg->scope) { + case CBC_MSG_SCOPE_PLMN: + list->id_discr = CELL_IDENT_BSS; + ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent); + if (!ent) + return -ENOMEM; + //ent->cell_id = ? + llist_add_tail(&ent->list, &list->list); + return 0; + default: + OSMO_ASSERT(0); + } +} + +/* generate a CBSP WRITE-REPLACE from our internal representation */ +struct osmo_cbsp_decoded *cbcmsg_to_cbsp(void *ctx, const struct cbc_message *cbcmsg) +{ + struct osmo_cbsp_write_replace *wrepl; + const struct smscb_message *smscb = &cbcmsg->msg; + struct osmo_cbsp_decoded *cbsp = osmo_cbsp_decoded_alloc(ctx, CBSP_MSGT_WRITE_REPLACE); + unsigned int i; + int rc; + + if (!cbsp) + return NULL; + wrepl = &cbsp->u.write_replace; + + wrepl->msg_id = smscb->message_id; + wrepl->new_serial_nr = smscb->serial_nr; + /* FIXME: old? */ + /* Cell list */ + rc = cbcmsg_to_cbsp_cell_list(smscb, &wrepl->cell_list, cbcmsg); + if (rc < 0) { + talloc_free(cbsp); + return NULL; + } + if (!smscb->is_etws) + wrepl->is_cbs = true; + if (wrepl->is_cbs) { + if (cbcmsg->extended_cbch) + wrepl->u.cbs.channel_ind = CBSP_CHAN_IND_EXTENDED; + else + wrepl->u.cbs.channel_ind = CBSP_CHAN_IND_BASIC; + wrepl->u.cbs.category = cbcmsg->priority; + wrepl->u.cbs.rep_period = cbcmsg->rep_period; + wrepl->u.cbs.num_bcast_req = cbcmsg->num_bcast; + wrepl->u.cbs.dcs = smscb->cbs.dcs; + for (i = 0; i < smscb->cbs.num_pages; i++) { + struct osmo_cbsp_content *ce = talloc_zero(cbsp, struct osmo_cbsp_content); + // FIXME: ce->user_len = + memcpy(ce->data, smscb->cbs.data[i], SMSCB_RAW_PAGE_LEN); + llist_add_tail(&ce->list, &wrepl->u.cbs.msg_content); + } + } else { + /* FIXME */ + talloc_free(cbsp); + return NULL; + } + return cbsp; +} + +/* determine if peer is within scope of cbc_msg */ +static bool is_peer_in_scope(const struct cbc_peer *peer, const struct cbc_message *cbcmsg) +{ + switch (cbcmsg->scope) { + case CBC_MSG_SCOPE_PLMN: + return true; + default: + OSMO_ASSERT(0); + } +} + +/* send given message to given peer */ +int peer_new_cbc_message(struct cbc_peer *peer, struct cbc_message *cbcmsg) +{ + struct osmo_cbsp_decoded *cbsp; + + switch (peer->proto) { + case CBC_PEER_PROTO_CBSP: + cbsp = cbcmsg_to_cbsp(peer, cbcmsg); + cbsp_cbc_client_tx(peer->client.cbsp, cbsp); + break; + default: + OSMO_ASSERT(0); + } + + /* record that we've sent the message to the peer */ + cbc_message_add_peer(cbcmsg, peer); + + return 0; +} + +/* receive a new CBC message */ +int cbc_message_new(struct cbc_message *cbcmsg) +{ + struct cbc_peer *peer; + + /* FIXME: check for duplicate message_id / serial_nr */ + + /* add to global list of messages */ + llist_add_tail(&cbcmsg->list, &g_cbc->messages); + + /* iterate over all peers */ + llist_for_each_entry(peer, &g_cbc->peers, list) { + if (!is_peer_in_scope(peer, cbcmsg)) + continue; + peer_new_cbc_message(peer, cbcmsg); + } + return 0; +} + +struct cbc_message *cbc_message_by_id(uint16_t message_id) +{ + struct cbc_message *cbc_msg; + llist_for_each_entry(cbc_msg, &g_cbc->messages, list) { + if (cbc_msg->msg.message_id == message_id) + return cbc_msg; + } + return NULL; +} diff --git a/src/rest_api.c b/src/rest_api.c index 01952e0..ade6b12 100644 --- a/src/rest_api.c +++ b/src/rest_api.c @@ -320,6 +320,7 @@ static int json2payload(struct smscb_message *out, json_t *in) } +/* decode a "smscb.schema.json#definitions/smscb_message" */ static int json2smscb_message(struct smscb_message *out, json_t *in) { json_t *jser_nr, *jtmp; @@ -345,11 +346,117 @@ static int json2smscb_message(struct smscb_message *out, json_t *in) return 0; } +static const struct value_string category_str_vals[] = { + { CBSP_CATEG_NORMAL, "normal" }, + { CBSP_CATEG_HIGH_PRIO, "high_priority" }, + { CBSP_CATEG_BACKGROUND, "background" }, + { 0, NULL } +}; + +/* decode a "cbc.schema.json#definitions/cbc_message" */ +static int json2cbc_message(struct cbc_message *out, json_t *in) +{ + const char *category_str, *cbe_str; + json_t *jtmp; + int rc, tmp; + + if (!json_is_object(in)) + return -EINVAL; + + /* CBE name (M) */ + cbe_str = json_get_string(in, "cbe_name"); + if (!cbe_str) + return -EINVAL; + out->cbe_name = talloc_strdup(out, cbe_str); + + /* Category (O) */ + category_str = json_get_string(in, "category"); + if (!category_str) + out->priority = CBSP_CATEG_NORMAL; + else { + rc = get_string_value(category_str_vals, category_str); + if (rc < 0) + return -EINVAL; + out->priority = rc; + } + + /* Repetition Period (O) */ + jtmp = json_object_get(in, "repetition_period"); + if (jtmp) { + } + + /* Number of Broadcasts (O) */ + rc = json_get_integer_range(&tmp, in, "number_of_broadcasts", 0, 65535); + if (rc == 0) + out->num_bcast = tmp; + else if (rc == -ENOENT) + out->num_bcast = 0; /* unlimited */ + else + return rc; + + /* Warning Period in seconds (O) */ + rc = json_get_integer_range(&tmp, in, "warning_period_sec", 0, 65535); + if (rc == 0) + out->warning_period_sec = tmp; + else if (rc == -ENOENT) + out->warning_period_sec = 0xffffffff; /* infinite */ + else + return rc; + + /* [Geographic] Scope (M) */ + jtmp = json_object_get(in, "scope"); + if (!jtmp) + return -EINVAL; + if ((jtmp = json_object_get(jtmp, "scope_plmn"))) { + out->scope = CBC_MSG_SCOPE_PLMN; + } else + return -EINVAL; + + /* SMSCB message itself */ + jtmp = json_object_get(in, "smscb_message"); + if (!jtmp) + return -EINVAL; + rc = json2smscb_message(&out->msg, jtmp); + if (rc < 0) + return rc; + + return 0; +} + static int api_cb_message_post(const struct _u_request *req, struct _u_response *resp, void *user_data) +{ + struct cbc_message *cbc_msg = talloc_zero(g_cbc, struct cbc_message); + json_error_t json_err; + json_t *json_req = NULL; + int rc; + + json_req = ulfius_get_json_body_request(req, &json_err); + if (!json_req) { + LOGP(DREST, LOGL_ERROR, "REST: No JSON Body\n"); + goto err; + } + + rc = json2cbc_message(cbc_msg, json_req); + if (rc < 0) + goto err; + + /* FIXME: actually add the message */ + + json_decref(json_req); + ulfius_set_empty_body_response(resp, 200); + return U_CALLBACK_COMPLETE; +err: + json_decref(json_req); + talloc_free(cbc_msg); + ulfius_set_empty_body_response(resp, 400); + return U_CALLBACK_COMPLETE; +} + +static int api_cb_message_del(const struct _u_request *req, struct _u_response *resp, void *user_data) { struct smscb_message message; json_error_t json_err; - json_t *json_req; + json_t *json_req = NULL; int rc; json_req = ulfius_get_json_body_request(req, &json_err); @@ -362,16 +469,23 @@ static int api_cb_message_post(const struct _u_request *req, struct _u_response if (rc < 0) goto err; + /* FIXME: actually look-up and delete the message */ + + + json_decref(json_req); ulfius_set_empty_body_response(resp, 200); return U_CALLBACK_COMPLETE; err: + json_decref(json_req); ulfius_set_empty_body_response(resp, 400); return U_CALLBACK_COMPLETE; } + static const struct _u_endpoint api_endpoints[] = { /* create/update a message */ { "POST", PREFIX, "/message", 0, &api_cb_message_post, NULL }, + { "DELETE", PREFIX, "/message/:FIXME", 0, &api_cb_message_del, NULL }, }; static struct _u_instance g_instance;