From 80b135581909fef595d48436ab04dbcb147e3895 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Fri, 11 Oct 2019 17:58:07 +0200 Subject: [PATCH] ss7: Support multiple addresses in SCTP connections After this patch, Several "local-ip" and "remote-ip" lines are accepted under "listen" and "asp" VTY nodes, allowing to configure an SCTP connection with multiple connections, hence allowing control of SCTP multi-homing features. libosmo-sccp clients such as osmo-bsc and osmo-msc also gain support for this feature with this commit. Related: OS#3608 Depends: libosmocore.git Ic8681d9e093216c99c6bca4be81c31ef83688ed1 Depends: libosmo-netif.git I0fe62f518e195db4e34f3b0ad1762bb57ba9d92a Change-Id: Ibd15de7a4e00dbec78ff2e2dd6a686b0f3af22de --- TODO-RELEASE | 1 + include/osmocom/sigtran/osmo_ss7.h | 10 ++- src/osmo_ss7.c | 134 +++++++++++++++++++++++++---- src/osmo_ss7_vty.c | 53 ++++++++---- src/sccp_user.c | 10 ++- tests/vty/ss7_asp_test.vty | 28 +++--- 6 files changed, 184 insertions(+), 52 deletions(-) diff --git a/TODO-RELEASE b/TODO-RELEASE index d0852fc9..98b3b88a 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -7,3 +7,4 @@ # If any interfaces have been added since the last public release: c:r:a + 1. # If any interfaces have been removed or changed since the last public release: c:r:0. #library what description / commit summary line +libosmo-sigtran osmo_ss7_asp_peer ABI breakage (host is now an array of strings) diff --git a/include/osmocom/sigtran/osmo_ss7.h b/include/osmocom/sigtran/osmo_ss7.h index 9e8f2b3a..12aeea46 100644 --- a/include/osmocom/sigtran/osmo_ss7.h +++ b/include/osmocom/sigtran/osmo_ss7.h @@ -8,6 +8,7 @@ #include #include #include +#include extern struct llist_head osmo_ss7_instances; @@ -348,7 +349,8 @@ void osmo_ss7_asp_disconnect(struct osmo_ss7_asp *asp); ***********************************************************************/ struct osmo_ss7_asp_peer { - char *host; + char *host[OSMO_SOCK_MAX_ADDRS]; + size_t host_cnt; uint16_t port; }; @@ -409,6 +411,8 @@ struct osmo_ss7_asp { } cfg; }; +int osmo_ss7_asp_peer_snprintf(char* buf, size_t buf_len, struct osmo_ss7_asp_peer *peer); + struct osmo_ss7_asp * osmo_ss7_asp_find_by_name(struct osmo_ss7_instance *inst, const char *name); struct osmo_ss7_asp @@ -480,7 +484,9 @@ osmo_ss7_xua_server_bind(struct osmo_xua_server *xs); int osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local_host); - +int +osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt); +int osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host); void osmo_ss7_xua_server_destroy(struct osmo_xua_server *xs); struct osmo_sccp_instance * diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c index 9b51c29a..a0b931e9 100644 --- a/src/osmo_ss7.c +++ b/src/osmo_ss7.c @@ -1065,6 +1065,37 @@ bool osmo_ss7_as_has_asp(struct osmo_ss7_as *as, * SS7 Application Server Process ***********************************************************************/ +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; + + if (peer->host_cnt != 1) { + 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; +} + struct osmo_ss7_asp * osmo_ss7_asp_find_by_name(struct osmo_ss7_instance *inst, const char *name) { @@ -1103,6 +1134,7 @@ osmo_ss7_asp_find_by_socket_addr(int fd) char hostbuf_l[64], hostbuf_r[64]; uint16_t local_port, remote_port; int rc; + int i; OSMO_ASSERT(ss7_initialized); /* convert local and remote IP to string */ @@ -1129,11 +1161,26 @@ osmo_ss7_asp_find_by_socket_addr(int fd) llist_for_each_entry(inst, &osmo_ss7_instances, list) { struct osmo_ss7_asp *asp; llist_for_each_entry(asp, &inst->asp_list, list) { - if (asp->cfg.local.port == local_port && - (!asp->cfg.remote.port ||asp->cfg.remote.port == remote_port) && - (!asp->cfg.local.host || !strcmp(asp->cfg.local.host, hostbuf_l)) && - (!asp->cfg.remote.host || !strcmp(asp->cfg.remote.host, hostbuf_r))) - return asp; + 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++) { + if (!asp->cfg.local.host[i] || !strcmp(asp->cfg.local.host[i], hostbuf_l)) + break; + } + if (i == asp->cfg.local.host_cnt) + continue; /* didn't match any local.host */ + + for (i = 0; i < asp->cfg.remote.host_cnt; i++) { + if (!asp->cfg.remote.host[i] || !strcmp(asp->cfg.remote.host[i], hostbuf_r)) + break; + } + if (i == asp->cfg.remote.host_cnt) + continue; /* didn't match any remote.host */ + + return asp; } } @@ -1250,9 +1297,9 @@ int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp) return -1; } osmo_stream_cli_set_nodelay(asp->client, true); - osmo_stream_cli_set_addr(asp->client, asp->cfg.remote.host); + osmo_stream_cli_set_addrs(asp->client, (const char**)asp->cfg.remote.host, asp->cfg.remote.host_cnt); osmo_stream_cli_set_port(asp->client, asp->cfg.remote.port); - osmo_stream_cli_set_local_addr(asp->client, asp->cfg.local.host); + osmo_stream_cli_set_local_addrs(asp->client, (const char**)asp->cfg.local.host, asp->cfg.local.host_cnt); osmo_stream_cli_set_local_port(asp->client, asp->cfg.local.port); osmo_stream_cli_set_proto(asp->client, asp_proto_to_ip_proto(asp->cfg.proto)); osmo_stream_cli_set_reconnect_timeout(asp->client, 5); @@ -1699,8 +1746,11 @@ static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd) LOGP(DLSS7, LOGL_INFO, "%s: created dynamicASP %s\n", sock_name, asp->cfg.name); asp->cfg.is_server = true; + asp->cfg.local.host[0] = NULL; + asp->cfg.remote.host_cnt = 1; asp->cfg.remote.port = atoi(portbuf); - asp->cfg.remote.host = talloc_strdup(asp, hostbuf); + asp->cfg.remote.host[0] = talloc_strdup(asp, hostbuf); + asp->cfg.remote.host_cnt = 1; asp->dyn_allocated = true; asp->server = srv; osmo_ss7_asp_restart(asp); @@ -1832,17 +1882,17 @@ osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_pro oxs->cfg.proto = proto; oxs->cfg.local.port = local_port; - oxs->cfg.local.host = talloc_strdup(oxs, local_host); 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); osmo_stream_srv_link_set_nodelay(oxs->server, true); - osmo_stream_srv_link_set_addr(oxs->server, oxs->cfg.local.host); osmo_stream_srv_link_set_port(oxs->server, oxs->cfg.local.port); osmo_stream_srv_link_set_proto(oxs->server, asp_proto_to_ip_proto(proto)); + osmo_ss7_xua_server_set_local_host(oxs, local_host); + LOGP(DLSS7, LOGL_INFO, "Created %s server on %s:%" PRIu16 "\n", get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port); @@ -1863,20 +1913,74 @@ osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_pro int osmo_ss7_xua_server_bind(struct osmo_xua_server *xs) { - LOGP(DLSS7, LOGL_INFO, "(Re)binding %s Server to %s:%u\n", - get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto), - xs->cfg.local.host, xs->cfg.local.port); + 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); + } return osmo_stream_srv_link_open(xs->server); } int osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local_host) { + osmo_ss7_xua_server_set_local_hosts(xs, &local_host, 1); + return 0; +} + +int +osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt) +{ + int i = 0; OSMO_ASSERT(ss7_initialized); - osmo_talloc_replace_string(xs, &xs->cfg.local.host, local_host); - osmo_stream_srv_link_set_addr(xs->server, xs->cfg.local.host); + if (local_host_cnt > ARRAY_SIZE(xs->cfg.local.host)) + return -EINVAL; + for (; i < local_host_cnt; i++) + osmo_talloc_replace_string(xs, &xs->cfg.local.host[i], local_hosts[i]); + for (; i < xs->cfg.local.host_cnt; i++) { + talloc_free(xs->cfg.local.host[i]); + xs->cfg.local.host[i] = NULL; + } + + xs->cfg.local.host_cnt = local_host_cnt; + + osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt); + + return 0; +} + +int +osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host) +{ + int i; + bool new_is_any = !local_host || !strcmp(local_host, "0.0.0.0"); + bool iter_is_any; + + /* Makes no sense to have INET_ANY and specific addresses in the set */ + for (i = 0; i < xs->cfg.local.host_cnt; i++) { + iter_is_any = !xs->cfg.local.host[i] || + !strcmp(xs->cfg.local.host[i], "0.0.0.0"); + if (new_is_any && iter_is_any) + return -EINVAL; + if (!new_is_any && iter_is_any) + return -EINVAL; + } + /* Makes no sense to have INET_ANY many times */ + if (new_is_any && xs->cfg.local.host_cnt) + return -EINVAL; + + osmo_talloc_replace_string(xs, &xs->cfg.local.host[xs->cfg.local.host_cnt], local_host); + xs->cfg.local.host_cnt++; + + osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt); return 0; } diff --git a/src/osmo_ss7_vty.c b/src/osmo_ss7_vty.c index 85cc6955..8169aaf7 100644 --- a/src/osmo_ss7_vty.c +++ b/src/osmo_ss7_vty.c @@ -436,6 +436,8 @@ DEFUN(cs7_xua, cs7_xua_cmd, xs = osmo_ss7_xua_server_create(inst, proto, port, NULL); if (!xs) return CMD_SUCCESS; + /* Drop first dummy address created automatically by _create(): */ + osmo_ss7_xua_server_set_local_hosts(xs, NULL, 0); } vty->node = L_CS7_XUA_NODE; @@ -469,7 +471,7 @@ DEFUN(xua_local_ip, xua_local_ip_cmd, { struct osmo_xua_server *xs = vty->index; - osmo_ss7_xua_server_set_local_host(xs, argv[0]); + osmo_ss7_xua_server_add_local_host(xs, argv[0]); return CMD_SUCCESS; } @@ -492,21 +494,26 @@ DEFUN(xua_accept_dyn_asp, xua_accept_dyn_asp_cmd, static void write_one_xua(struct vty *vty, struct osmo_xua_server *xs) { + int i; vty_out(vty, " listen %s %u%s", get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto), xs->cfg.local.port, VTY_NEWLINE); - if (xs->cfg.local.host) - vty_out(vty, " local-ip %s%s", xs->cfg.local.host, VTY_NEWLINE); + + for (i = 0; i < xs->cfg.local.host_cnt; i++) { + if (xs->cfg.local.host) + vty_out(vty, " local-ip %s%s", xs->cfg.local.host[i], VTY_NEWLINE); + } if (xs->cfg.accept_dyn_reg) vty_out(vty, " accept-asp-connections dynamic-permitted%s", VTY_NEWLINE); } static void vty_dump_xua_server(struct vty *vty, struct osmo_xua_server *xs) { - vty_out(vty, "xUA server for %s on %s:%u%s", - get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto), - xs->cfg.local.host ? xs->cfg.local.host : "0.0.0.0", - xs->cfg.local.port, VTY_NEWLINE); + char buf[512]; + const char *proto = get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto); + if (osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &xs->cfg.local) < 0) + snprintf(buf, sizeof(buf), ""); + vty_out(vty, "xUA server for %s on %s%s", proto, buf, VTY_NEWLINE); } DEFUN(show_cs7_xua, show_cs7_xua_cmd, @@ -604,7 +611,8 @@ DEFUN(asp_local_ip, asp_local_ip_cmd, "Local IP Address from which to contact of ASP\n") { struct osmo_ss7_asp *asp = vty->index; - osmo_talloc_replace_string(asp, &asp->cfg.local.host, argv[0]); + osmo_talloc_replace_string(asp, &asp->cfg.local.host[asp->cfg.local.host_cnt], argv[0]); + asp->cfg.local.host_cnt++; return CMD_SUCCESS; } @@ -614,7 +622,8 @@ DEFUN(asp_remote_ip, asp_remote_ip_cmd, "Remote IP Address of ASP\n") { struct osmo_ss7_asp *asp = vty->index; - osmo_talloc_replace_string(asp, &asp->cfg.remote.host, argv[0]); + osmo_talloc_replace_string(asp, &asp->cfg.remote.host[asp->cfg.remote.host_cnt], argv[0]); + asp->cfg.remote.host_cnt++; return CMD_SUCCESS; } @@ -652,6 +661,7 @@ DEFUN(show_cs7_asp, show_cs7_asp_cmd, { struct osmo_ss7_instance *inst; struct osmo_ss7_asp *asp; + char buf[512]; int id = atoi(argv[0]); inst = osmo_ss7_instance_find(id); @@ -661,21 +671,23 @@ DEFUN(show_cs7_asp, show_cs7_asp_cmd, } vty_out(vty, " Effect Primary%s", VTY_NEWLINE); - vty_out(vty, "ASP Name AS Name State Type Rmt Port Remote IP Addr SCTP%s", VTY_NEWLINE); - vty_out(vty, "------------ ------------ ------------- ---- -------- --------------- ----------%s", VTY_NEWLINE); + vty_out(vty, "ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP%s", VTY_NEWLINE); + vty_out(vty, "------------ ------------ ------------- ---- ----------------------- ----------%s", VTY_NEWLINE); llist_for_each_entry(asp, &inst->asp_list, list) { - vty_out(vty, "%-12s %-12s %-13s %-4s %-8u %-15s %-10s%s", + osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &asp->cfg.remote); + vty_out(vty, "%-12s %-12s %-13s %-4s %-14s %-10s%s", asp->cfg.name, "?", asp->fi? osmo_fsm_inst_state_name(asp->fi) : "uninitialized", get_value_string(osmo_ss7_asp_protocol_vals, asp->cfg.proto), - asp->cfg.remote.port, asp->cfg.remote.host, "", VTY_NEWLINE); + buf, "", VTY_NEWLINE); } return CMD_SUCCESS; } static void write_one_asp(struct vty *vty, struct osmo_ss7_asp *asp) { + int i; /* skip any dynamically created ASPs (e.g. auto-created at connect time) */ if (asp->dyn_allocated || asp->simple_client_allocated) return; @@ -685,10 +697,14 @@ static void write_one_asp(struct vty *vty, struct osmo_ss7_asp *asp) osmo_ss7_asp_protocol_name(asp->cfg.proto), VTY_NEWLINE); if (asp->cfg.description) vty_out(vty, " description %s%s", asp->cfg.description, VTY_NEWLINE); - if (asp->cfg.local.host) - vty_out(vty, " local-ip %s%s", asp->cfg.local.host, VTY_NEWLINE); - if (asp->cfg.remote.host) - vty_out(vty, " remote-ip %s%s", asp->cfg.remote.host, VTY_NEWLINE); + for (i = 0; i < asp->cfg.local.host_cnt; i++) { + if (asp->cfg.local.host) + vty_out(vty, " local-ip %s%s", asp->cfg.local.host[i], VTY_NEWLINE); + } + for (i = 0; i < asp->cfg.remote.host_cnt; i++) { + if (asp->cfg.remote.host) + vty_out(vty, " remote-ip %s%s", asp->cfg.remote.host[i], VTY_NEWLINE); + } if (asp->cfg.qos_class) vty_out(vty, " qos-class %u%s", asp->cfg.qos_class, VTY_NEWLINE); } @@ -1722,6 +1738,9 @@ int osmo_ss7_vty_go_parent(struct vty *vty) break; case L_CS7_XUA_NODE: oxs = vty->index; + /* If no local addr was set, or erased after _create(): */ + if (!oxs->cfg.local.host_cnt) + osmo_ss7_xua_server_set_local_host(oxs, NULL); if (osmo_ss7_xua_server_bind(oxs) < 0) vty_out(vty, "%% Unable to bind xUA server to IP(s)%s", VTY_NEWLINE); vty->node = L_CS7_NODE; diff --git a/src/sccp_user.c b/src/sccp_user.c index a1302d9d..4e4144e2 100644 --- a/src/sccp_user.c +++ b/src/sccp_user.c @@ -566,16 +566,18 @@ osmo_sccp_simple_client_on_ss7_id(void *ctx, uint32_t ss7_id, const char *name, goto out_rt; asp_created = true; - asp->cfg.local.host = NULL; - asp->cfg.remote.host = NULL; + asp->cfg.local.host[0] = NULL; + asp->cfg.remote.host[0] = NULL; if (default_local_ip) { - asp->cfg.local.host = + asp->cfg.local.host[0] = talloc_strdup(asp, default_local_ip); } if (default_remote_ip) { - asp->cfg.remote.host = + asp->cfg.remote.host[0] = talloc_strdup(asp, default_remote_ip); } + asp->cfg.local.host_cnt = 1; + asp->cfg.remote.host_cnt = 1; asp->simple_client_allocated = true; } else talloc_free(asp_name); diff --git a/tests/vty/ss7_asp_test.vty b/tests/vty/ss7_asp_test.vty index 1aa954ad..b502367e 100644 --- a/tests/vty/ss7_asp_test.vty +++ b/tests/vty/ss7_asp_test.vty @@ -231,9 +231,9 @@ ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.200 ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.100 ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp Effect Primary -ASP Name AS Name State Type Rmt Port Remote IP Addr SCTP ------------- ------------ ------------- ---- -------- --------------- ---------- -my-asp ? uninitialized m3ua 12345 127.0.0.200 +ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP +------------ ------------ ------------- ---- ----------------------- ---------- +my-asp ? uninitialized m3ua 127.0.0.200:12345 ss7_asp_vty_test(config-cs7-asp)# exit ss7_asp_vty_test(config-cs7)# as my-ass m3ua @@ -294,25 +294,25 @@ ss7_asp_vty_test(config-cs7-as)# routing-key 0 3.2.1 ss7_asp_vty_test(config-cs7-as)# do show cs7 instance 0 asp Effect Primary -ASP Name AS Name State Type Rmt Port Remote IP Addr SCTP ------------- ------------ ------------- ---- -------- --------------- ---------- -my-asp ? ASP_DOWN m3ua 12345 127.0.0.200 +ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP +------------ ------------ ------------- ---- ----------------------- ---------- +my-asp ? ASP_DOWN m3ua 127.0.0.200:12345 ss7_asp_vty_test(config-cs7-as)# exit ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 asp Effect Primary -ASP Name AS Name State Type Rmt Port Remote IP Addr SCTP ------------- ------------ ------------- ---- -------- --------------- ---------- -my-asp ? ASP_DOWN m3ua 12345 127.0.0.200 +ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP +------------ ------------ ------------- ---- ----------------------- ---------- +my-asp ? ASP_DOWN m3ua 127.0.0.200:12345 ss7_asp_vty_test(config-cs7)# exit ss7_asp_vty_test(config)# do show cs7 instance 0 asp Effect Primary -ASP Name AS Name State Type Rmt Port Remote IP Addr SCTP ------------- ------------ ------------- ---- -------- --------------- ---------- -my-asp ? ASP_DOWN m3ua 12345 127.0.0.200 +ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP +------------ ------------ ------------- ---- ----------------------- ---------- +my-asp ? ASP_DOWN m3ua 127.0.0.200:12345 ss7_asp_vty_test(config)# do show cs7 instance 0 as all Routing Routing Key Cic Cic @@ -352,8 +352,8 @@ ss7_asp_vty_test(config-cs7)# no asp my-asp ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 asp Effect Primary -ASP Name AS Name State Type Rmt Port Remote IP Addr SCTP ------------- ------------ ------------- ---- -------- --------------- ---------- +ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP +------------ ------------ ------------- ---- ----------------------- ---------- ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 as all Routing Routing Key Cic Cic