From db07a4498868a9ae219d9370861066630aaf5fe9 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Sun, 6 Jun 2021 18:58:01 +0200 Subject: [PATCH] gprs_ns2_sns: implement outbound SNS DEL procedures When removing a bind the remote side needs to be informed via the SNS DELETE procedure. Related: OS#5036 Change-Id: I53cd54dfd262c70c425c3f13dad3b29526daa523 --- src/gb/gprs_ns2_sns.c | 104 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 9 deletions(-) diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c index 0cb24c564..3eff0f3a0 100644 --- a/src/gb/gprs_ns2_sns.c +++ b/src/gb/gprs_ns2_sns.c @@ -150,6 +150,9 @@ struct ns2_sns_state { /* prevent recursive reselection */ bool reselection_running; + /* protection against recursive free() */ + bool block_no_nsvc_events; + /* The current initial SNS endpoints. * The initial connection will be moved into the NSE * if configured via SNS. Otherwise it will be removed @@ -323,7 +326,8 @@ void ns2_sns_replace_nsvc(struct gprs_ns2_vc *nsvc) } } - osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_NO_NSVC, NULL); + if (gss->block_no_nsvc_events) + osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_NO_NSVC, NULL); } static void ns2_clear_elems(struct ns2_sns_elems *elems) @@ -595,6 +599,32 @@ static int update_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elem return -1; } +static int remove_bind_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems, struct ns2_sns_bind *sbind) +{ + struct gprs_ns_ie_ip4_elem ip4; + struct gprs_ns_ie_ip6_elem ip6; + const struct osmo_sockaddr *saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind); + + switch (saddr->u.sa.sa_family) { + case AF_INET: + ip4.ip_addr = saddr->u.sin.sin_addr.s_addr; + ip4.udp_port = saddr->u.sin.sin_port; + ip4.sig_weight = sbind->bind->sns_sig_weight; + ip4.data_weight = sbind->bind->sns_data_weight; + return remove_ip4_elem(gss, elems, &ip4); + case AF_INET6: + memcpy(&ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr)); + ip6.udp_port = saddr->u.sin.sin_port; + ip6.sig_weight = sbind->bind->sns_sig_weight; + ip6.data_weight = sbind->bind->sns_data_weight; + return remove_ip6_elem(gss, elems, &ip6); + default: + return -1; + } + + return -1; +} + static int do_sns_change_weight(struct osmo_fsm_inst *fi, const struct gprs_ns_ie_ip4_elem *ip4, const struct gprs_ns_ie_ip6_elem *ip6) { struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv; @@ -1487,6 +1517,12 @@ static void ns2_sns_st_local_procedure_onenter(struct osmo_fsm_inst *fi, uint32_ else ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1); break; + case SNS_PROC_DEL: + if (gss->family == AF_INET) + ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0); + else + ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1); + break; default: break; } @@ -1599,8 +1635,11 @@ static void ns2_sns_st_local_procedure(struct osmo_fsm_inst *fi, uint32_t event, add_ip6_elem(gss, &gss->local, &gss->current_procedure->ip6); break; } - create_nsvc_for_new_sbind(gss, gss->current_procedure->sbind); - gprs_ns2_start_alive_all_nsvcs(nse); + /* the sbind can be NULL if the bind has been released by del_bind */ + if (gss->current_procedure->sbind) { + create_nsvc_for_new_sbind(gss, gss->current_procedure->sbind); + gprs_ns2_start_alive_all_nsvcs(nse); + } break; case SNS_PROC_CHANGE_WEIGHT: switch (gss->family) { @@ -1633,6 +1672,16 @@ static void ns2_sns_st_local_procedure(struct osmo_fsm_inst *fi, uint32_t event, OSMO_ASSERT(0); } break; + case SNS_PROC_DEL: + switch (gss->family) { + case AF_INET: + remove_ip4_elem(gss, &gss->local, &gss->current_procedure->ip4); + break; + case AF_INET6: + remove_ip6_elem(gss, &gss->local, &gss->current_procedure->ip6); + break; + } + break; default: break; } @@ -1849,6 +1898,8 @@ static void ns2_add_procedure(struct ns2_sns_state *gss, struct ns2_sns_bind *sb switch (procedure_type) { case SNS_PROC_ADD: break; + case SNS_PROC_DEL: + break; case SNS_PROC_CHANGE_WEIGHT: llist_for_each_entry(procedure, &gss->procedures, list) { if (procedure->sbind == sbind && procedure->procedure == procedure_type && @@ -1879,8 +1930,16 @@ static void ns2_add_procedure(struct ns2_sns_state *gss, struct ns2_sns_bind *sb if (!procedure) return; + switch (procedure_type) { + case SNS_PROC_ADD: + case SNS_PROC_CHANGE_WEIGHT: + procedure->sbind = sbind; + break; + default: + break; + } + llist_add_tail(&procedure->list, &gss->procedures); - procedure->sbind = sbind; procedure->procedure = procedure_type; procedure->sig_weight = sbind->bind->sns_sig_weight; procedure->data_weight = sbind->bind->sns_data_weight; @@ -1893,7 +1952,6 @@ static void ns2_add_procedure(struct ns2_sns_state *gss, struct ns2_sns_bind *sb procedure->ip4.data_weight = sbind->bind->sns_data_weight; break; case AF_INET6: - memcpy(&procedure->ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr)); procedure->ip6.udp_port = saddr->u.sin.sin_port; procedure->ip6.sig_weight = sbind->bind->sns_sig_weight; @@ -1948,6 +2006,7 @@ static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv; struct ns2_sns_bind *sbind; struct gprs_ns2_vc *nsvc, *nsvc2; + struct ns2_sns_procedure *procedure; switch (event) { case NS2_SNS_EV_REQ_ADD_BIND: @@ -2020,25 +2079,46 @@ static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void case GPRS_SNS_ST_UNCONFIGURED: break; case GPRS_SNS_ST_BSS_SIZE: - /* TODO: remove the ip4 element from the list */ llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) { if (nsvc->bind == sbind->bind) { gprs_ns2_free_nsvc(nsvc); } } + osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL); break; case GPRS_SNS_ST_BSS_CONFIG_BSS: case GPRS_SNS_ST_BSS_CONFIG_SGSN: case GPRS_SNS_ST_CONFIGURED: - /* TODO: do an delete SNS-IP procedure */ - /* TODO: remove the ip4 element to the list */ + case GPRS_SNS_ST_LOCAL_PROCEDURE: + remove_bind_elem(gss, &gss->local_procedure, sbind); + if (ip46_weight_sum(&gss->local_procedure, true) == 0 || + ip46_weight_sum(&gss->local_procedure, false) == 0) { + LOGPFSML(fi, LOGL_ERROR, "NSE %d: weight has become invalid because of removing bind %s. Resetting the configuration\n", + nse->nsei, sbind->bind->name); + sns_failed(fi, NULL); + break; + } + gss->block_no_nsvc_events = true; llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) { if (nsvc->bind == sbind->bind) { gprs_ns2_free_nsvc(nsvc); } } + gss->block_no_nsvc_events = false; + if (nse->sum_sig_weight == 0 || !nse->alive || !gss->alive) { + sns_failed(fi, "While deleting a bind the current state became invalid (no signalling weight)"); + break; + } + + /* ensure other procedures doesn't use the sbind */ + llist_for_each_entry(procedure, &gss->procedures, list) { + if (procedure->sbind == sbind) + procedure->sbind = NULL; + } + ns2_add_procedure(gss, sbind, SNS_PROC_DEL); break; } + /* if this is the last bind, the free_nsvc() will trigger a reselection */ talloc_free(sbind); break; @@ -2124,7 +2204,7 @@ static void ns2_sns_st_all_action_bss(struct osmo_fsm_inst *fi, uint32_t event, switch (event) { case NS2_SNS_EV_REQ_NO_NSVC: /* ignore reselection running */ - if (gss->reselection_running) + if (gss->reselection_running || gss->block_no_nsvc_events) break; sns_failed(fi, "no remaining NSVC, resetting SNS FSM"); @@ -2134,6 +2214,9 @@ static void ns2_sns_st_all_action_bss(struct osmo_fsm_inst *fi, uint32_t event, /* TODO: keep the order of binds when data == GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER */ /* tear down previous state * gprs_ns2_free_nsvcs() will trigger NO_NSVC, prevent this from triggering a reselection */ + if (gss->reselection_running || gss->block_no_nsvc_events) + break; + gss->reselection_running = true; ns2_free_nsvcs(nse); ns2_clear_elems(&gss->local); @@ -2512,6 +2595,9 @@ void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bo if (nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED && nse->bss_sns_fi->state != GPRS_SNS_ST_LOCAL_PROCEDURE) return; + if (gss->block_no_nsvc_events) + return; + if (alive == gss->alive) return;