diff options
author | Harald Welte <laforge@gnumonks.org> | 2017-04-05 21:45:50 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2017-04-09 21:30:52 +0200 |
commit | 46d65a69a23d62a1815365186e1aac758c4606c4 (patch) | |
tree | 34ad193b51cb935c0f5fe17cb138df51eb2932d4 | |
parent | 6391c30a220a161a13f88a1354aa1b3691c0e5a4 (diff) |
Add M3UA RKM (routing key management) support, SGW side only
Change-Id: I9b1cf438a42519c0fe2f555c1672fafa499122a1
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/m3ua.c | 4 | ||||
-rw-r--r-- | src/xua_internal.h | 1 | ||||
-rw-r--r-- | src/xua_rkm.c | 296 |
4 files changed, 301 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index a4cfeeb..7867282 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,7 +28,7 @@ LIBVERSION=0:0:0 libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c m3ua.c xua_msg.c sccp_helpers.c \ sccp2sua.c sccp_scrc.c sccp_sclc.c sccp_scoc.c \ - sccp_user.c \ + sccp_user.c xua_rkm.c \ osmo_ss7.c osmo_ss7_hmrt.c xua_asp_fsm.c xua_as_fsm.c libosmo_sigtran_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_' libosmo_sigtran_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS) @@ -666,8 +666,10 @@ int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg) case M3UA_MSGC_MGMT: rc = m3ua_rx_mgmt(asp, xua); break; - case M3UA_MSGC_SNM: case M3UA_MSGC_RKM: + rc = m3ua_rx_rkm(asp, xua); + break; + case M3UA_MSGC_SNM: /* FIXME */ LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unsupported M3UA " "Message Class %u\n", xua->hdr.msg_class); diff --git a/src/xua_internal.h b/src/xua_internal.h index c6f79b7..24fcb1c 100644 --- a/src/xua_internal.h +++ b/src/xua_internal.h @@ -58,3 +58,4 @@ struct m3ua_notify_params { struct xua_msg *m3ua_encode_notify(const struct m3ua_notify_params *npar); int m3ua_decode_notify(struct m3ua_notify_params *npar, void *ctx, const struct xua_msg *xua); +int m3ua_rx_rkm(struct osmo_ss7_asp *asp, struct xua_msg *xua); diff --git a/src/xua_rkm.c b/src/xua_rkm.c new file mode 100644 index 0000000..12d59c7 --- /dev/null +++ b/src/xua_rkm.c @@ -0,0 +1,296 @@ +/* xUA Routing Key Management (RKM) as per RFC 4666 */ +/* (C) 2017 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 General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/linuxlist.h> + +#include <osmocom/sigtran/xua_msg.h> +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/protocol/m3ua.h> + +#include "xua_internal.h" + +/* push a M3UA header to the front of the given message */ +static void msgb_push_m3ua_hdr(struct msgb *msg, uint8_t msg_class, uint8_t msg_type) +{ + struct xua_common_hdr *hdr; + + msg->l2h = msgb_push(msg, sizeof(*hdr)); + hdr = (struct xua_common_hdr *) msg->l2h; + + hdr->version = M3UA_VERSION; + hdr->spare = 0; + hdr->msg_class = msg_class; + hdr->msg_type = msg_type; + hdr->msg_length = htonl(msgb_l2len(msg)); +} + +/* append a single registration result to given msgb */ +static int msgb_append_reg_res(struct msgb *msg, uint32_t local_rk_id, + uint32_t status, uint32_t rctx) +{ + uint8_t *old_tail = msg->tail; + + /* One individual Registration Result according to Chapter 3.6.2 */ + msgb_put_u16(msg, M3UA_IEI_REG_RESULT); /* outer IEI */ + msgb_put_u16(msg, 24 + 4); /* outer length */ + /* nested IEIs */ + msgb_t16l16vp_put_u32(msg, M3UA_IEI_LOC_RKEY_ID, local_rk_id); + msgb_t16l16vp_put_u32(msg, M3UA_IEI_REG_STATUS, status); + msgb_t16l16vp_put_u32(msg, M3UA_IEI_ROUTE_CTX, rctx); + + return msg->tail - old_tail; +} + +/* append a single de-registration result to given msgb */ +static int msgb_append_dereg_res(struct msgb *msg, + uint32_t status, uint32_t rctx) +{ + uint8_t *old_tail = msg->tail; + + /* One individual De-Registration Result according to Chapter 3.6.4 */ + msgb_put_u16(msg, M3UA_IEI_DEREG_RESULT); /* outer IEI */ + msgb_put_u16(msg, 16 + 4); /* outer length */ + /* nested IEIs */ + msgb_t16l16vp_put_u32(msg, M3UA_IEI_ROUTE_CTX, rctx); + msgb_t16l16vp_put_u32(msg, M3UA_IEI_DEREG_STATUS, status); + + return msg->tail - old_tail; +} + +/* handle a single registration request IE (nested IEs in 'innner' */ +static int handle_rkey_reg(struct osmo_ss7_asp *asp, struct xua_msg *inner, + struct msgb *resp) +{ + uint32_t rk_id, rctx, _tmode, dpc; + enum osmo_ss7_as_traffic_mode tmode; + struct osmo_ss7_as *as; + struct osmo_ss7_route *rt; + char namebuf[32]; + + /* mandatory local routing key ID */ + rk_id = xua_msg_get_u32(inner, M3UA_IEI_LOC_RKEY_ID); + /* ASP may already include a routing context value here */ + rctx = xua_msg_get_u32(inner, M3UA_IEI_ROUTE_CTX); + + /* traffic mode type (0 = undefined) */ + _tmode = xua_msg_get_u32(inner, M3UA_IEI_TRAF_MODE_TYP); + if (xua_msg_find_tag(inner, M3UA_IEI_TRAF_MODE_TYP) && _tmode != M3UA_TMOD_OVERRIDE && + _tmode != M3UA_TMOD_LOADSHARE && _tmode != M3UA_TMOD_BCAST) { + LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: Invalid Traffic Mode %u\n", _tmode); + msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_UNSUPP_TRAF_MODE, 0); + return -1; + } + + tmode = osmo_ss7_tmode_from_xua(_tmode); + + /* destination point code (mandatory) */ + dpc = xua_msg_get_u32(inner, M3UA_IEI_DEST_PC); + + /* We don't support routing keys with the following criteria, so + * we have to reject those */ + /* TODO: network appearance (optional) */ + /* TODO: service indicators (optional) */ + /* TODO: originating point code list (optional) */ + if (xua_msg_find_tag(inner, M3UA_IEI_NET_APPEAR) || + xua_msg_find_tag(inner, M3UA_IEI_SVC_IND) || + xua_msg_find_tag(inner, M3UA_IEI_ORIG_PC)) { + LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: Unsupported Routing Key\n"); + msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_UNSUPP_RK_PARAM, 0); + return -1; + } + + /* if the ASP did not include a routing context number, allocate + * one locally (will be part of response) */ + if (!rctx) + rctx = osmo_ss7_find_free_rctx(asp->inst); + + LOGPASP(asp, DLSS7, LOGL_INFO, "RKM: Registering routing key %u for DPC %s\n", + rctx, osmo_ss7_pointcode_print(asp->inst, dpc)); + + /* check if there is already an AS for this routing key */ + if (osmo_ss7_as_find_by_rctx(asp->inst, rctx)) { + LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: RCTX %u already in use\n", rctx); + msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_RKEY_ALRDY_REGD, 0); + return -1; + } + + /* Create an AS for this routing key */ + snprintf(namebuf, sizeof(namebuf), "as-rkm-%u", rctx); + as = osmo_ss7_as_find_or_create(asp->inst, namebuf, OSMO_SS7_ASP_PROT_M3UA); + if (!as) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "RKM: Cannot create AS %s\n", namebuf); + msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_INSUFF_RESRC, 0); + return -1; + } + + as->cfg.description = talloc_strdup(as, "Auto-generated by RKM"); + as->cfg.mode = tmode; + /* fill routing key */ + as->cfg.routing_key.context = rctx; + as->cfg.routing_key.pc = dpc; + + /* add route for that routing key */ + rt = osmo_ss7_route_create(as->inst->rtable_system, dpc, 0xFFFFFF, namebuf); + if (!rt) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "RKM: Cannot insert route for DPC %s / as %s\n", + osmo_ss7_pointcode_print(asp->inst, dpc), namebuf); + osmo_ss7_as_destroy(as); + msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_CANT_SUPP_UNQ_RT, 0); + return -1; + } + + /* Success: Add just-create AS to connected ASP + report success */ + osmo_ss7_as_add_asp(as, asp->cfg.name); + msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_SUCCESS, rctx); + return 0; +} + +/* receive a registration requuest (SG role) */ +static int m3ua_rx_rkm_reg_req(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + struct xua_msg_part *part; + struct msgb *resp = m3ua_msgb_alloc(__func__); + + /* iterate over all routing key IEs in message */ + llist_for_each_entry(part, &xua->headers, entry) { + struct xua_msg *inner; + + if (part->tag != M3UA_IEI_ROUT_KEY) + continue; + + inner = xua_from_nested(part); + if (!inner) { + LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: Unable to parse " + "nested IE for Routing Key\n"); + continue; + } + /* handle single registration and append result to + * 'resp' */ + handle_rkey_reg(asp, inner, resp); + } + msgb_push_m3ua_hdr(resp, M3UA_MSGC_RKM, M3UA_RKM_REG_RSP); + osmo_ss7_asp_send(asp, resp); + + return 0; +} + +/* receive a deregistration requuest (SG role) */ +static int handle_rkey_dereg(struct osmo_ss7_asp *asp, uint32_t rctx, + struct msgb *resp) +{ + struct osmo_ss7_instance *inst = asp->inst; + struct osmo_ss7_as *as; + struct osmo_ss7_route *rt; + + as = osmo_ss7_as_find_by_rctx(inst, rctx); + if (!as) { + msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_INVAL_RCTX, 0); + return -1; + } + + /* Reject if ASP is not even part of AS */ + if (!osmo_ss7_as_has_asp(as, asp)) { + msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_INVAL_RCTX, 0); + return -1; + } + + /* FIXME Reject if any ASP stillactively using this RCTX */ + + rt = osmo_ss7_route_find_dpc(inst->rtable_system, as->cfg.routing_key.pc); + if (!rt) { + msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_UNKNOWN, 0); + return -1; + } + + LOGPASP(asp, DLSS7, LOGL_INFO, "RKM: De-Registering rctx %u for DPC %s\n", + rctx, osmo_ss7_pointcode_print(inst, as->cfg.routing_key.pc)); + + /* remove route + AS definition */ + osmo_ss7_route_destroy(rt); + osmo_ss7_as_destroy(as); + /* report success */ + msgb_append_dereg_res(resp, M3UA_RKM_DEREG_SUCCESS, rctx); + + return 0; +} + +static int m3ua_rx_rkm_dereg_req(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + struct xua_msg_part *part = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX); + struct msgb *resp = m3ua_msgb_alloc(__func__); + uint32_t *rctx; + + if (!part) + return -1; + + for (rctx = (uint32_t *)part->dat; (uint8_t *)rctx < part->dat + part->len; rctx++) + handle_rkey_dereg(asp, ntohl(*rctx), resp); + + msgb_push_m3ua_hdr(resp, M3UA_MSGC_RKM, M3UA_RKM_DEREG_RSP); + osmo_ss7_asp_send(asp, resp); + + return 0; +} + +/* receive a registration response (ASP role) */ +static int m3ua_rx_rkm_reg_rsp(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + /* TODO */ +} + +/* receive a deregistration response (ASP role) */ +static int m3ua_rx_rkm_dereg_rsp(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + /* TODO */ +} + +/* process an incoming RKM message in xua format */ +int m3ua_rx_rkm(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + int rc; + + switch (xua->hdr.msg_type) { + /* SG Side */ + case M3UA_RKM_REG_REQ: + /* TOOD: ensure we are role SG */ + rc = m3ua_rx_rkm_reg_req(asp, xua); + break; + case M3UA_RKM_DEREG_REQ: + /* TOOD: ensure we are role SG */ + rc = m3ua_rx_rkm_dereg_req(asp, xua); + break; + /* ASP Side */ + case M3UA_RKM_REG_RSP: + /* TOOD: ensure we are role ASP */ + rc = m3ua_rx_rkm_reg_rsp(asp, xua); + break; + case M3UA_RKM_DEREG_RSP: + /* TOOD: ensure we are role ASP */ + rc = m3ua_rx_rkm_dereg_rsp(asp, xua); + break; + default: + LOGPASP(asp, DLSS7, LOGL_ERROR, "Received unknown RKM msg_type %u\n", + xua->hdr.msg_type); + rc = -1; + break; + } + return rc; +} |