2017-04-03 15:37:10 +00:00
|
|
|
/* Core SS7 Instance/Linkset/Link/AS/ASP Handling */
|
|
|
|
|
|
|
|
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
2017-11-12 16:25:47 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
|
|
*
|
2017-04-03 15:37:10 +00:00
|
|
|
* 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 <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
2018-11-09 14:44:40 +00:00
|
|
|
#include <inttypes.h>
|
2017-04-03 15:37:10 +00:00
|
|
|
#include <netdb.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/sctp.h>
|
|
|
|
|
|
|
|
#include <osmocom/sigtran/osmo_ss7.h>
|
|
|
|
#include <osmocom/sigtran/mtp_sap.h>
|
2017-04-05 09:14:24 +00:00
|
|
|
#include <osmocom/sigtran/protocol/mtp.h>
|
2017-04-03 15:37:10 +00:00
|
|
|
#include <osmocom/sigtran/protocol/sua.h>
|
|
|
|
#include <osmocom/sigtran/protocol/m3ua.h>
|
|
|
|
|
|
|
|
#include <osmocom/core/linuxlist.h>
|
|
|
|
#include <osmocom/core/select.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
|
|
|
#include <osmocom/core/talloc.h>
|
|
|
|
#include <osmocom/core/logging.h>
|
|
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/core/socket.h>
|
2020-08-27 14:24:59 +00:00
|
|
|
#include <osmocom/core/sockaddr_str.h>
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
#include <osmocom/netif/stream.h>
|
2017-04-02 09:58:17 +00:00
|
|
|
#include <osmocom/netif/ipa.h>
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-04-03 18:39:26 +00:00
|
|
|
#include "sccp_internal.h"
|
2017-04-03 15:37:10 +00:00
|
|
|
#include "xua_internal.h"
|
|
|
|
#include "xua_asp_fsm.h"
|
|
|
|
#include "xua_as_fsm.h"
|
|
|
|
|
|
|
|
#define MAX_PC_STR_LEN 32
|
|
|
|
|
|
|
|
static bool ss7_initialized = false;
|
|
|
|
|
2017-04-14 11:09:17 +00:00
|
|
|
LLIST_HEAD(osmo_ss7_instances);
|
2017-04-05 15:33:00 +00:00
|
|
|
static int32_t next_rctx = 1;
|
2017-04-10 20:34:20 +00:00
|
|
|
static int32_t next_l_rk_id = 1;
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2021-02-08 16:46:08 +00:00
|
|
|
const struct value_string mtp_unavail_cause_vals[] = {
|
|
|
|
{ MTP_UNAVAIL_C_UNKNOWN, "unknown" },
|
|
|
|
{ MTP_UNAVAIL_C_UNEQUIP_REM_USER, "unequipped-remote-user" },
|
|
|
|
{ MTP_UNAVAIL_C_INACC_REM_USER, "inaccessible-remote-user" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
struct value_string osmo_ss7_as_traffic_mode_vals[] = {
|
|
|
|
{ OSMO_SS7_AS_TMOD_BCAST, "broadcast" },
|
|
|
|
{ OSMO_SS7_AS_TMOD_LOADSHARE, "loadshare" },
|
|
|
|
{ OSMO_SS7_AS_TMOD_ROUNDROBIN, "round-robin" },
|
|
|
|
{ OSMO_SS7_AS_TMOD_OVERRIDE, "override" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct value_string osmo_ss7_asp_protocol_vals[] = {
|
|
|
|
{ OSMO_SS7_ASP_PROT_NONE, "none" },
|
|
|
|
{ OSMO_SS7_ASP_PROT_SUA, "sua" },
|
|
|
|
{ OSMO_SS7_ASP_PROT_M3UA, "m3ua" },
|
2017-04-02 09:58:17 +00:00
|
|
|
{ OSMO_SS7_ASP_PROT_IPA, "ipa" },
|
2017-04-03 15:37:10 +00:00
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2019-10-29 20:48:35 +00:00
|
|
|
const struct value_string osmo_ss7_asp_role_names[] = {
|
|
|
|
{ OSMO_SS7_ASP_ROLE_ASP, "ASP" },
|
|
|
|
{ OSMO_SS7_ASP_ROLE_SG, "SG" },
|
|
|
|
{ OSMO_SS7_ASP_ROLE_IPSP, "IPSP" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2017-04-02 09:58:17 +00:00
|
|
|
static int asp_proto_to_ip_proto(enum osmo_ss7_asp_protocol proto)
|
|
|
|
{
|
|
|
|
switch (proto) {
|
|
|
|
case OSMO_SS7_ASP_PROT_IPA:
|
|
|
|
return IPPROTO_TCP;
|
|
|
|
case OSMO_SS7_ASP_PROT_SUA:
|
|
|
|
case OSMO_SS7_ASP_PROT_M3UA:
|
|
|
|
default:
|
|
|
|
return IPPROTO_SCTP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-05 15:33:00 +00:00
|
|
|
int osmo_ss7_find_free_rctx(struct osmo_ss7_instance *inst)
|
|
|
|
{
|
|
|
|
int32_t rctx;
|
|
|
|
|
|
|
|
for (rctx = next_rctx; rctx; rctx = ++next_rctx) {
|
|
|
|
if (!osmo_ss7_as_find_by_rctx(inst, next_rctx))
|
|
|
|
return rctx;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-04-10 20:34:20 +00:00
|
|
|
static uint32_t find_free_l_rk_id(struct osmo_ss7_instance *inst)
|
|
|
|
{
|
|
|
|
uint32_t l_rk_id;
|
|
|
|
|
|
|
|
for (l_rk_id = next_l_rk_id; next_l_rk_id; l_rk_id = ++next_l_rk_id) {
|
|
|
|
if (!osmo_ss7_as_find_by_l_rk_id(inst, next_l_rk_id))
|
|
|
|
return l_rk_id;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* SS7 Point Code Parsing / Printing
|
|
|
|
***********************************************************************/
|
|
|
|
|
2017-04-09 09:51:58 +00:00
|
|
|
static const struct osmo_ss7_pc_fmt default_pc_fmt = {
|
|
|
|
.delimiter = '.',
|
|
|
|
.component_len = { 3, 8, 3},
|
|
|
|
};
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/* like strcat() but appends a single character */
|
|
|
|
static int strnappendchar(char *str, char c, size_t n)
|
|
|
|
{
|
|
|
|
unsigned int curlen = strlen(str);
|
|
|
|
|
|
|
|
if (n < curlen + 2)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
str[curlen] = c;
|
|
|
|
str[curlen+1] = '\0';
|
|
|
|
|
|
|
|
return curlen+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* generate a format string for formatting a point code. The result can
|
|
|
|
* e.g. be used with sscanf() or sprintf() */
|
2017-04-09 09:51:58 +00:00
|
|
|
static const char *gen_pc_fmtstr(const struct osmo_ss7_pc_fmt *pc_fmt,
|
2017-04-03 15:37:10 +00:00
|
|
|
unsigned int *num_comp_exp)
|
|
|
|
{
|
|
|
|
static char buf[MAX_PC_STR_LEN];
|
|
|
|
unsigned int num_comp = 0;
|
|
|
|
|
|
|
|
buf[0] = '\0';
|
|
|
|
strcat(buf, "%u");
|
|
|
|
num_comp++;
|
|
|
|
|
2017-04-09 09:51:58 +00:00
|
|
|
if (pc_fmt->component_len[1] == 0)
|
2017-04-03 15:37:10 +00:00
|
|
|
goto out;
|
2017-04-09 09:51:58 +00:00
|
|
|
strnappendchar(buf, pc_fmt->delimiter, sizeof(buf));
|
2017-04-03 15:37:10 +00:00
|
|
|
strcat(buf, "%u");
|
|
|
|
num_comp++;
|
|
|
|
|
2017-04-09 09:51:58 +00:00
|
|
|
if (pc_fmt->component_len[2] == 0)
|
2017-04-03 15:37:10 +00:00
|
|
|
goto out;
|
2017-04-09 09:51:58 +00:00
|
|
|
strnappendchar(buf, pc_fmt->delimiter, sizeof(buf));
|
2017-04-03 15:37:10 +00:00
|
|
|
strcat(buf, "%u");
|
|
|
|
num_comp++;
|
|
|
|
out:
|
|
|
|
if (num_comp_exp)
|
|
|
|
*num_comp_exp = num_comp;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get number of components we expect for a point code, depending on the
|
|
|
|
* configuration of this ss7_instance */
|
2017-04-09 09:51:58 +00:00
|
|
|
static unsigned int num_pc_comp_exp(const struct osmo_ss7_pc_fmt *pc_fmt)
|
2017-04-03 15:37:10 +00:00
|
|
|
{
|
|
|
|
unsigned int num_comp_exp = 1;
|
|
|
|
|
2017-04-09 09:51:58 +00:00
|
|
|
if (pc_fmt->component_len[1])
|
2017-04-03 15:37:10 +00:00
|
|
|
num_comp_exp++;
|
2017-04-09 09:51:58 +00:00
|
|
|
if (pc_fmt->component_len[2])
|
2017-04-03 15:37:10 +00:00
|
|
|
num_comp_exp++;
|
|
|
|
|
|
|
|
return num_comp_exp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the total width (in bits) of the point-codes in this ss7_instance */
|
2018-11-09 14:41:35 +00:00
|
|
|
uint8_t osmo_ss7_pc_width(const struct osmo_ss7_pc_fmt *pc_fmt)
|
2017-04-03 15:37:10 +00:00
|
|
|
{
|
2017-04-09 09:51:58 +00:00
|
|
|
return pc_fmt->component_len[0] + pc_fmt->component_len[1] + pc_fmt->component_len[2];
|
2017-04-03 15:37:10 +00:00
|
|
|
}
|
|
|
|
|
2021-04-29 17:30:58 +00:00
|
|
|
/* truncate pc or mask to maximum permitted length. This solves
|
|
|
|
* callers specifying arbitrary large masks which then evade duplicate
|
|
|
|
* detection with longer mask lengths */
|
|
|
|
uint32_t osmo_ss7_pc_normalize(const struct osmo_ss7_pc_fmt *pc_fmt, uint32_t pc)
|
|
|
|
{
|
|
|
|
uint32_t mask = (1 << osmo_ss7_pc_width(pc_fmt))-1;
|
|
|
|
return pc & mask;
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/* get the number of bits we must shift the given component of a point
|
|
|
|
* code in this ss7_instance */
|
2017-04-09 09:51:58 +00:00
|
|
|
static unsigned int get_pc_comp_shift(const struct osmo_ss7_pc_fmt *pc_fmt,
|
2017-04-03 15:37:10 +00:00
|
|
|
unsigned int comp_num)
|
|
|
|
{
|
2018-11-09 14:41:35 +00:00
|
|
|
uint32_t pc_width = osmo_ss7_pc_width(pc_fmt);
|
2017-04-03 15:37:10 +00:00
|
|
|
switch (comp_num) {
|
|
|
|
case 0:
|
2017-04-09 09:51:58 +00:00
|
|
|
return pc_width - pc_fmt->component_len[0];
|
2017-04-03 15:37:10 +00:00
|
|
|
case 1:
|
2017-04-09 09:51:58 +00:00
|
|
|
return pc_width - pc_fmt->component_len[0] - pc_fmt->component_len[1];
|
2017-04-03 15:37:10 +00:00
|
|
|
case 2:
|
|
|
|
return 0;
|
|
|
|
default:
|
2018-11-09 14:54:43 +00:00
|
|
|
/* Invalid number of components */
|
|
|
|
OSMO_ASSERT(false);
|
2017-04-03 15:37:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-09 09:51:58 +00:00
|
|
|
static uint32_t pc_comp_shift_and_mask(const struct osmo_ss7_pc_fmt *pc_fmt,
|
2017-04-03 15:37:10 +00:00
|
|
|
unsigned int comp_num, uint32_t pc)
|
|
|
|
{
|
2017-04-09 09:51:58 +00:00
|
|
|
unsigned int shift = get_pc_comp_shift(pc_fmt, comp_num);
|
|
|
|
uint32_t mask = (1 << pc_fmt->component_len[comp_num]) - 1;
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
return (pc >> shift) & mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse a point code according to the structure configured for this
|
|
|
|
* ss7_instance */
|
|
|
|
int osmo_ss7_pointcode_parse(struct osmo_ss7_instance *inst, const char *str)
|
|
|
|
{
|
|
|
|
unsigned int component[3];
|
2017-04-09 09:51:58 +00:00
|
|
|
const struct osmo_ss7_pc_fmt *pc_fmt = inst ? &inst->cfg.pc_fmt : &default_pc_fmt;
|
|
|
|
unsigned int num_comp_exp = num_pc_comp_exp(pc_fmt);
|
|
|
|
const char *fmtstr = gen_pc_fmtstr(pc_fmt, &num_comp_exp);
|
2017-04-03 15:37:10 +00:00
|
|
|
int i, rc;
|
|
|
|
|
|
|
|
rc = sscanf(str, fmtstr, &component[0], &component[1], &component[2]);
|
|
|
|
/* ensure all components were parsed */
|
|
|
|
if (rc != num_comp_exp)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* check none of the component values exceeds what can be
|
|
|
|
* represented within its bit-width */
|
|
|
|
for (i = 0; i < num_comp_exp; i++) {
|
2017-04-09 09:51:58 +00:00
|
|
|
if (component[i] >= (1 << pc_fmt->component_len[i]))
|
2017-04-03 15:37:10 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* shift them all together */
|
2017-04-09 09:51:58 +00:00
|
|
|
rc = (component[0] << get_pc_comp_shift(pc_fmt, 0));
|
2017-04-03 15:37:10 +00:00
|
|
|
if (num_comp_exp > 1)
|
2017-04-09 09:51:58 +00:00
|
|
|
rc |= (component[1] << get_pc_comp_shift(pc_fmt, 1));
|
2017-04-03 15:37:10 +00:00
|
|
|
if (num_comp_exp > 2)
|
2017-04-09 09:51:58 +00:00
|
|
|
rc |= (component[2] << get_pc_comp_shift(pc_fmt, 2));
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
err:
|
|
|
|
LOGSS7(inst, LOGL_NOTICE, "Error parsing Pointcode '%s'\n", str);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-02-07 11:48:51 +00:00
|
|
|
const char *osmo_ss7_pointcode_print_buf(char *buf, size_t len, const struct osmo_ss7_instance *inst, uint32_t pc)
|
2017-04-03 15:37:10 +00:00
|
|
|
{
|
2017-07-26 15:31:53 +00:00
|
|
|
const struct osmo_ss7_pc_fmt *pc_fmt;
|
|
|
|
unsigned int num_comp_exp;
|
|
|
|
const char *fmtstr;
|
|
|
|
|
|
|
|
if (!osmo_ss7_pc_is_valid(pc))
|
|
|
|
return "(no PC)";
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-07-26 15:31:53 +00:00
|
|
|
pc_fmt = inst ? &inst->cfg.pc_fmt : &default_pc_fmt;
|
|
|
|
num_comp_exp = num_pc_comp_exp(pc_fmt);
|
|
|
|
fmtstr = gen_pc_fmtstr(pc_fmt, &num_comp_exp);
|
2017-04-03 15:37:10 +00:00
|
|
|
OSMO_ASSERT(fmtstr);
|
2017-06-22 19:03:14 +00:00
|
|
|
snprintf(buf, len, fmtstr,
|
2017-04-09 09:51:58 +00:00
|
|
|
pc_comp_shift_and_mask(pc_fmt, 0, pc),
|
|
|
|
pc_comp_shift_and_mask(pc_fmt, 1, pc),
|
|
|
|
pc_comp_shift_and_mask(pc_fmt, 2, pc));
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2017-06-22 19:03:14 +00:00
|
|
|
|
|
|
|
/* print a pointcode according to the structure configured for this
|
|
|
|
* ss7_instance */
|
2017-07-27 11:50:31 +00:00
|
|
|
const char *osmo_ss7_pointcode_print(const struct osmo_ss7_instance *inst, uint32_t pc)
|
2017-06-22 19:03:14 +00:00
|
|
|
{
|
|
|
|
static char buf[MAX_PC_STR_LEN];
|
2021-02-07 11:48:51 +00:00
|
|
|
return osmo_ss7_pointcode_print_buf(buf, sizeof(buf), inst, pc);
|
2017-06-22 19:03:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* same as osmo_ss7_pointcode_print() but using a separate buffer, useful for multiple point codes in the
|
|
|
|
* same LOGP/printf. */
|
2017-07-27 11:50:31 +00:00
|
|
|
const char *osmo_ss7_pointcode_print2(const struct osmo_ss7_instance *inst, uint32_t pc)
|
2017-06-22 19:03:14 +00:00
|
|
|
{
|
|
|
|
static char buf[MAX_PC_STR_LEN];
|
2021-02-07 11:48:51 +00:00
|
|
|
return osmo_ss7_pointcode_print_buf(buf, sizeof(buf), inst, pc);
|
2017-06-22 19:03:14 +00:00
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
int osmo_ss7_pointcode_parse_mask_or_len(struct osmo_ss7_instance *inst, const char *in)
|
|
|
|
{
|
2018-11-09 14:41:35 +00:00
|
|
|
unsigned int width = osmo_ss7_pc_width(inst ? &inst->cfg.pc_fmt : &default_pc_fmt);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
if (in[0] == '/') {
|
|
|
|
/* parse mask by length */
|
|
|
|
int masklen = atoi(in+1);
|
|
|
|
if (masklen < 0 || masklen > 32)
|
|
|
|
return -EINVAL;
|
|
|
|
if (masklen == 0)
|
|
|
|
return 0;
|
|
|
|
return (0xFFFFFFFF << (width - masklen)) & ((1 << width)-1);
|
|
|
|
} else {
|
|
|
|
/* parse mask as point code */
|
|
|
|
return osmo_ss7_pointcode_parse(inst, in);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const uint16_t prot2port[] = {
|
|
|
|
[OSMO_SS7_ASP_PROT_NONE] = 0,
|
|
|
|
[OSMO_SS7_ASP_PROT_SUA] = SUA_PORT,
|
|
|
|
[OSMO_SS7_ASP_PROT_M3UA] = M3UA_PORT,
|
2018-05-26 09:35:08 +00:00
|
|
|
[OSMO_SS7_ASP_PROT_IPA] = 5000,
|
2017-04-03 15:37:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
int osmo_ss7_asp_protocol_port(enum osmo_ss7_asp_protocol prot)
|
|
|
|
{
|
|
|
|
if (prot >= ARRAY_SIZE(prot2port))
|
|
|
|
return -EINVAL;
|
|
|
|
else
|
|
|
|
return prot2port[prot];
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* SS7 Instance
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/*! \brief Find a SS7 Instance with given ID
|
|
|
|
* \param[in] id ID for which to search
|
|
|
|
* \returns \ref osmo_ss7_instance on success; NULL on error */
|
|
|
|
struct osmo_ss7_instance *
|
|
|
|
osmo_ss7_instance_find(uint32_t id)
|
|
|
|
{
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
|
|
|
|
struct osmo_ss7_instance *inst;
|
2017-04-14 11:09:17 +00:00
|
|
|
llist_for_each_entry(inst, &osmo_ss7_instances, list) {
|
2017-04-03 15:37:10 +00:00
|
|
|
if (inst->cfg.id == id)
|
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Find or create a SS7 Instance
|
|
|
|
* \param[in] ctx talloc allocation context to use for allocations
|
|
|
|
* \param[in] id ID of SS7 Instance
|
|
|
|
* \returns \ref osmo_ss7_instance on success; NULL on error */
|
|
|
|
struct osmo_ss7_instance *
|
|
|
|
osmo_ss7_instance_find_or_create(void *ctx, uint32_t id)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_instance *inst;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
|
|
|
|
inst = osmo_ss7_instance_find(id);
|
2017-04-14 14:34:42 +00:00
|
|
|
if (inst)
|
|
|
|
return inst;
|
|
|
|
|
|
|
|
inst = talloc_zero(ctx, struct osmo_ss7_instance);
|
2017-04-03 15:37:10 +00:00
|
|
|
if (!inst)
|
|
|
|
return NULL;
|
|
|
|
|
2017-07-26 15:31:53 +00:00
|
|
|
inst->cfg.primary_pc = OSMO_SS7_PC_INVALID;
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
inst->cfg.id = id;
|
|
|
|
LOGSS7(inst, LOGL_INFO, "Creating SS7 Instance\n");
|
|
|
|
|
|
|
|
INIT_LLIST_HEAD(&inst->linksets);
|
|
|
|
INIT_LLIST_HEAD(&inst->as_list);
|
|
|
|
INIT_LLIST_HEAD(&inst->asp_list);
|
|
|
|
INIT_LLIST_HEAD(&inst->rtable_list);
|
2017-04-14 20:16:53 +00:00
|
|
|
INIT_LLIST_HEAD(&inst->xua_servers);
|
2017-04-03 15:37:10 +00:00
|
|
|
inst->rtable_system = osmo_ss7_route_table_find_or_create(inst, "system");
|
|
|
|
|
|
|
|
/* default point code structure + formatting */
|
|
|
|
inst->cfg.pc_fmt.delimiter = '.';
|
|
|
|
inst->cfg.pc_fmt.component_len[0] = 3;
|
|
|
|
inst->cfg.pc_fmt.component_len[1] = 8;
|
|
|
|
inst->cfg.pc_fmt.component_len[2] = 3;
|
|
|
|
|
2017-04-14 11:09:17 +00:00
|
|
|
llist_add(&inst->list, &osmo_ss7_instances);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-06-14 16:08:59 +00:00
|
|
|
INIT_LLIST_HEAD(&inst->cfg.sccp_address_book);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Destroy a SS7 Instance
|
|
|
|
* \param[in] inst SS7 Instance to be destroyed */
|
|
|
|
void osmo_ss7_instance_destroy(struct osmo_ss7_instance *inst)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_linkset *lset;
|
|
|
|
struct osmo_ss7_as *as;
|
|
|
|
struct osmo_ss7_asp *asp;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
LOGSS7(inst, LOGL_INFO, "Destroying SS7 Instance\n");
|
|
|
|
|
|
|
|
llist_for_each_entry(asp, &inst->asp_list, list)
|
|
|
|
osmo_ss7_asp_destroy(asp);
|
|
|
|
|
|
|
|
llist_for_each_entry(as, &inst->as_list, list)
|
|
|
|
osmo_ss7_as_destroy(as);
|
|
|
|
|
|
|
|
llist_for_each_entry(lset, &inst->linksets, list)
|
|
|
|
osmo_ss7_linkset_destroy(lset);
|
|
|
|
|
|
|
|
llist_del(&inst->list);
|
|
|
|
talloc_free(inst);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Set the point code format used in given SS7 instance */
|
|
|
|
int osmo_ss7_instance_set_pc_fmt(struct osmo_ss7_instance *inst,
|
|
|
|
uint8_t c0, uint8_t c1, uint8_t c2)
|
|
|
|
{
|
|
|
|
if (c0+c1+c2 > 32)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (c0+c1+c2 > 14)
|
|
|
|
LOGSS7(inst, LOGL_NOTICE, "Point Code Format %u-%u-%u "
|
|
|
|
"is longer than 14 bits, odd?\n", c0, c1, c2);
|
|
|
|
|
|
|
|
inst->cfg.pc_fmt.component_len[0] = c0;
|
|
|
|
inst->cfg.pc_fmt.component_len[1] = c1;
|
|
|
|
inst->cfg.pc_fmt.component_len[2] = c2;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-26 23:21:45 +00:00
|
|
|
/*! Allocate an SCCP instance, if not present yet.
|
|
|
|
* \returns inst->sccp. */
|
|
|
|
struct osmo_sccp_instance *osmo_ss7_ensure_sccp(struct osmo_ss7_instance *inst)
|
|
|
|
{
|
|
|
|
if (inst->sccp)
|
|
|
|
return inst->sccp;
|
|
|
|
|
|
|
|
LOGSS7(inst, LOGL_NOTICE, "Creating SCCP instance\n");
|
|
|
|
inst->sccp = osmo_sccp_instance_create(inst, NULL);
|
|
|
|
return inst->sccp;
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* MTP Users (Users of MTP, such as SCCP or ISUP)
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/*! \brief Register a MTP user for a given service indicator
|
|
|
|
* \param[in] inst SS7 instance for which we register the user
|
|
|
|
* \param[in] service_ind Service (ISUP, SCCP, ...)
|
|
|
|
* \param[in] user SS7 user (including primitive call-back)
|
|
|
|
* \returns 0 on success; negative on error */
|
|
|
|
int osmo_ss7_user_register(struct osmo_ss7_instance *inst, uint8_t service_ind,
|
|
|
|
struct osmo_ss7_user *user)
|
|
|
|
{
|
|
|
|
if (service_ind >= ARRAY_SIZE(inst->user))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (inst->user[service_ind])
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
DEBUGP(DLSS7, "registering user=%s for SI %u with priv %p\n",
|
|
|
|
user->name, service_ind, user->priv);
|
|
|
|
|
|
|
|
user->inst = inst;
|
|
|
|
inst->user[service_ind] = user;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Unregister a MTP user for a given service indicator
|
|
|
|
* \param[in] inst SS7 instance for which we register the user
|
|
|
|
* \param[in] service_ind Service (ISUP, SCCP, ...)
|
|
|
|
* \param[in] user (optional) SS7 user. If present, we will not
|
2019-10-11 19:17:40 +00:00
|
|
|
* unregister other users
|
2017-04-03 15:37:10 +00:00
|
|
|
* \returns 0 on success; negative on error */
|
|
|
|
int osmo_ss7_user_unregister(struct osmo_ss7_instance *inst, uint8_t service_ind,
|
|
|
|
struct osmo_ss7_user *user)
|
|
|
|
{
|
|
|
|
if (service_ind >= ARRAY_SIZE(inst->user))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!inst->user[service_ind])
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (user && (inst->user[service_ind] != user))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-04-27 10:24:38 +00:00
|
|
|
if (user)
|
|
|
|
user->inst = NULL;
|
2017-04-03 15:37:10 +00:00
|
|
|
inst->user[service_ind] = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* deliver to a local MTP user */
|
|
|
|
int osmo_ss7_mtp_to_user(struct osmo_ss7_instance *inst, struct osmo_mtp_prim *omp)
|
|
|
|
{
|
|
|
|
uint32_t service_ind;
|
|
|
|
const struct osmo_ss7_user *osu;
|
|
|
|
|
|
|
|
if (omp->oph.sap != MTP_SAP_USER ||
|
|
|
|
omp->oph.primitive != OSMO_MTP_PRIM_TRANSFER ||
|
|
|
|
omp->oph.operation != PRIM_OP_INDICATION) {
|
|
|
|
LOGP(DLSS7, LOGL_ERROR, "Unsupported Primitive\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
service_ind = omp->u.transfer.sio & 0xF;
|
|
|
|
osu = inst->user[service_ind];
|
|
|
|
|
|
|
|
if (!osu) {
|
|
|
|
LOGP(DLSS7, LOGL_NOTICE, "No MTP-User for SI %u\n", service_ind);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUGP(DLSS7, "delivering MTP-TRANSFER.ind to user %s, priv=%p\n",
|
|
|
|
osu->name, osu->priv);
|
|
|
|
return osu->prim_cb(&omp->oph, (void *) osu->priv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* SS7 Linkset
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/*! \brief Destroy a SS7 Linkset
|
|
|
|
* \param[in] lset Linkset to be destroyed */
|
|
|
|
void osmo_ss7_linkset_destroy(struct osmo_ss7_linkset *lset)
|
|
|
|
{
|
2017-04-11 18:42:31 +00:00
|
|
|
struct osmo_ss7_route *rt, *rt2;
|
2017-04-03 15:37:10 +00:00
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
LOGSS7(lset->inst, LOGL_INFO, "Destroying Linkset %s\n",
|
|
|
|
lset->cfg.name);
|
|
|
|
|
2017-04-11 18:42:31 +00:00
|
|
|
/* find any routes pointing to this AS and remove them */
|
|
|
|
llist_for_each_entry_safe(rt, rt2, &lset->inst->rtable_system->routes, list) {
|
|
|
|
if (rt->dest.linkset == lset)
|
|
|
|
osmo_ss7_route_destroy(rt);
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(lset->links); i++) {
|
|
|
|
struct osmo_ss7_link *link = lset->links[i];
|
|
|
|
if (!link)
|
|
|
|
continue;
|
|
|
|
osmo_ss7_link_destroy(link);
|
|
|
|
}
|
|
|
|
llist_del(&lset->list);
|
|
|
|
talloc_free(lset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Find SS7 Linkset by given name
|
|
|
|
* \param[in] inst SS7 Instance in which to look
|
|
|
|
* \param[in] name Name of SS7 Linkset
|
|
|
|
* \returns pointer to linkset on success; NULL on error */
|
|
|
|
struct osmo_ss7_linkset *
|
|
|
|
osmo_ss7_linkset_find_by_name(struct osmo_ss7_instance *inst, const char *name)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_linkset *lset;
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
llist_for_each_entry(lset, &inst->linksets, list) {
|
|
|
|
if (!strcmp(name, lset->cfg.name))
|
|
|
|
return lset;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Find or allocate SS7 Linkset
|
|
|
|
* \param[in] inst SS7 Instance in which we operate
|
|
|
|
* \param[in] name Name of SS7 Linkset
|
|
|
|
* \param[in] pc Adjacent Pointcode
|
|
|
|
* \returns pointer to Linkset on success; NULL on error */
|
|
|
|
struct osmo_ss7_linkset *
|
|
|
|
osmo_ss7_linkset_find_or_create(struct osmo_ss7_instance *inst, const char *name, uint32_t pc)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_linkset *lset;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
lset = osmo_ss7_linkset_find_by_name(inst, name);
|
|
|
|
if (lset && lset->cfg.adjacent_pc != pc)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!lset) {
|
|
|
|
LOGSS7(inst, LOGL_INFO, "Creating Linkset %s\n", name);
|
|
|
|
lset = talloc_zero(inst, struct osmo_ss7_linkset);
|
|
|
|
lset->inst = inst;
|
|
|
|
lset->cfg.adjacent_pc = pc;
|
|
|
|
lset->cfg.name = talloc_strdup(lset, name);
|
|
|
|
llist_add_tail(&lset->list, &inst->linksets);
|
|
|
|
}
|
|
|
|
|
|
|
|
return lset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* SS7 Link
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/*! \brief Destryo SS7 Link
|
|
|
|
* \param[in] link SS7 Link to be destroyed */
|
|
|
|
void osmo_ss7_link_destroy(struct osmo_ss7_link *link)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_linkset *lset = link->linkset;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
LOGSS7(lset->inst, LOGL_INFO, "Destroying Link %s:%u\n",
|
|
|
|
lset->cfg.name, link->cfg.id);
|
|
|
|
/* FIXME: do cleanup */
|
|
|
|
lset->links[link->cfg.id] = NULL;
|
|
|
|
talloc_free(link);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Find or create SS7 Link with given ID in given Linkset
|
|
|
|
* \param[in] lset SS7 Linkset on which we operate
|
|
|
|
* \param[in] id Link number within Linkset
|
|
|
|
* \returns pointer to SS7 Link on success; NULL on error */
|
|
|
|
struct osmo_ss7_link *
|
|
|
|
osmo_ss7_link_find_or_create(struct osmo_ss7_linkset *lset, uint32_t id)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_link *link;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
if (id >= ARRAY_SIZE(lset->links))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (lset->links[id]) {
|
|
|
|
link = lset->links[id];
|
|
|
|
} else {
|
|
|
|
LOGSS7(lset->inst, LOGL_INFO, "Creating Link %s:%u\n",
|
|
|
|
lset->cfg.name, id);
|
|
|
|
link = talloc_zero(lset, struct osmo_ss7_link);
|
|
|
|
if (!link)
|
|
|
|
return NULL;
|
|
|
|
link->linkset = lset;
|
|
|
|
lset->links[id] = link;
|
|
|
|
link->cfg.id = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return link;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* SS7 Route Tables
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
struct osmo_ss7_route_table *
|
|
|
|
osmo_ss7_route_table_find(struct osmo_ss7_instance *inst, const char *name)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_route_table *rtbl;
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
llist_for_each_entry(rtbl, &inst->rtable_list, list) {
|
|
|
|
if (!strcmp(rtbl->cfg.name, name))
|
|
|
|
return rtbl;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct osmo_ss7_route_table *
|
|
|
|
osmo_ss7_route_table_find_or_create(struct osmo_ss7_instance *inst, const char *name)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_route_table *rtbl;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
rtbl = osmo_ss7_route_table_find(inst, name);
|
|
|
|
if (!rtbl) {
|
|
|
|
LOGSS7(inst, LOGL_INFO, "Creating Route Table %s\n", name);
|
|
|
|
rtbl = talloc_zero(inst, struct osmo_ss7_route_table);
|
|
|
|
rtbl->inst = inst;
|
|
|
|
rtbl->cfg.name = talloc_strdup(rtbl, name);
|
|
|
|
INIT_LLIST_HEAD(&rtbl->routes);
|
|
|
|
llist_add_tail(&rtbl->list, &inst->rtable_list);
|
|
|
|
}
|
|
|
|
return rtbl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void osmo_ss7_route_table_destroy(struct osmo_ss7_route_table *rtbl)
|
|
|
|
{
|
|
|
|
llist_del(&rtbl->list);
|
|
|
|
/* routes are allocated as children of route table, will be
|
|
|
|
* automatically freed() */
|
|
|
|
talloc_free(rtbl);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* SS7 Routes
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/*! \brief Find a SS7 route for given destination point code in given table */
|
|
|
|
struct osmo_ss7_route *
|
|
|
|
osmo_ss7_route_find_dpc(struct osmo_ss7_route_table *rtbl, uint32_t dpc)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_route *rt;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
2021-04-29 17:30:58 +00:00
|
|
|
|
|
|
|
dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/* we assume the routes are sorted by mask length, i.e. more
|
|
|
|
* specific routes first, and less specific routes with shorter
|
|
|
|
* mask later */
|
|
|
|
llist_for_each_entry(rt, &rtbl->routes, list) {
|
|
|
|
if ((dpc & rt->cfg.mask) == rt->cfg.pc)
|
|
|
|
return rt;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Find a SS7 route for given destination point code + mask in given table */
|
|
|
|
struct osmo_ss7_route *
|
|
|
|
osmo_ss7_route_find_dpc_mask(struct osmo_ss7_route_table *rtbl, uint32_t dpc,
|
|
|
|
uint32_t mask)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_route *rt;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
2021-04-29 17:30:58 +00:00
|
|
|
mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask);
|
|
|
|
dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/* we assume the routes are sorted by mask length, i.e. more
|
|
|
|
* specific routes first, and less specific routes with shorter
|
|
|
|
* mask later */
|
|
|
|
llist_for_each_entry(rt, &rtbl->routes, list) {
|
|
|
|
if (dpc == rt->cfg.pc && mask == rt->cfg.mask)
|
|
|
|
return rt;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Find a SS7 route for given destination point code in given SS7 */
|
|
|
|
struct osmo_ss7_route *
|
|
|
|
osmo_ss7_route_lookup(struct osmo_ss7_instance *inst, uint32_t dpc)
|
|
|
|
{
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
return osmo_ss7_route_find_dpc(inst->rtable_system, dpc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* insert the route in the ordered list of routes. The list is sorted by
|
|
|
|
* mask length, so that the more specific (longer mask) routes are
|
|
|
|
* first, while the less specific routes with shorter masks are last.
|
|
|
|
* Hence, the first matching route in a linear iteration is the most
|
|
|
|
* specific match. */
|
|
|
|
static void route_insert_sorted(struct osmo_ss7_route_table *rtbl,
|
|
|
|
struct osmo_ss7_route *cmp)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_route *rt;
|
|
|
|
|
|
|
|
llist_for_each_entry(rt, &rtbl->routes, list) {
|
|
|
|
if (rt->cfg.mask < cmp->cfg.mask) {
|
|
|
|
/* insert before the current entry */
|
|
|
|
llist_add(&cmp->list, rt->list.prev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* not added, i.e. no smaller mask length found: we are the
|
|
|
|
* smallest mask and thus should go last */
|
|
|
|
llist_add_tail(&cmp->list, &rtbl->routes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Create a new route in the given routing table
|
|
|
|
* \param[in] rtbl Routing Table in which the route is to be created
|
|
|
|
* \param[in] pc Point Code of the destination of the route
|
|
|
|
* \param[in] mask Mask of the destination Point Code \ref pc
|
|
|
|
* \param[in] linkset_name string name of the linkset to be used
|
|
|
|
* \returns caller-allocated + initialized route, NULL on error
|
|
|
|
*/
|
|
|
|
struct osmo_ss7_route *
|
|
|
|
osmo_ss7_route_create(struct osmo_ss7_route_table *rtbl, uint32_t pc,
|
|
|
|
uint32_t mask, const char *linkset_name)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_route *rt;
|
|
|
|
struct osmo_ss7_linkset *lset;
|
2017-06-22 09:00:23 +00:00
|
|
|
struct osmo_ss7_as *as = NULL;
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2021-04-29 17:30:58 +00:00
|
|
|
/* truncate mask to maximum. Let's avoid callers specifying arbitrary large
|
|
|
|
* masks to ensure we don't fail duplicate detection with longer mask lengths */
|
|
|
|
mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask);
|
|
|
|
pc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, pc);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
lset = osmo_ss7_linkset_find_by_name(rtbl->inst, linkset_name);
|
|
|
|
if (!lset) {
|
|
|
|
as = osmo_ss7_as_find_by_name(rtbl->inst, linkset_name);
|
|
|
|
if (!as)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-04-25 13:08:24 +00:00
|
|
|
/* check for duplicates */
|
|
|
|
rt = osmo_ss7_route_find_dpc_mask(rtbl, pc, mask);
|
|
|
|
if (rt && !strcmp(rt->cfg.linkset_name, linkset_name)) {
|
|
|
|
LOGSS7(rtbl->inst, LOGL_ERROR, "Refusing to create duplicate route: "
|
|
|
|
"pc=%u=%s mask=0x%x via linkset/AS '%s'\n",
|
|
|
|
pc, osmo_ss7_pointcode_print(rtbl->inst, pc), mask, linkset_name);
|
|
|
|
return rt;
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
rt = talloc_zero(rtbl, struct osmo_ss7_route);
|
|
|
|
if (!rt)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
rt->cfg.pc = pc;
|
|
|
|
rt->cfg.mask = mask;
|
|
|
|
rt->cfg.linkset_name = talloc_strdup(rt, linkset_name);
|
2017-06-22 19:03:14 +00:00
|
|
|
if (lset) {
|
2017-04-03 15:37:10 +00:00
|
|
|
rt->dest.linkset = lset;
|
2017-06-22 19:03:14 +00:00
|
|
|
LOGSS7(rtbl->inst, LOGL_INFO, "Creating route: pc=%u=%s mask=0x%x via linkset '%s'\n",
|
|
|
|
pc, osmo_ss7_pointcode_print(rtbl->inst, pc), mask, lset->cfg.name);
|
|
|
|
} else {
|
2017-04-03 15:37:10 +00:00
|
|
|
rt->dest.as = as;
|
2017-06-22 19:03:14 +00:00
|
|
|
LOGSS7(rtbl->inst, LOGL_INFO, "Creating route: pc=%u=%s mask=0x%x via AS '%s'\n",
|
|
|
|
pc, osmo_ss7_pointcode_print(rtbl->inst, pc), mask, as->cfg.name);
|
|
|
|
}
|
2017-04-03 15:37:10 +00:00
|
|
|
rt->rtable = rtbl;
|
|
|
|
|
|
|
|
route_insert_sorted(rtbl, rt);
|
|
|
|
|
|
|
|
return rt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Destroy a given SS7 route */
|
|
|
|
void osmo_ss7_route_destroy(struct osmo_ss7_route *rt)
|
|
|
|
{
|
2021-04-25 16:44:35 +00:00
|
|
|
struct osmo_ss7_route_table *rtbl = rt->rtable;
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
OSMO_ASSERT(ss7_initialized);
|
2021-04-25 16:44:35 +00:00
|
|
|
|
|
|
|
LOGSS7(rtbl->inst, LOGL_INFO, "Destroying route: pc=%u=%s mask=0x%x via linkset/ASP '%s'\n",
|
|
|
|
rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc), rt->cfg.mask, rt->cfg.linkset_name);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
llist_del(&rt->list);
|
|
|
|
talloc_free(rt);
|
|
|
|
}
|
|
|
|
|
2019-03-12 23:33:06 +00:00
|
|
|
/* count number of consecutive leading (MSB) bits that are '1' */
|
|
|
|
static unsigned int count_leading_one_bits(uint32_t inp, unsigned int nbits)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < nbits; i++) {
|
|
|
|
if (!(inp & (1 << (nbits-1-i))))
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* determine the mask length in number of bits; negative if non-consecutive mask */
|
|
|
|
static int u32_masklen(uint32_t mask, unsigned int nbits)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
unsigned int leading_one_bits = count_leading_one_bits(mask, nbits);
|
|
|
|
|
|
|
|
/* are there any bits set after the initial bits? */
|
|
|
|
for (i = leading_one_bits; i < nbits; i++) {
|
|
|
|
if (mask & (1 << (nbits-1-i)))
|
|
|
|
return -1; /* not a simple prefix mask */
|
|
|
|
}
|
|
|
|
return leading_one_bits;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *osmo_ss7_route_print(const struct osmo_ss7_route *rt)
|
|
|
|
{
|
|
|
|
const struct osmo_ss7_instance *inst = rt->rtable->inst;
|
|
|
|
unsigned int pc_width = osmo_ss7_pc_width(&inst->cfg.pc_fmt);
|
|
|
|
static char buf[64];
|
|
|
|
int rc = u32_masklen(rt->cfg.mask, pc_width);
|
|
|
|
|
|
|
|
if (rc < 0)
|
|
|
|
snprintf(buf, sizeof(buf), "%s/%s", osmo_ss7_pointcode_print(inst, rt->cfg.pc),
|
|
|
|
osmo_ss7_pointcode_print2(inst, rt->cfg.mask));
|
|
|
|
else
|
|
|
|
snprintf(buf, sizeof(buf), "%s/%u", osmo_ss7_pointcode_print(inst, rt->cfg.pc), rc);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* SS7 Application Server
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/*! \brief Find Application Server by given name
|
|
|
|
* \param[in] inst SS7 Instance on which we operate
|
|
|
|
* \param[in] name Name of AS
|
|
|
|
* \returns pointer to Application Server on success; NULL otherwise */
|
|
|
|
struct osmo_ss7_as *
|
|
|
|
osmo_ss7_as_find_by_name(struct osmo_ss7_instance *inst, const char *name)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_as *as;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
llist_for_each_entry(as, &inst->as_list, list) {
|
|
|
|
if (!strcmp(name, as->cfg.name))
|
|
|
|
return as;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Find Application Server by given routing context
|
|
|
|
* \param[in] inst SS7 Instance on which we operate
|
|
|
|
* \param[in] rctx Routing Context
|
|
|
|
* \returns pointer to Application Server on success; NULL otherwise */
|
|
|
|
struct osmo_ss7_as *
|
|
|
|
osmo_ss7_as_find_by_rctx(struct osmo_ss7_instance *inst, uint32_t rctx)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_as *as;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
llist_for_each_entry(as, &inst->as_list, list) {
|
|
|
|
if (as->cfg.routing_key.context == rctx)
|
|
|
|
return as;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-04-10 20:34:20 +00:00
|
|
|
/*! \brief Find Application Server by given local routing key ID
|
|
|
|
* \param[in] inst SS7 Instance on which we operate
|
|
|
|
* \param[in] l_rk_id Local Routing Key ID
|
|
|
|
* \returns pointer to Application Server on success; NULL otherwise */
|
|
|
|
struct osmo_ss7_as *
|
|
|
|
osmo_ss7_as_find_by_l_rk_id(struct osmo_ss7_instance *inst, uint32_t l_rk_id)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_as *as;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
llist_for_each_entry(as, &inst->as_list, list) {
|
|
|
|
if (as->cfg.routing_key.l_rk_id == l_rk_id)
|
|
|
|
return as;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
sccp: make simple client configurable via VTY
The osmo_sccp_simple_client_on_ss7_id and osmo_sccp_simple_client
are not entirely configurable via VTY commands. The relation to
the VTY is implicit. The user may set up instance objects via
VTY (cs7/ss7, AS, ASP), which are then automatically created on
startup.
Each cs7 instance gets its own ID via the VTY configuration. When
osmo_sccp_simple_client_on_ss7_id() is called with the cs7 instance
id. (for osmo_sccp_simple_client() the ID will be hardcoded to 1),
the function automatically checks if the CS7 instance is present,
if not it will create one automatically using the caller supplied
parameters as a defult. If a CS7 instance is present, the function
checks for the presence of an AS and an ASP. These objects are
present, they will be used. If not, new objects will be created.
Both functions must not be called if an SCCP instance is already
present. Since there can only be one SCCP instance per CS7 instance,
this is an error condition.
Add additional logic that checks to detect an already existing, valid
configuration. If no or an insufficient configuration is detected,
use the caller supplied parameters as default configuration.
Change-Id: I293f3526ce6182dca74a169a23449dbc7af57c7c
2017-07-19 16:41:09 +00:00
|
|
|
/*! \brief Find Application Server (AS) by given protocol.
|
|
|
|
* \param[in] inst SS7 Instance on which we operate
|
|
|
|
* \param[in] proto Protocol identifier that must match
|
|
|
|
* \returns pointer to AS on success; NULL otherwise
|
|
|
|
* If an AS has an ASP also matching the given protocol, that AS is preferred.
|
|
|
|
* If there are multiple matches, return the first matching AS. */
|
|
|
|
struct osmo_ss7_as *osmo_ss7_as_find_by_proto(struct osmo_ss7_instance *inst,
|
|
|
|
enum osmo_ss7_asp_protocol proto)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_as *as;
|
|
|
|
struct osmo_ss7_as *as_without_asp = NULL;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
|
|
|
|
/* Loop through the list with AS and try to find one where the proto
|
|
|
|
matches up */
|
|
|
|
llist_for_each_entry(as, &inst->as_list, list) {
|
|
|
|
if (as->cfg.proto == proto) {
|
|
|
|
|
|
|
|
/* Put down the first AS that matches the proto, just in
|
|
|
|
* case we will not find any matching ASP */
|
|
|
|
if (!as_without_asp)
|
|
|
|
as_without_asp = as;
|
|
|
|
|
|
|
|
/* Check if the candicate we have here has any suitable
|
|
|
|
* ASP */
|
|
|
|
if (osmo_ss7_asp_find_by_proto(as, proto))
|
|
|
|
return as;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return with the second best find, if there is any */
|
|
|
|
return as_without_asp;
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/*! \brief Find or Create Application Server
|
|
|
|
* \param[in] inst SS7 Instance on which we operate
|
|
|
|
* \param[in] name Name of Application Server
|
|
|
|
* \param[in] proto Protocol of Application Server
|
|
|
|
* \returns pointer to Application Server on suuccess; NULL otherwise */
|
|
|
|
struct osmo_ss7_as *
|
|
|
|
osmo_ss7_as_find_or_create(struct osmo_ss7_instance *inst, const char *name,
|
|
|
|
enum osmo_ss7_asp_protocol proto)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_as *as;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
as = osmo_ss7_as_find_by_name(inst, name);
|
|
|
|
|
|
|
|
if (as && as->cfg.proto != proto)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!as) {
|
|
|
|
as = talloc_zero(inst, struct osmo_ss7_as);
|
|
|
|
if (!as)
|
|
|
|
return NULL;
|
|
|
|
as->inst = inst;
|
|
|
|
as->cfg.name = talloc_strdup(as, name);
|
|
|
|
as->cfg.proto = proto;
|
2019-10-23 14:36:41 +00:00
|
|
|
as->cfg.mode = OSMO_SS7_AS_TMOD_OVERRIDE;
|
2017-04-03 15:37:10 +00:00
|
|
|
as->cfg.recovery_timeout_msec = 2000;
|
2017-04-10 20:34:20 +00:00
|
|
|
as->cfg.routing_key.l_rk_id = find_free_l_rk_id(inst);
|
2017-04-03 15:37:10 +00:00
|
|
|
as->fi = xua_as_fsm_start(as, LOGL_DEBUG);
|
|
|
|
llist_add_tail(&as->list, &inst->as_list);
|
2019-10-19 10:23:21 +00:00
|
|
|
LOGPAS(as, DLSS7, LOGL_INFO, "Created AS\n");
|
2017-04-03 15:37:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return as;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Add given ASP to given AS
|
|
|
|
* \param[in] as Application Server to which \ref asp is added
|
|
|
|
* \param[in] asp Application Server Process to be added to \ref as
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int osmo_ss7_as_add_asp(struct osmo_ss7_as *as, const char *asp_name)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_asp *asp;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
asp = osmo_ss7_asp_find_by_name(as->inst, asp_name);
|
|
|
|
if (!asp)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2019-10-19 10:23:21 +00:00
|
|
|
LOGPAS(as, DLSS7, LOGL_INFO, "Adding ASP %s to AS\n", asp->cfg.name);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
if (osmo_ss7_as_has_asp(as, asp))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
|
|
|
if (!as->cfg.asps[i]) {
|
|
|
|
as->cfg.asps[i] = asp;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Delete given ASP from given AS
|
|
|
|
* \param[in] as Application Server from which \ref asp is deleted
|
|
|
|
* \param[in] asp Application Server Process to delete from \ref as
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int osmo_ss7_as_del_asp(struct osmo_ss7_as *as, const char *asp_name)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_asp *asp;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
asp = osmo_ss7_asp_find_by_name(as->inst, asp_name);
|
|
|
|
if (!asp)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2019-10-19 10:23:21 +00:00
|
|
|
LOGPAS(as, DLSS7, LOGL_INFO, "Removing ASP %s from AS\n", asp->cfg.name);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
|
|
|
if (as->cfg.asps[i] == asp) {
|
|
|
|
as->cfg.asps[i] = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Destroy given Application Server
|
|
|
|
* \param[in] as Application Server to destroy */
|
|
|
|
void osmo_ss7_as_destroy(struct osmo_ss7_as *as)
|
|
|
|
{
|
2017-04-11 18:42:31 +00:00
|
|
|
struct osmo_ss7_route *rt, *rt2;
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
OSMO_ASSERT(ss7_initialized);
|
2019-10-19 10:23:21 +00:00
|
|
|
LOGPAS(as, DLSS7, LOGL_INFO, "Destroying AS\n");
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
if (as->fi)
|
|
|
|
osmo_fsm_inst_term(as->fi, OSMO_FSM_TERM_REQUEST, NULL);
|
|
|
|
|
2017-04-11 18:42:31 +00:00
|
|
|
/* find any routes pointing to this AS and remove them */
|
|
|
|
llist_for_each_entry_safe(rt, rt2, &as->inst->rtable_system->routes, list) {
|
|
|
|
if (rt->dest.as == as)
|
|
|
|
osmo_ss7_route_destroy(rt);
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
as->inst = NULL;
|
|
|
|
llist_del(&as->list);
|
|
|
|
talloc_free(as);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Determine if given AS contains ASP
|
|
|
|
* \param[in] as Application Server in which to look for \ref asp
|
|
|
|
* \param[in] asp Application Server Process to look for in \ref as
|
|
|
|
* \returns true in case \ref asp is part of \ref as; false otherwise */
|
2021-02-07 11:31:34 +00:00
|
|
|
bool osmo_ss7_as_has_asp(const struct osmo_ss7_as *as,
|
|
|
|
const struct osmo_ss7_asp *asp)
|
2017-04-03 15:37:10 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
|
|
|
if (as->cfg.asps[i] == asp)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-11-06 19:52:29 +00:00
|
|
|
/*! Determine if given AS is in the active state.
|
|
|
|
* \param[in] as Application Server.
|
|
|
|
* \returns true in case as is active; false otherwise. */
|
|
|
|
bool osmo_ss7_as_active(const struct osmo_ss7_as *as)
|
|
|
|
{
|
|
|
|
if (!as->fi)
|
|
|
|
return false;
|
|
|
|
return as->fi->state == XUA_AS_S_ACTIVE;
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* SS7 Application Server Process
|
|
|
|
***********************************************************************/
|
|
|
|
|
2019-10-11 15:58:07 +00:00
|
|
|
int osmo_ss7_asp_peer_snprintf(char* buf, size_t buf_len, struct osmo_ss7_asp_peer *peer)
|
|
|
|
{
|
|
|
|
int len = 0, offset = 0, rem = buf_len;
|
|
|
|
int ret, i;
|
|
|
|
char *after;
|
|
|
|
|
|
|
|
if (buf_len < 3)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-10-23 16:09:04 +00:00
|
|
|
if (peer->host_cnt > 1) {
|
2019-10-11 15:58:07 +00:00
|
|
|
ret = snprintf(buf, rem, "(");
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
|
|
|
}
|
|
|
|
for (i = 0; i < peer->host_cnt; i++) {
|
|
|
|
if (peer->host_cnt == 1)
|
|
|
|
after = "";
|
|
|
|
else
|
|
|
|
after = (i == (peer->host_cnt - 1)) ? ")" : "|";
|
|
|
|
ret = snprintf(buf + offset, rem, "%s%s", peer->host[i] ? : "0.0.0.0", after);
|
|
|
|
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
|
|
|
}
|
|
|
|
ret = snprintf(buf + offset, rem, ":%u", peer->port);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2020-01-10 13:14:41 +00:00
|
|
|
/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed.
|
|
|
|
* \param[in] peer Application Server Process peer whose addresses are to be set.
|
|
|
|
* \param[in] talloc_ctx talloc context used to allocate new addresses.
|
|
|
|
* \param[in] hosts Array of strings containing IP addresses.
|
|
|
|
* \param[in] host_cnt Number of strings in hosts
|
|
|
|
* \returns 0 on success; negtive otherwise */
|
|
|
|
int osmo_ss7_asp_peer_set_hosts(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char* const* hosts, size_t host_cnt)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (host_cnt > ARRAY_SIZE(peer->host))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (; i < host_cnt; i++)
|
|
|
|
osmo_talloc_replace_string(talloc_ctx, &peer->host[i], hosts[i]);
|
|
|
|
for (; i < peer->host_cnt; i++) {
|
|
|
|
talloc_free(peer->host[i]);
|
|
|
|
peer->host[i] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
peer->host_cnt = host_cnt;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-27 14:24:59 +00:00
|
|
|
/* Is string formatted IPv4/v6 addr considered IN(6)ADDR_ANY? */
|
|
|
|
static inline bool host_is_ip_anyaddr(const char *host, bool is_v6)
|
|
|
|
{
|
|
|
|
/* NULL addr is resolved as 0.0.0.0 (IPv4) by getaddrinfo(), most
|
|
|
|
* probably for backward-compatibility reasons.
|
|
|
|
*/
|
|
|
|
return is_v6 ? (host && !strcmp(host, "::"))
|
|
|
|
: (!host || !strcmp(host, "0.0.0.0"));
|
|
|
|
}
|
|
|
|
|
2020-01-10 13:14:41 +00:00
|
|
|
/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept.
|
|
|
|
* \param[in] peer Application Server Process peer the address is appened to.
|
|
|
|
* \param[in] talloc_ctx talloc context used to allocate new address.
|
|
|
|
* \param[in] host string containing an IP addresses.
|
|
|
|
* \returns 0 on success; negtive otherwise */
|
|
|
|
int osmo_ss7_asp_peer_add_host(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *host)
|
|
|
|
{
|
|
|
|
int i;
|
2020-08-27 14:24:59 +00:00
|
|
|
bool new_is_v6 = osmo_ip_str_type(host) == AF_INET6;
|
|
|
|
bool new_is_any = host_is_ip_anyaddr(host, new_is_v6);
|
|
|
|
bool iter_is_v6;
|
2020-01-10 13:14:41 +00:00
|
|
|
|
2020-01-13 11:58:30 +00:00
|
|
|
if (peer->host_cnt >= ARRAY_SIZE(peer->host))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2020-08-27 14:24:59 +00:00
|
|
|
/* Makes no sense to have INET(6)_ANY many times, or INET(6)_ANY
|
|
|
|
together with specific addresses, be it of same or different
|
|
|
|
IP version:*/
|
2020-01-13 11:58:30 +00:00
|
|
|
if (new_is_any && peer->host_cnt != 0)
|
2020-01-10 13:14:41 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2020-01-13 11:58:30 +00:00
|
|
|
if (!new_is_any) {
|
2020-08-27 14:24:59 +00:00
|
|
|
/* Makes no sense to add specific address to set if INET(6)_ANY
|
|
|
|
is already set, be it from same or different IP version: */
|
2020-01-13 11:58:30 +00:00
|
|
|
for (i = 0; i < peer->host_cnt; i++) {
|
2020-08-27 14:24:59 +00:00
|
|
|
iter_is_v6 = osmo_ip_str_type(peer->host[i]) == AF_INET6;
|
|
|
|
if (host_is_ip_anyaddr(peer->host[i], iter_is_v6))
|
2020-01-13 11:58:30 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2020-01-10 13:14:41 +00:00
|
|
|
osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host);
|
|
|
|
peer->host_cnt++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
struct osmo_ss7_asp *
|
|
|
|
osmo_ss7_asp_find_by_name(struct osmo_ss7_instance *inst, const char *name)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_asp *asp;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
llist_for_each_entry(asp, &inst->asp_list, list) {
|
|
|
|
if (!strcmp(name, asp->cfg.name))
|
|
|
|
return asp;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t get_in_port(struct sockaddr *sa)
|
|
|
|
{
|
|
|
|
switch (sa->sa_family) {
|
|
|
|
case AF_INET:
|
|
|
|
return (((struct sockaddr_in*)sa)->sin_port);
|
|
|
|
case AF_INET6:
|
|
|
|
return (((struct sockaddr_in6*)sa)->sin6_port);
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-21 14:46:06 +00:00
|
|
|
/* Converts string representation of v4-mappend-on-v6 IP addr to a pure IPv4 address.
|
|
|
|
* Example: ::ffff:172.18.19.200 => 172.18.19.200
|
|
|
|
*/
|
|
|
|
static void chop_v4_mapped_on_v6_prefix(char* buf)
|
|
|
|
{
|
|
|
|
char *last_colon;
|
|
|
|
size_t len;
|
|
|
|
char *first_dot = strchr(buf, '.');
|
|
|
|
|
|
|
|
if (!first_dot)
|
|
|
|
return; /* Not an IPv4-mappend-on-v6 string representation, nothing to do */
|
|
|
|
last_colon = strrchr(buf, ':');
|
|
|
|
if (!last_colon)
|
|
|
|
return; /* pure IPv4 address, nothing to do */
|
|
|
|
|
|
|
|
len = strlen(last_colon + 1);
|
|
|
|
memmove(buf, last_colon + 1, len);
|
|
|
|
buf[len] = '\0';
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/*! \brief Find an ASP definition matching the local+remote IP/PORT of given fd
|
|
|
|
* \param[in] fd socket descriptor of given socket
|
|
|
|
* \returns SS7 ASP in case a matching one is found; NULL otherwise */
|
|
|
|
static struct osmo_ss7_asp *
|
|
|
|
osmo_ss7_asp_find_by_socket_addr(int fd)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_instance *inst;
|
2020-08-21 14:46:06 +00:00
|
|
|
struct sockaddr_storage sa_l, sa_r;
|
2017-04-03 15:37:10 +00:00
|
|
|
socklen_t sa_len_l = sizeof(sa_l);
|
|
|
|
socklen_t sa_len_r = sizeof(sa_r);
|
|
|
|
char hostbuf_l[64], hostbuf_r[64];
|
|
|
|
uint16_t local_port, remote_port;
|
2020-08-27 14:39:55 +00:00
|
|
|
bool loc_is_v6, rem_is_v6;
|
2017-04-03 15:37:10 +00:00
|
|
|
int rc;
|
2019-10-11 15:58:07 +00:00
|
|
|
int i;
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
/* convert local and remote IP to string */
|
2020-08-21 14:46:06 +00:00
|
|
|
rc = getsockname(fd, (struct sockaddr*)&sa_l, &sa_len_l);
|
2017-04-03 15:37:10 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return NULL;
|
2020-08-21 14:46:06 +00:00
|
|
|
rc = getnameinfo((struct sockaddr*)&sa_l, sa_len_l,
|
|
|
|
hostbuf_l, sizeof(hostbuf_l),
|
2017-04-03 15:37:10 +00:00
|
|
|
NULL, 0, NI_NUMERICHOST);
|
|
|
|
if (rc < 0)
|
|
|
|
return NULL;
|
2020-08-21 14:46:06 +00:00
|
|
|
local_port = ntohs(get_in_port((struct sockaddr*)&sa_l));
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2020-08-21 14:46:06 +00:00
|
|
|
rc = getpeername(fd, (struct sockaddr*)&sa_r, &sa_len_r);
|
2017-04-03 15:37:10 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return NULL;
|
2020-08-21 14:46:06 +00:00
|
|
|
rc = getnameinfo((struct sockaddr*)&sa_r, sa_len_r,
|
|
|
|
hostbuf_r, sizeof(hostbuf_r),
|
2017-04-03 15:37:10 +00:00
|
|
|
NULL, 0, NI_NUMERICHOST);
|
|
|
|
if (rc < 0)
|
|
|
|
return NULL;
|
2020-08-21 14:46:06 +00:00
|
|
|
remote_port = ntohs(get_in_port((struct sockaddr*)&sa_r));
|
|
|
|
|
|
|
|
/* If multi-home is used with both IPv4 and IPv6, then the socket is
|
|
|
|
* AF_INET6, and then returned IPv4 addresses are actually v6mapped ones.
|
|
|
|
* We need to convert them to IPv4 before matching.
|
|
|
|
*/
|
|
|
|
chop_v4_mapped_on_v6_prefix(hostbuf_l);
|
|
|
|
chop_v4_mapped_on_v6_prefix(hostbuf_r);
|
2020-08-27 14:39:55 +00:00
|
|
|
loc_is_v6 = osmo_ip_str_type(hostbuf_l) == AF_INET6;
|
|
|
|
rem_is_v6 = osmo_ip_str_type(hostbuf_r) == AF_INET6;
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
/* check all instances for any ASP definition matching the
|
|
|
|
* address combination of local/remote ip/port */
|
2017-04-14 11:09:17 +00:00
|
|
|
llist_for_each_entry(inst, &osmo_ss7_instances, list) {
|
2017-04-03 15:37:10 +00:00
|
|
|
struct osmo_ss7_asp *asp;
|
|
|
|
llist_for_each_entry(asp, &inst->asp_list, list) {
|
2019-10-11 15:58:07 +00:00
|
|
|
if (asp->cfg.local.port != local_port)
|
|
|
|
continue;
|
|
|
|
if (asp->cfg.remote.port && asp->cfg.remote.port != remote_port)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (i = 0; i < asp->cfg.local.host_cnt; i++) {
|
2020-08-27 14:39:55 +00:00
|
|
|
bool iter_is_v6 = osmo_ip_str_type(asp->cfg.local.host[i]) == AF_INET6;
|
|
|
|
bool iter_is_anyaddr = host_is_ip_anyaddr(asp->cfg.local.host[i], iter_is_v6);
|
|
|
|
/* "::" (v6) covers "0.0.0.0" (v4), but not otherwise */
|
|
|
|
if (iter_is_v6 != loc_is_v6 &&
|
|
|
|
!(iter_is_v6 && iter_is_anyaddr))
|
|
|
|
continue;
|
|
|
|
if (iter_is_anyaddr ||
|
|
|
|
!strcmp(asp->cfg.local.host[i], hostbuf_l))
|
2019-10-11 15:58:07 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == asp->cfg.local.host_cnt)
|
|
|
|
continue; /* didn't match any local.host */
|
|
|
|
|
2019-10-23 16:39:51 +00:00
|
|
|
/* If no remote host was set, it's probably a server and hence we match any cli src */
|
|
|
|
if (asp->cfg.remote.host_cnt) {
|
|
|
|
for (i = 0; i < asp->cfg.remote.host_cnt; i++) {
|
2020-08-27 14:39:55 +00:00
|
|
|
bool iter_is_v6 = osmo_ip_str_type(asp->cfg.remote.host[i]) == AF_INET6;
|
|
|
|
bool iter_is_anyaddr = host_is_ip_anyaddr(asp->cfg.remote.host[i], iter_is_v6);
|
|
|
|
/* "::" (v6) covers "0.0.0.0" (v4), but not otherwise */
|
|
|
|
if (iter_is_v6 != rem_is_v6 &&
|
|
|
|
!(iter_is_v6 && iter_is_anyaddr))
|
|
|
|
continue;
|
|
|
|
if (iter_is_anyaddr ||
|
|
|
|
!strcmp(asp->cfg.remote.host[i], hostbuf_r))
|
2019-10-23 16:39:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == asp->cfg.remote.host_cnt)
|
|
|
|
continue; /* didn't match any remote.host */
|
2019-10-11 15:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return asp;
|
2017-04-03 15:37:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
sccp: make simple client configurable via VTY
The osmo_sccp_simple_client_on_ss7_id and osmo_sccp_simple_client
are not entirely configurable via VTY commands. The relation to
the VTY is implicit. The user may set up instance objects via
VTY (cs7/ss7, AS, ASP), which are then automatically created on
startup.
Each cs7 instance gets its own ID via the VTY configuration. When
osmo_sccp_simple_client_on_ss7_id() is called with the cs7 instance
id. (for osmo_sccp_simple_client() the ID will be hardcoded to 1),
the function automatically checks if the CS7 instance is present,
if not it will create one automatically using the caller supplied
parameters as a defult. If a CS7 instance is present, the function
checks for the presence of an AS and an ASP. These objects are
present, they will be used. If not, new objects will be created.
Both functions must not be called if an SCCP instance is already
present. Since there can only be one SCCP instance per CS7 instance,
this is an error condition.
Add additional logic that checks to detect an already existing, valid
configuration. If no or an insufficient configuration is detected,
use the caller supplied parameters as default configuration.
Change-Id: I293f3526ce6182dca74a169a23449dbc7af57c7c
2017-07-19 16:41:09 +00:00
|
|
|
/*! \brief Find an ASP that matches the given protocol.
|
|
|
|
* \param[in] as Application Server in which to look for \ref asp
|
|
|
|
* \returns SS7 ASP in case a matching one is found; NULL otherwise */
|
|
|
|
struct osmo_ss7_asp
|
|
|
|
*osmo_ss7_asp_find_by_proto(struct osmo_ss7_as *as,
|
|
|
|
enum osmo_ss7_asp_protocol proto)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
|
|
|
if (as->cfg.asps[i] && as->cfg.asps[i]->cfg.proto == proto)
|
|
|
|
return as->cfg.asps[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-11-19 00:24:44 +00:00
|
|
|
struct osmo_ss7_asp *
|
|
|
|
osmo_ss7_asp_find(struct osmo_ss7_instance *inst, const char *name,
|
|
|
|
uint16_t remote_port, uint16_t local_port,
|
|
|
|
enum osmo_ss7_asp_protocol proto)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_asp *asp;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
asp = osmo_ss7_asp_find_by_name(inst, name);
|
|
|
|
if (!asp)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ((asp->cfg.remote.port != remote_port || asp->cfg.local.port != local_port || asp->cfg.proto != proto))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return asp;
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
struct osmo_ss7_asp *
|
|
|
|
osmo_ss7_asp_find_or_create(struct osmo_ss7_instance *inst, const char *name,
|
|
|
|
uint16_t remote_port, uint16_t local_port,
|
|
|
|
enum osmo_ss7_asp_protocol proto)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_asp *asp;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
asp = osmo_ss7_asp_find_by_name(inst, name);
|
|
|
|
|
|
|
|
if (asp && (asp->cfg.remote.port != remote_port ||
|
|
|
|
asp->cfg.local.port != local_port ||
|
|
|
|
asp->cfg.proto != proto))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!asp) {
|
|
|
|
/* FIXME: check if local port has SCTP? */
|
|
|
|
asp = talloc_zero(inst, struct osmo_ss7_asp);
|
|
|
|
asp->inst = inst;
|
|
|
|
asp->cfg.remote.port = remote_port;
|
|
|
|
asp->cfg.local.port = local_port;
|
|
|
|
asp->cfg.proto = proto;
|
|
|
|
asp->cfg.name = talloc_strdup(asp, name);
|
|
|
|
llist_add_tail(&asp->list, &inst->asp_list);
|
2017-04-17 08:42:30 +00:00
|
|
|
|
|
|
|
/* The SUA code internally needs SCCP to work */
|
2018-09-26 23:21:45 +00:00
|
|
|
if (proto == OSMO_SS7_ASP_PROT_SUA)
|
|
|
|
osmo_ss7_ensure_sccp(inst);
|
2017-04-17 08:42:30 +00:00
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
}
|
|
|
|
return asp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void osmo_ss7_asp_destroy(struct osmo_ss7_asp *asp)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_as *as;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
2019-10-19 10:23:21 +00:00
|
|
|
LOGPASP(asp, DLSS7, LOGL_INFO, "Destroying ASP\n");
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
if (asp->server)
|
|
|
|
osmo_stream_srv_destroy(asp->server);
|
|
|
|
if (asp->client)
|
|
|
|
osmo_stream_cli_destroy(asp->client);
|
|
|
|
if (asp->fi)
|
|
|
|
osmo_fsm_inst_term(asp->fi, OSMO_FSM_TERM_REQUEST, NULL);
|
2017-04-14 20:24:15 +00:00
|
|
|
if (asp->xua_server)
|
|
|
|
llist_del(&asp->siblings);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
/* unlink from all ASs we are part of */
|
|
|
|
llist_for_each_entry(as, &asp->inst->as_list, list) {
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
|
|
|
if (as->cfg.asps[i] == asp) {
|
|
|
|
as->cfg.asps[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* unlink from ss7_instance */
|
|
|
|
asp->inst = NULL;
|
|
|
|
llist_del(&asp->list);
|
|
|
|
/* release memory */
|
|
|
|
talloc_free(asp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xua_cli_read_cb(struct osmo_stream_cli *conn);
|
2017-04-02 09:58:17 +00:00
|
|
|
static int ipa_cli_read_cb(struct osmo_stream_cli *conn);
|
2017-04-03 15:37:10 +00:00
|
|
|
static int xua_cli_connect_cb(struct osmo_stream_cli *cli);
|
|
|
|
|
|
|
|
int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp)
|
|
|
|
{
|
|
|
|
int rc;
|
2019-10-11 19:17:40 +00:00
|
|
|
char bufloc[512], bufrem[512];
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
2019-10-11 19:17:40 +00:00
|
|
|
osmo_ss7_asp_peer_snprintf(bufloc, sizeof(bufloc), &asp->cfg.local);
|
|
|
|
osmo_ss7_asp_peer_snprintf(bufrem, sizeof(bufrem), &asp->cfg.remote);
|
2019-11-08 14:39:59 +00:00
|
|
|
LOGPASP(asp, DLSS7, LOGL_INFO, "Restarting ASP %s, r=%s<->l=%s\n",
|
|
|
|
asp->cfg.name, bufrem, bufloc);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
if (!asp->cfg.is_server) {
|
|
|
|
/* We are in client mode now */
|
|
|
|
if (asp->server) {
|
|
|
|
/* if we previously were in server mode,
|
|
|
|
* destroy it */
|
|
|
|
osmo_stream_srv_destroy(asp->server);
|
|
|
|
asp->server = NULL;
|
|
|
|
}
|
|
|
|
if (!asp->client)
|
|
|
|
asp->client = osmo_stream_cli_create(asp);
|
|
|
|
if (!asp->client) {
|
2019-10-19 10:23:21 +00:00
|
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Unable to create stream"
|
2017-04-03 15:37:10 +00:00
|
|
|
" client for ASP %s\n", asp->cfg.name);
|
|
|
|
return -1;
|
|
|
|
}
|
2017-04-11 16:35:06 +00:00
|
|
|
osmo_stream_cli_set_nodelay(asp->client, true);
|
2019-10-11 15:58:07 +00:00
|
|
|
osmo_stream_cli_set_addrs(asp->client, (const char**)asp->cfg.remote.host, asp->cfg.remote.host_cnt);
|
2017-04-03 15:37:10 +00:00
|
|
|
osmo_stream_cli_set_port(asp->client, asp->cfg.remote.port);
|
2019-10-11 15:58:07 +00:00
|
|
|
osmo_stream_cli_set_local_addrs(asp->client, (const char**)asp->cfg.local.host, asp->cfg.local.host_cnt);
|
2017-04-08 20:33:50 +00:00
|
|
|
osmo_stream_cli_set_local_port(asp->client, asp->cfg.local.port);
|
2017-04-02 09:58:17 +00:00
|
|
|
osmo_stream_cli_set_proto(asp->client, asp_proto_to_ip_proto(asp->cfg.proto));
|
2017-04-03 15:37:10 +00:00
|
|
|
osmo_stream_cli_set_reconnect_timeout(asp->client, 5);
|
|
|
|
osmo_stream_cli_set_connect_cb(asp->client, xua_cli_connect_cb);
|
2017-04-02 09:58:17 +00:00
|
|
|
if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA)
|
|
|
|
osmo_stream_cli_set_read_cb(asp->client, ipa_cli_read_cb);
|
|
|
|
else
|
|
|
|
osmo_stream_cli_set_read_cb(asp->client, xua_cli_read_cb);
|
2017-04-03 15:37:10 +00:00
|
|
|
osmo_stream_cli_set_data(asp->client, asp);
|
2019-02-07 10:06:15 +00:00
|
|
|
rc = osmo_stream_cli_open(asp->client);
|
2017-04-03 15:37:10 +00:00
|
|
|
if (rc < 0) {
|
2019-10-19 10:23:21 +00:00
|
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Unable to open stream"
|
2019-10-11 19:17:40 +00:00
|
|
|
" client for ASP %s, %s ==> %s\n", asp->cfg.name, bufloc, bufrem);
|
2019-02-07 10:06:15 +00:00
|
|
|
/* we don't return error in here because osmo_stream_cli_open()
|
|
|
|
will continue to retry (due to timeout being explicitly set with
|
|
|
|
osmo_stream_cli_set_reconnect_timeout() above) to connect so the error is transient */
|
2017-04-03 15:37:10 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* We are in server mode now */
|
|
|
|
if (asp->client) {
|
|
|
|
/* if we previously were in client mode,
|
|
|
|
* destroy it */
|
|
|
|
osmo_stream_cli_destroy(asp->client);
|
|
|
|
asp->client = NULL;
|
|
|
|
}
|
|
|
|
/* FIXME: ensure we have a SCTP server */
|
2019-10-19 10:23:21 +00:00
|
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "ASP Restart for server "
|
2017-04-03 15:37:10 +00:00
|
|
|
"not implemented yet!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (re)start the ASP FSM */
|
|
|
|
if (asp->fi)
|
|
|
|
osmo_fsm_inst_term(asp->fi, OSMO_FSM_TERM_REQUEST, NULL);
|
2019-10-29 20:55:49 +00:00
|
|
|
asp->fi = xua_asp_fsm_start(asp, asp->cfg.role, LOGL_DEBUG);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-05 18:11:16 +00:00
|
|
|
bool osmo_ss7_asp_active(const struct osmo_ss7_asp *asp)
|
|
|
|
{
|
|
|
|
if (!asp->fi)
|
|
|
|
return false;
|
|
|
|
return asp->fi->state == XUA_ASP_S_ACTIVE;
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* libosmo-netif integration for SCTP stream server/client
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
static const struct value_string sctp_assoc_chg_vals[] = {
|
|
|
|
{ SCTP_COMM_UP, "COMM_UP" },
|
|
|
|
{ SCTP_COMM_LOST, "COMM_LOST" },
|
|
|
|
{ SCTP_RESTART, "RESTART" },
|
|
|
|
{ SCTP_SHUTDOWN_COMP, "SHUTDOWN_COMP" },
|
|
|
|
{ SCTP_CANT_STR_ASSOC, "CANT_STR_ASSOC" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct value_string sctp_sn_type_vals[] = {
|
|
|
|
{ SCTP_ASSOC_CHANGE, "ASSOC_CHANGE" },
|
|
|
|
{ SCTP_PEER_ADDR_CHANGE, "PEER_ADDR_CHANGE" },
|
|
|
|
{ SCTP_SHUTDOWN_EVENT, "SHUTDOWN_EVENT" },
|
|
|
|
{ SCTP_SEND_FAILED, "SEND_FAILED" },
|
|
|
|
{ SCTP_REMOTE_ERROR, "REMOTE_ERROR" },
|
|
|
|
{ SCTP_PARTIAL_DELIVERY_EVENT, "PARTIAL_DELIVERY_EVENT" },
|
|
|
|
{ SCTP_ADAPTATION_INDICATION, "ADAPTATION_INDICATION" },
|
|
|
|
#ifdef SCTP_AUTHENTICATION_INDICATION
|
2019-02-12 13:38:28 +00:00
|
|
|
{ SCTP_AUTHENTICATION_INDICATION, "AUTHENTICATION_INDICATION" },
|
2017-04-03 15:37:10 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SCTP_SENDER_DRY_EVENT
|
|
|
|
{ SCTP_SENDER_DRY_EVENT, "SENDER_DRY_EVENT" },
|
|
|
|
#endif
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int get_logevel_by_sn_type(int sn_type)
|
|
|
|
{
|
|
|
|
switch (sn_type) {
|
|
|
|
case SCTP_ADAPTATION_INDICATION:
|
|
|
|
case SCTP_PEER_ADDR_CHANGE:
|
|
|
|
#ifdef SCTP_AUTHENTICATION_INDICATION
|
|
|
|
case SCTP_AUTHENTICATION_INDICATION:
|
|
|
|
#endif
|
|
|
|
#ifdef SCTP_SENDER_DRY_EVENT
|
|
|
|
case SCTP_SENDER_DRY_EVENT:
|
|
|
|
#endif
|
|
|
|
return LOGL_INFO;
|
|
|
|
case SCTP_ASSOC_CHANGE:
|
|
|
|
return LOGL_NOTICE;
|
|
|
|
case SCTP_SHUTDOWN_EVENT:
|
|
|
|
case SCTP_PARTIAL_DELIVERY_EVENT:
|
|
|
|
return LOGL_NOTICE;
|
|
|
|
case SCTP_SEND_FAILED:
|
|
|
|
case SCTP_REMOTE_ERROR:
|
|
|
|
return LOGL_ERROR;
|
|
|
|
default:
|
|
|
|
return LOGL_NOTICE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void log_sctp_notification(struct osmo_ss7_asp *asp, const char *pfx,
|
|
|
|
union sctp_notification *notif)
|
|
|
|
{
|
|
|
|
int log_level;
|
|
|
|
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_INFO, "%s SCTP NOTIFICATION %u flags=0x%0x\n",
|
|
|
|
pfx, notif->sn_header.sn_type,
|
|
|
|
notif->sn_header.sn_flags);
|
|
|
|
|
|
|
|
log_level = get_logevel_by_sn_type(notif->sn_header.sn_type);
|
|
|
|
|
|
|
|
switch (notif->sn_header.sn_type) {
|
|
|
|
case SCTP_ASSOC_CHANGE:
|
|
|
|
LOGPASP(asp, DLSS7, log_level, "%s SCTP_ASSOC_CHANGE: %s\n",
|
|
|
|
pfx, get_value_string(sctp_assoc_chg_vals,
|
|
|
|
notif->sn_assoc_change.sac_state));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOGPASP(asp, DLSS7, log_level, "%s %s\n",
|
|
|
|
pfx, get_value_string(sctp_sn_type_vals,
|
|
|
|
notif->sn_header.sn_type));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-02 09:58:17 +00:00
|
|
|
/* netif code tells us we can read something from the socket */
|
|
|
|
static int ipa_srv_conn_cb(struct osmo_stream_srv *conn)
|
|
|
|
{
|
|
|
|
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
|
|
|
struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(conn);
|
|
|
|
struct msgb *msg = NULL;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* read IPA message from socket and process it */
|
|
|
|
rc = ipa_msg_recv_buffered(ofd->fd, &msg, &asp->pending_msg);
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): ipa_msg_recv_buffered() returned %d\n",
|
|
|
|
__func__, rc);
|
|
|
|
if (rc <= 0) {
|
|
|
|
if (rc == -EAGAIN) {
|
|
|
|
/* more data needed */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
osmo_stream_srv_destroy(conn);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if (osmo_ipa_process_msg(msg) < 0) {
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Bad IPA message\n");
|
|
|
|
osmo_stream_srv_destroy(conn);
|
|
|
|
msgb_free(msg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
msg->dst = asp;
|
|
|
|
|
|
|
|
return ipa_rx_msg(asp, msg);
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/* netif code tells us we can read something from the socket */
|
|
|
|
static int xua_srv_conn_cb(struct osmo_stream_srv *conn)
|
|
|
|
{
|
|
|
|
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
|
|
|
struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(conn);
|
2017-04-15 21:17:01 +00:00
|
|
|
struct msgb *msg = m3ua_msgb_alloc("xUA Server Rx");
|
2017-04-03 15:37:10 +00:00
|
|
|
struct sctp_sndrcvinfo sinfo;
|
|
|
|
unsigned int ppid;
|
|
|
|
int flags = 0;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* read xUA message from socket and process it */
|
|
|
|
rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg),
|
|
|
|
NULL, NULL, &sinfo, &flags);
|
2017-04-07 15:19:19 +00:00
|
|
|
LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n",
|
|
|
|
__func__, rc, flags);
|
2017-04-03 15:37:10 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
osmo_stream_srv_destroy(conn);
|
2018-11-09 13:56:56 +00:00
|
|
|
rc = -EBADF;
|
2017-04-03 15:37:10 +00:00
|
|
|
goto out;
|
|
|
|
} else if (rc == 0) {
|
|
|
|
osmo_stream_srv_destroy(conn);
|
2018-11-09 13:56:56 +00:00
|
|
|
rc = -EBADF;
|
2017-04-03 15:37:10 +00:00
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
msgb_put(msg, rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & MSG_NOTIFICATION) {
|
|
|
|
union sctp_notification *notif = (union sctp_notification *) msgb_data(msg);
|
|
|
|
|
|
|
|
log_sctp_notification(asp, "xUA SRV", notif);
|
|
|
|
|
|
|
|
switch (notif->sn_header.sn_type) {
|
|
|
|
case SCTP_SHUTDOWN_EVENT:
|
|
|
|
osmo_stream_srv_destroy(conn);
|
2018-11-09 13:56:56 +00:00
|
|
|
rc = -EBADF;
|
2017-04-03 15:37:10 +00:00
|
|
|
break;
|
2017-04-11 17:08:17 +00:00
|
|
|
case SCTP_ASSOC_CHANGE:
|
|
|
|
if (notif->sn_assoc_change.sac_state == SCTP_RESTART)
|
|
|
|
xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RESTART,
|
|
|
|
PRIM_OP_INDICATION);
|
2018-11-09 13:56:56 +00:00
|
|
|
rc = 0;
|
2017-04-11 17:08:17 +00:00
|
|
|
break;
|
2017-04-03 15:37:10 +00:00
|
|
|
default:
|
2018-11-09 13:56:56 +00:00
|
|
|
rc = 0;
|
2017-04-03 15:37:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:08:15 +00:00
|
|
|
ppid = ntohl(sinfo.sinfo_ppid);
|
2017-04-03 15:37:10 +00:00
|
|
|
msgb_sctp_ppid(msg) = ppid;
|
2017-04-10 15:51:30 +00:00
|
|
|
msgb_sctp_stream(msg) = sinfo.sinfo_stream;
|
2017-04-03 15:37:10 +00:00
|
|
|
msg->dst = asp;
|
|
|
|
|
2017-04-03 19:42:08 +00:00
|
|
|
if (ppid == SUA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA)
|
|
|
|
rc = sua_rx_msg(asp, msg);
|
|
|
|
else if (ppid == M3UA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_M3UA)
|
2017-04-03 15:37:10 +00:00
|
|
|
rc = m3ua_rx_msg(asp, msg);
|
2018-05-26 09:32:50 +00:00
|
|
|
else
|
|
|
|
rc = ss7_asp_rx_unknown(asp, ppid, msg);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
msgb_free(msg);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* client has established SCTP connection to server */
|
|
|
|
static int xua_cli_connect_cb(struct osmo_stream_cli *cli)
|
|
|
|
{
|
|
|
|
struct osmo_fd *ofd = osmo_stream_cli_get_ofd(cli);
|
|
|
|
struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(cli);
|
|
|
|
|
|
|
|
/* update the socket name */
|
2021-04-30 10:03:07 +00:00
|
|
|
talloc_free(asp->sock_name);
|
2017-04-11 17:34:12 +00:00
|
|
|
asp->sock_name = osmo_sock_get_name(asp, ofd->fd);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_INFO, "Client connected %s\n", asp->sock_name);
|
|
|
|
|
2017-04-07 13:01:56 +00:00
|
|
|
if (asp->lm && asp->lm->prim_cb) {
|
|
|
|
/* Notify layer manager that a connection has been
|
|
|
|
* established */
|
|
|
|
xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION);
|
|
|
|
} else {
|
|
|
|
/* directly as the ASP FSM to start by sending an ASP-UP ... */
|
|
|
|
osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_M_ASP_UP_REQ, NULL);
|
|
|
|
}
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-11 17:00:58 +00:00
|
|
|
static void xua_cli_close(struct osmo_stream_cli *cli)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(cli);
|
|
|
|
|
|
|
|
osmo_stream_cli_close(cli);
|
2017-04-15 20:37:49 +00:00
|
|
|
osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_COMM_DOWN_IND, asp);
|
2017-04-11 17:00:58 +00:00
|
|
|
/* send M-SCTP_RELEASE.ind to XUA Layer Manager */
|
|
|
|
xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RELEASE, PRIM_OP_INDICATION);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xua_cli_close_and_reconnect(struct osmo_stream_cli *cli)
|
|
|
|
{
|
|
|
|
xua_cli_close(cli);
|
|
|
|
osmo_stream_cli_reconnect(cli);
|
|
|
|
}
|
|
|
|
|
2017-04-02 09:58:17 +00:00
|
|
|
/* read call-back for IPA/SCCPlite socket */
|
|
|
|
static int ipa_cli_read_cb(struct osmo_stream_cli *conn)
|
|
|
|
{
|
|
|
|
struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
|
|
|
|
struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(conn);
|
|
|
|
struct msgb *msg = NULL;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* read IPA message from socket and process it */
|
|
|
|
rc = ipa_msg_recv_buffered(ofd->fd, &msg, &asp->pending_msg);
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): ipa_msg_recv_buffered() returned %d\n",
|
|
|
|
__func__, rc);
|
|
|
|
if (rc <= 0) {
|
|
|
|
if (rc == -EAGAIN) {
|
|
|
|
/* more data needed */
|
|
|
|
return 0;
|
|
|
|
}
|
2017-04-15 20:37:49 +00:00
|
|
|
xua_cli_close_and_reconnect(conn);
|
2017-04-02 09:58:17 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if (osmo_ipa_process_msg(msg) < 0) {
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Bad IPA message\n");
|
2017-04-15 20:37:49 +00:00
|
|
|
xua_cli_close_and_reconnect(conn);
|
2017-04-02 09:58:17 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
msg->dst = asp;
|
|
|
|
return ipa_rx_msg(asp, msg);
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
static int xua_cli_read_cb(struct osmo_stream_cli *conn)
|
|
|
|
{
|
|
|
|
struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
|
|
|
|
struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(conn);
|
2017-04-15 21:17:01 +00:00
|
|
|
struct msgb *msg = m3ua_msgb_alloc("xUA Client Rx");
|
2017-04-03 15:37:10 +00:00
|
|
|
struct sctp_sndrcvinfo sinfo;
|
|
|
|
unsigned int ppid;
|
|
|
|
int flags = 0;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* read xUA message from socket and process it */
|
|
|
|
rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg),
|
|
|
|
NULL, NULL, &sinfo, &flags);
|
2017-04-07 15:19:37 +00:00
|
|
|
LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n",
|
2017-04-03 15:37:10 +00:00
|
|
|
__func__, rc, flags);
|
|
|
|
if (rc < 0) {
|
2017-04-11 17:00:58 +00:00
|
|
|
xua_cli_close_and_reconnect(conn);
|
2017-04-03 15:37:10 +00:00
|
|
|
goto out;
|
|
|
|
} else if (rc == 0) {
|
2017-04-11 17:00:58 +00:00
|
|
|
xua_cli_close_and_reconnect(conn);
|
2017-04-03 15:37:10 +00:00
|
|
|
} else {
|
|
|
|
msgb_put(msg, rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & MSG_NOTIFICATION) {
|
|
|
|
union sctp_notification *notif = (union sctp_notification *) msgb_data(msg);
|
|
|
|
|
|
|
|
log_sctp_notification(asp, "xUA CLNT", notif);
|
|
|
|
|
|
|
|
switch (notif->sn_header.sn_type) {
|
|
|
|
case SCTP_SHUTDOWN_EVENT:
|
2017-04-11 17:00:58 +00:00
|
|
|
xua_cli_close_and_reconnect(conn);
|
2017-04-03 15:37:10 +00:00
|
|
|
break;
|
2017-04-11 17:08:17 +00:00
|
|
|
case SCTP_ASSOC_CHANGE:
|
|
|
|
if (notif->sn_assoc_change.sac_state == SCTP_RESTART)
|
|
|
|
xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RESTART,
|
|
|
|
PRIM_OP_INDICATION);
|
2017-04-03 15:37:10 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc == 0)
|
|
|
|
goto out;
|
|
|
|
|
2017-04-12 10:08:15 +00:00
|
|
|
ppid = ntohl(sinfo.sinfo_ppid);
|
2017-04-03 15:37:10 +00:00
|
|
|
msgb_sctp_ppid(msg) = ppid;
|
2017-04-10 15:51:30 +00:00
|
|
|
msgb_sctp_stream(msg) = sinfo.sinfo_stream;
|
2017-04-03 15:37:10 +00:00
|
|
|
msg->dst = asp;
|
|
|
|
|
2017-04-03 19:42:08 +00:00
|
|
|
if (ppid == SUA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA)
|
|
|
|
rc = sua_rx_msg(asp, msg);
|
|
|
|
else if (ppid == M3UA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_M3UA)
|
2017-04-03 15:37:10 +00:00
|
|
|
rc = m3ua_rx_msg(asp, msg);
|
2018-05-26 09:32:50 +00:00
|
|
|
else
|
|
|
|
rc = ss7_asp_rx_unknown(asp, ppid, msg);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
msgb_free(msg);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xua_srv_conn_closed_cb(struct osmo_stream_srv *srv)
|
|
|
|
{
|
|
|
|
struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(srv);
|
|
|
|
|
2021-04-25 13:44:16 +00:00
|
|
|
LOGP(DLSS7, LOGL_INFO, "%s: connection closed\n", asp ? asp->cfg.name : "?");
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-04-15 18:38:28 +00:00
|
|
|
if (!asp)
|
|
|
|
return 0;
|
|
|
|
|
2017-04-11 18:45:36 +00:00
|
|
|
/* notify ASP FSM and everyone else */
|
|
|
|
osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_COMM_DOWN_IND, NULL);
|
|
|
|
|
|
|
|
/* delete any RKM-dynamically allocated ASs for this ASP */
|
|
|
|
xua_rkm_cleanup_dyn_as_for_asp(asp);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-04-11 17:00:58 +00:00
|
|
|
/* send M-SCTP_RELEASE.ind to Layer Manager */
|
|
|
|
xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RELEASE, PRIM_OP_INDICATION);
|
|
|
|
|
2020-06-25 11:15:09 +00:00
|
|
|
asp->server = NULL;
|
|
|
|
|
2017-04-11 18:43:51 +00:00
|
|
|
/* if we were dynamically allocated at accept_cb() time, let's
|
|
|
|
* self-destruct now. A new connection will re-create the ASP. */
|
|
|
|
if (asp->dyn_allocated) {
|
|
|
|
/* avoid re-entrance via osmo_stream_srv_destroy() which
|
|
|
|
* called us */
|
|
|
|
osmo_ss7_asp_destroy(asp);
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* server has accept()ed a new SCTP association, let's find the ASP for
|
|
|
|
* it (if any) */
|
|
|
|
static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd)
|
|
|
|
{
|
|
|
|
struct osmo_xua_server *oxs = osmo_stream_srv_link_get_data(link);
|
|
|
|
struct osmo_stream_srv *srv;
|
|
|
|
struct osmo_ss7_asp *asp;
|
|
|
|
char *sock_name = osmo_sock_get_name(link, fd);
|
2019-10-22 19:36:43 +00:00
|
|
|
const char *proto_name = get_value_string(osmo_ss7_asp_protocol_vals, oxs->cfg.proto);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2019-10-22 19:36:43 +00:00
|
|
|
LOGP(DLSS7, LOGL_INFO, "%s: New %s connection accepted\n", sock_name, proto_name);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-04-02 09:58:17 +00:00
|
|
|
if (oxs->cfg.proto == OSMO_SS7_ASP_PROT_IPA) {
|
|
|
|
srv = osmo_stream_srv_create(oxs, link, fd,
|
|
|
|
ipa_srv_conn_cb,
|
|
|
|
xua_srv_conn_closed_cb, NULL);
|
|
|
|
} else {
|
|
|
|
srv = osmo_stream_srv_create(oxs, link, fd,
|
|
|
|
xua_srv_conn_cb,
|
|
|
|
xua_srv_conn_closed_cb, NULL);
|
|
|
|
}
|
2017-04-03 15:37:10 +00:00
|
|
|
if (!srv) {
|
|
|
|
LOGP(DLSS7, LOGL_ERROR, "%s: Unable to create stream server "
|
2017-04-02 09:58:17 +00:00
|
|
|
"for connection\n", sock_name);
|
2017-04-03 15:37:10 +00:00
|
|
|
close(fd);
|
|
|
|
talloc_free(sock_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
asp = osmo_ss7_asp_find_by_socket_addr(fd);
|
2017-04-05 23:41:03 +00:00
|
|
|
if (asp) {
|
|
|
|
LOGP(DLSS7, LOGL_INFO, "%s: matched connection to ASP %s\n",
|
|
|
|
sock_name, asp->cfg.name);
|
2020-06-24 19:14:36 +00:00
|
|
|
/* we need to check if we already have a socket associated, and close it. Otherwise it might
|
|
|
|
* happen that both the listen-fd for this accept() and the old socket are marked 'readable'
|
|
|
|
* during the same scheduling interval, and we're processing them in the "wrong" order, i.e.
|
|
|
|
* we first see the accept of the new fd before we see the close on the old fd */
|
|
|
|
if (asp->server) {
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_FATAL, "accept of new connection from %s before old was closed "
|
|
|
|
"-> close old one\n", sock_name);
|
|
|
|
osmo_stream_srv_set_data(asp->server, NULL);
|
|
|
|
osmo_stream_srv_destroy(asp->server);
|
|
|
|
asp->server = NULL;
|
|
|
|
}
|
2017-04-05 23:41:03 +00:00
|
|
|
} else {
|
|
|
|
if (!oxs->cfg.accept_dyn_reg) {
|
2019-10-22 19:36:43 +00:00
|
|
|
LOGP(DLSS7, LOGL_NOTICE, "%s: %s connection without matching "
|
2017-04-05 23:41:03 +00:00
|
|
|
"ASP definition and no dynamic registration enabled, terminating\n",
|
2019-10-22 19:36:43 +00:00
|
|
|
sock_name, proto_name);
|
2017-04-05 23:41:03 +00:00
|
|
|
} else {
|
|
|
|
char namebuf[32];
|
|
|
|
static uint32_t dyn_asp_num = 0;
|
|
|
|
snprintf(namebuf, sizeof(namebuf), "asp-dyn-%u", dyn_asp_num++);
|
2017-04-11 16:36:44 +00:00
|
|
|
asp = osmo_ss7_asp_find_or_create(oxs->inst, namebuf, 0, 0,
|
2017-04-15 17:02:54 +00:00
|
|
|
oxs->cfg.proto);
|
2017-04-11 16:37:40 +00:00
|
|
|
if (asp) {
|
2019-03-13 11:45:58 +00:00
|
|
|
char hostbuf[INET6_ADDRSTRLEN];
|
2020-01-10 13:39:42 +00:00
|
|
|
const char *hostbuf_ptr = &hostbuf[0];
|
2019-03-13 11:45:58 +00:00
|
|
|
char portbuf[16];
|
|
|
|
|
|
|
|
osmo_sock_get_ip_and_port(fd, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), false);
|
2019-11-06 12:34:00 +00:00
|
|
|
LOGP(DLSS7, LOGL_INFO, "%s: created dynamic ASP %s\n",
|
2017-04-05 23:41:03 +00:00
|
|
|
sock_name, asp->cfg.name);
|
2017-04-11 16:37:40 +00:00
|
|
|
asp->cfg.is_server = true;
|
2019-11-06 12:56:23 +00:00
|
|
|
asp->cfg.role = OSMO_SS7_ASP_ROLE_SG;
|
2019-11-08 14:51:14 +00:00
|
|
|
asp->cfg.local.port = oxs->cfg.local.port;
|
2019-03-13 11:45:58 +00:00
|
|
|
asp->cfg.remote.port = atoi(portbuf);
|
2017-04-11 18:43:51 +00:00
|
|
|
asp->dyn_allocated = true;
|
2017-04-02 09:58:17 +00:00
|
|
|
asp->server = srv;
|
2020-01-10 13:39:42 +00:00
|
|
|
osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp,
|
|
|
|
(const char* const*)oxs->cfg.local.host,
|
|
|
|
oxs->cfg.local.host_cnt);
|
|
|
|
osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp,
|
|
|
|
&hostbuf_ptr, 1);
|
2017-04-11 16:37:40 +00:00
|
|
|
osmo_ss7_asp_restart(asp);
|
|
|
|
}
|
2017-04-05 23:41:03 +00:00
|
|
|
}
|
|
|
|
if (!asp) {
|
|
|
|
osmo_stream_srv_destroy(srv);
|
|
|
|
talloc_free(sock_name);
|
|
|
|
return -1;
|
|
|
|
}
|
2020-06-24 19:14:36 +00:00
|
|
|
llist_add_tail(&asp->siblings, &oxs->asp_list);
|
2017-04-03 15:37:10 +00:00
|
|
|
}
|
2017-04-05 23:41:03 +00:00
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/* update the ASP reference back to the server over which the
|
|
|
|
* connection came in */
|
|
|
|
asp->server = srv;
|
2017-04-14 20:24:15 +00:00
|
|
|
asp->xua_server = oxs;
|
2017-04-03 15:37:10 +00:00
|
|
|
/* update the ASP socket name */
|
2021-04-30 10:03:07 +00:00
|
|
|
talloc_free(asp->sock_name);
|
2017-04-03 15:37:10 +00:00
|
|
|
asp->sock_name = talloc_reparent(link, asp, sock_name);
|
|
|
|
/* make sure the conn_cb() is called with the asp as private
|
|
|
|
* data */
|
|
|
|
osmo_stream_srv_set_data(srv, asp);
|
|
|
|
|
2017-04-07 13:01:56 +00:00
|
|
|
/* send M-SCTP_ESTABLISH.ind to Layer Manager */
|
2017-04-15 18:39:11 +00:00
|
|
|
osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_EST_IND, 0);
|
2017-04-07 13:01:56 +00:00
|
|
|
xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief send a fully encoded msgb via a given ASP
|
|
|
|
* \param[in] asp Application Server Process through which to send
|
|
|
|
* \param[in] msg message buffer to transmit. Ownership transferred.
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int osmo_ss7_asp_send(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|
|
|
{
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
|
|
|
|
switch (asp->cfg.proto) {
|
|
|
|
case OSMO_SS7_ASP_PROT_SUA:
|
|
|
|
msgb_sctp_ppid(msg) = SUA_PPID;
|
|
|
|
break;
|
|
|
|
case OSMO_SS7_ASP_PROT_M3UA:
|
|
|
|
msgb_sctp_ppid(msg) = M3UA_PPID;
|
|
|
|
break;
|
2017-04-02 09:58:17 +00:00
|
|
|
case OSMO_SS7_ASP_PROT_IPA:
|
|
|
|
break;
|
2017-04-03 15:37:10 +00:00
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
2017-04-07 17:21:35 +00:00
|
|
|
if (asp->cfg.is_server) {
|
|
|
|
if (!asp->server) {
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, no asp->server\n");
|
|
|
|
/* FIXME: what to do here? delete the route? send DUNA? */
|
2017-04-09 12:29:37 +00:00
|
|
|
msgb_free(msg);
|
2017-04-07 17:21:35 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
2017-04-03 15:37:10 +00:00
|
|
|
osmo_stream_srv_send(asp->server, msg);
|
2017-04-07 17:21:35 +00:00
|
|
|
} else {
|
|
|
|
if (!asp->client) {
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, no asp->client\n");
|
|
|
|
/* FIXME: what to do here? delete the route? send DUNA? */
|
2017-04-09 12:29:37 +00:00
|
|
|
msgb_free(msg);
|
2017-04-07 17:21:35 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
2019-09-04 15:12:00 +00:00
|
|
|
if (!osmo_stream_cli_is_connected(asp->client)) {
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, asp->client not connected\n");
|
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
2017-04-03 15:37:10 +00:00
|
|
|
osmo_stream_cli_send(asp->client, msg);
|
2017-04-07 17:21:35 +00:00
|
|
|
}
|
2017-04-03 15:37:10 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-15 20:39:05 +00:00
|
|
|
void osmo_ss7_asp_disconnect(struct osmo_ss7_asp *asp)
|
|
|
|
{
|
|
|
|
if (asp->server)
|
|
|
|
osmo_stream_srv_destroy(asp->server);
|
|
|
|
/* the close_cb() will handle the remaining cleanup here */
|
|
|
|
else if (asp->client)
|
|
|
|
xua_cli_close_and_reconnect(asp->client);
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* SS7 xUA Server
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
struct osmo_xua_server *
|
|
|
|
osmo_ss7_xua_server_find(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto,
|
|
|
|
uint16_t local_port)
|
|
|
|
{
|
|
|
|
struct osmo_xua_server *xs;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
2017-04-14 20:16:53 +00:00
|
|
|
llist_for_each_entry(xs, &inst->xua_servers, list) {
|
2017-04-03 15:37:10 +00:00
|
|
|
if (proto == xs->cfg.proto &&
|
|
|
|
local_port == xs->cfg.local.port)
|
|
|
|
return xs;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
ss7: Re-bind xUA server socket after setting new IP
In osmo-stp, cmd "local-ip" inside node "listen m3ua 2905" was actually
not being applied, because the server was created + bound at "listen" command
time using NULL as IP, and at "local-ip" time the IP was changed but the
server was not re-bound using the new IP, so it kept listening at
0.0.0.0.
With this patch, we defer binding the socket to "local-ip" cmd time,
after the IP has been applied.
As a result, if no "local-ip" command is provided, then the bind never
happens, which means it is now mandatory that users of osmo_ss7_xua_server_create
API not using osmo_ss7_xua_server_set_local_host call new provided API
osmo_ss7_xua_server_bind. Another new API osmo_ss7_bind_all_instances is
provided to easily make sure all servers are bound after configuration
process. This is specially important for servers which doesn't contain
the "local-ip" parameter.
Users of osmo_sccp_simple_server API are not affected by this change,
and they not requrie to call any new API.
Furthermore, using osmo_ss7_xua_server_bind in VTY code ensures the xUA
server is automatically bound to the new address if the operator changes
the "local-ip" cmd at runtime.
Related: OS#2647
Change-Id: I79738963d633bec70705ff159c5b2127cd498aa2
2017-11-17 11:57:25 +00:00
|
|
|
/*! \brief create a new xUA server configured with given ip/port
|
2017-04-03 15:37:10 +00:00
|
|
|
* \param[in] ctx talloc allocation context
|
|
|
|
* \param[in] proto protocol (xUA variant) to use
|
|
|
|
* \param[in] local_port local SCTP port to bind/listen to
|
|
|
|
* \param[in] local_host local IP address to bind/listen to (optional)
|
|
|
|
* \returns callee-allocated \ref osmo_xua_server in case of success
|
|
|
|
*/
|
|
|
|
struct osmo_xua_server *
|
|
|
|
osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto,
|
|
|
|
uint16_t local_port, const char *local_host)
|
|
|
|
{
|
|
|
|
struct osmo_xua_server *oxs = talloc_zero(inst, struct osmo_xua_server);
|
|
|
|
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
if (!oxs)
|
|
|
|
return NULL;
|
|
|
|
|
2017-04-02 09:58:17 +00:00
|
|
|
LOGP(DLSS7, LOGL_INFO, "Creating %s Server %s:%u\n",
|
|
|
|
get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-04-14 20:24:15 +00:00
|
|
|
INIT_LLIST_HEAD(&oxs->asp_list);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
oxs->cfg.proto = proto;
|
|
|
|
oxs->cfg.local.port = local_port;
|
|
|
|
|
|
|
|
oxs->server = osmo_stream_srv_link_create(oxs);
|
|
|
|
osmo_stream_srv_link_set_data(oxs->server, oxs);
|
|
|
|
osmo_stream_srv_link_set_accept_cb(oxs->server, xua_accept_cb);
|
|
|
|
|
2017-04-11 16:35:06 +00:00
|
|
|
osmo_stream_srv_link_set_nodelay(oxs->server, true);
|
2017-04-03 15:37:10 +00:00
|
|
|
osmo_stream_srv_link_set_port(oxs->server, oxs->cfg.local.port);
|
2017-04-02 09:58:17 +00:00
|
|
|
osmo_stream_srv_link_set_proto(oxs->server, asp_proto_to_ip_proto(proto));
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2019-10-11 15:58:07 +00:00
|
|
|
osmo_ss7_xua_server_set_local_host(oxs, local_host);
|
|
|
|
|
2018-11-09 14:44:40 +00:00
|
|
|
LOGP(DLSS7, LOGL_INFO, "Created %s server on %s:%" PRIu16 "\n",
|
|
|
|
get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
oxs->inst = inst;
|
2017-04-14 20:16:53 +00:00
|
|
|
llist_add_tail(&oxs->list, &inst->xua_servers);
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2017-04-17 08:42:30 +00:00
|
|
|
/* The SUA code internally needs SCCP to work */
|
2018-09-26 23:21:45 +00:00
|
|
|
if (proto == OSMO_SS7_ASP_PROT_SUA)
|
|
|
|
osmo_ss7_ensure_sccp(inst);
|
2017-04-17 08:42:30 +00:00
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
return oxs;
|
|
|
|
}
|
|
|
|
|
ss7: Re-bind xUA server socket after setting new IP
In osmo-stp, cmd "local-ip" inside node "listen m3ua 2905" was actually
not being applied, because the server was created + bound at "listen" command
time using NULL as IP, and at "local-ip" time the IP was changed but the
server was not re-bound using the new IP, so it kept listening at
0.0.0.0.
With this patch, we defer binding the socket to "local-ip" cmd time,
after the IP has been applied.
As a result, if no "local-ip" command is provided, then the bind never
happens, which means it is now mandatory that users of osmo_ss7_xua_server_create
API not using osmo_ss7_xua_server_set_local_host call new provided API
osmo_ss7_xua_server_bind. Another new API osmo_ss7_bind_all_instances is
provided to easily make sure all servers are bound after configuration
process. This is specially important for servers which doesn't contain
the "local-ip" parameter.
Users of osmo_sccp_simple_server API are not affected by this change,
and they not requrie to call any new API.
Furthermore, using osmo_ss7_xua_server_bind in VTY code ensures the xUA
server is automatically bound to the new address if the operator changes
the "local-ip" cmd at runtime.
Related: OS#2647
Change-Id: I79738963d633bec70705ff159c5b2127cd498aa2
2017-11-17 11:57:25 +00:00
|
|
|
/*! \brief Set the xUA server to bind/listen to the currently configured ip/port
|
|
|
|
* \param[in] xs xUA server to operate
|
|
|
|
* \returns 0 on success, negative value on error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
osmo_ss7_xua_server_bind(struct osmo_xua_server *xs)
|
|
|
|
{
|
2019-10-11 15:58:07 +00:00
|
|
|
char buf[512];
|
|
|
|
int rc;
|
|
|
|
const char *proto = get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto);
|
|
|
|
|
|
|
|
rc = osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &xs->cfg.local);
|
|
|
|
if (rc < 0) {
|
|
|
|
LOGP(DLSS7, LOGL_INFO, "Failed parsing %s Server osmo_ss7_asp_peer\n", proto);
|
|
|
|
} else {
|
|
|
|
LOGP(DLSS7, LOGL_INFO, "(Re)binding %s Server to %s\n",
|
|
|
|
proto, buf);
|
|
|
|
}
|
ss7: Re-bind xUA server socket after setting new IP
In osmo-stp, cmd "local-ip" inside node "listen m3ua 2905" was actually
not being applied, because the server was created + bound at "listen" command
time using NULL as IP, and at "local-ip" time the IP was changed but the
server was not re-bound using the new IP, so it kept listening at
0.0.0.0.
With this patch, we defer binding the socket to "local-ip" cmd time,
after the IP has been applied.
As a result, if no "local-ip" command is provided, then the bind never
happens, which means it is now mandatory that users of osmo_ss7_xua_server_create
API not using osmo_ss7_xua_server_set_local_host call new provided API
osmo_ss7_xua_server_bind. Another new API osmo_ss7_bind_all_instances is
provided to easily make sure all servers are bound after configuration
process. This is specially important for servers which doesn't contain
the "local-ip" parameter.
Users of osmo_sccp_simple_server API are not affected by this change,
and they not requrie to call any new API.
Furthermore, using osmo_ss7_xua_server_bind in VTY code ensures the xUA
server is automatically bound to the new address if the operator changes
the "local-ip" cmd at runtime.
Related: OS#2647
Change-Id: I79738963d633bec70705ff159c5b2127cd498aa2
2017-11-17 11:57:25 +00:00
|
|
|
return osmo_stream_srv_link_open(xs->server);
|
|
|
|
}
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
int
|
|
|
|
osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local_host)
|
|
|
|
{
|
2020-01-10 12:47:15 +00:00
|
|
|
return osmo_ss7_xua_server_set_local_hosts(xs, &local_host, 1);
|
2019-10-11 15:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt)
|
|
|
|
{
|
2020-01-10 13:14:41 +00:00
|
|
|
int rc;
|
2017-04-03 15:37:10 +00:00
|
|
|
OSMO_ASSERT(ss7_initialized);
|
|
|
|
|
2020-01-10 13:14:41 +00:00
|
|
|
rc = osmo_ss7_asp_peer_set_hosts(&xs->cfg.local, xs, local_hosts, local_host_cnt);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt);
|
2019-10-11 15:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host)
|
|
|
|
{
|
2020-01-10 13:14:41 +00:00
|
|
|
int rc;
|
2017-04-03 15:37:10 +00:00
|
|
|
|
2020-01-10 13:14:41 +00:00
|
|
|
rc = osmo_ss7_asp_peer_add_host(&xs->cfg.local, xs, local_host);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt);
|
2017-04-03 15:37:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void osmo_ss7_xua_server_destroy(struct osmo_xua_server *xs)
|
|
|
|
{
|
2017-04-14 20:24:15 +00:00
|
|
|
struct osmo_ss7_asp *asp, *asp2;
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
if (xs->server) {
|
|
|
|
osmo_stream_srv_link_close(xs->server);
|
|
|
|
osmo_stream_srv_link_destroy(xs->server);
|
|
|
|
}
|
2017-04-14 20:24:15 +00:00
|
|
|
/* iterate and close all connections established in relation
|
|
|
|
* with this server */
|
|
|
|
llist_for_each_entry_safe(asp, asp2, &xs->asp_list, siblings)
|
|
|
|
osmo_ss7_asp_destroy(asp);
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
llist_del(&xs->list);
|
|
|
|
talloc_free(xs);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool osmo_ss7_pc_is_local(struct osmo_ss7_instance *inst, uint32_t pc)
|
|
|
|
{
|
|
|
|
OSMO_ASSERT(ss7_initialized);
|
2017-07-26 15:31:53 +00:00
|
|
|
if (osmo_ss7_pc_is_valid(inst->cfg.primary_pc) && pc == inst->cfg.primary_pc)
|
2017-04-03 15:37:10 +00:00
|
|
|
return true;
|
|
|
|
/* FIXME: Secondary and Capability Point Codes */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int osmo_ss7_init(void)
|
|
|
|
{
|
2019-12-01 12:15:52 +00:00
|
|
|
int rc;
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
if (ss7_initialized)
|
|
|
|
return 1;
|
2019-12-01 12:15:52 +00:00
|
|
|
rc = osmo_fsm_register(&sccp_scoc_fsm);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = osmo_fsm_register(&xua_as_fsm);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = osmo_fsm_register(&xua_asp_fsm);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = osmo_fsm_register(&ipa_asp_fsm);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = osmo_fsm_register(&xua_default_lm_fsm);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
2017-04-03 15:37:10 +00:00
|
|
|
ss7_initialized = true;
|
|
|
|
return 0;
|
|
|
|
}
|
2017-04-05 20:32:13 +00:00
|
|
|
|
|
|
|
int osmo_ss7_tmode_to_xua(enum osmo_ss7_as_traffic_mode tmod)
|
|
|
|
{
|
|
|
|
switch (tmod) {
|
|
|
|
case OSMO_SS7_AS_TMOD_OVERRIDE:
|
|
|
|
return M3UA_TMOD_OVERRIDE;
|
|
|
|
case OSMO_SS7_AS_TMOD_LOADSHARE:
|
|
|
|
return M3UA_TMOD_LOADSHARE;
|
|
|
|
case OSMO_SS7_AS_TMOD_BCAST:
|
|
|
|
return M3UA_TMOD_BCAST;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum osmo_ss7_as_traffic_mode osmo_ss7_tmode_from_xua(uint32_t in)
|
|
|
|
{
|
|
|
|
switch (in) {
|
|
|
|
case M3UA_TMOD_OVERRIDE:
|
|
|
|
return OSMO_SS7_AS_TMOD_OVERRIDE;
|
|
|
|
case M3UA_TMOD_LOADSHARE:
|
|
|
|
return OSMO_SS7_AS_TMOD_LOADSHARE;
|
|
|
|
case M3UA_TMOD_BCAST:
|
|
|
|
return OSMO_SS7_AS_TMOD_BCAST;
|
2019-10-16 14:27:16 +00:00
|
|
|
default:
|
|
|
|
OSMO_ASSERT(false);
|
2017-04-05 20:32:13 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-26 09:32:50 +00:00
|
|
|
|
2019-10-22 18:24:14 +00:00
|
|
|
bool osmo_ss7_as_tmode_compatible_xua(struct osmo_ss7_as *as, uint32_t m3ua_tmt)
|
|
|
|
{
|
|
|
|
if (!as->cfg.mode_set_by_vty && !as->cfg.mode_set_by_peer)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
switch (m3ua_tmt) {
|
|
|
|
case M3UA_TMOD_OVERRIDE:
|
|
|
|
if (as->cfg.mode == OSMO_SS7_AS_TMOD_OVERRIDE)
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case M3UA_TMOD_LOADSHARE:
|
|
|
|
if (as->cfg.mode == OSMO_SS7_AS_TMOD_LOADSHARE ||
|
|
|
|
as->cfg.mode == OSMO_SS7_AS_TMOD_ROUNDROBIN)
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case M3UA_TMOD_BCAST:
|
|
|
|
if (as->cfg.mode == OSMO_SS7_AS_TMOD_BCAST)
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-26 09:32:50 +00:00
|
|
|
static osmo_ss7_asp_rx_unknown_cb *g_osmo_ss7_asp_rx_unknown_cb;
|
|
|
|
|
|
|
|
int ss7_asp_rx_unknown(struct osmo_ss7_asp *asp, int ppid_mux, struct msgb *msg)
|
|
|
|
{
|
|
|
|
if (g_osmo_ss7_asp_rx_unknown_cb)
|
|
|
|
return (*g_osmo_ss7_asp_rx_unknown_cb)(asp, ppid_mux, msg);
|
|
|
|
|
|
|
|
switch(asp->cfg.proto) {
|
|
|
|
case OSMO_SS7_ASP_PROT_IPA:
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "Rx IPA for unknown Stream ID 0x%02x: %s\n",
|
|
|
|
ppid_mux, msgb_hexdump(msg));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "Rx SCTP chunk for unknown PPID %u: %s\n",
|
|
|
|
ppid_mux, msgb_hexdump(msg));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-07 12:15:34 +00:00
|
|
|
/*! Get the logging subsystem for a given ASP. Used by generic code. */
|
|
|
|
int osmo_ss7_asp_get_log_subsys(const struct osmo_ss7_asp *asp)
|
|
|
|
{
|
|
|
|
switch (asp->cfg.proto) {
|
|
|
|
case OSMO_SS7_ASP_PROT_M3UA:
|
|
|
|
return DLM3UA;
|
|
|
|
case OSMO_SS7_ASP_PROT_SUA:
|
|
|
|
return DLSUA;
|
|
|
|
default:
|
|
|
|
return DLSS7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-26 09:32:50 +00:00
|
|
|
/*! Register a call-back function for unknown SCTP PPID / IPA Stream ID */
|
|
|
|
void osmo_ss7_register_rx_unknown_cb(osmo_ss7_asp_rx_unknown_cb *cb)
|
|
|
|
{
|
|
|
|
g_osmo_ss7_asp_rx_unknown_cb = cb;
|
|
|
|
}
|