mirror of https://gerrit.osmocom.org/libosmocore
gprs_ns2_sns: implement local change weight procedure
When changing the bind ip-sns weight, initiate a SNS CHANGE WEIGHT procedure to inform the other side. Related: OS#5036 Change-Id: Icec4dabb46bc198f68f91bfe09ba279fbe68d454
This commit is contained in:
parent
f074659520
commit
1f3193d344
|
@ -1440,6 +1440,7 @@ struct gprs_ns2_inst *gprs_ns2_instantiate(void *ctx, osmo_prim_cb cb, void *cb_
|
|||
nsi->timeout[NS_TOUT_TSNS_PROV] = 3; /* 1..10 */
|
||||
nsi->timeout[NS_TOUT_TSNS_SIZE_RETRIES] = 3;
|
||||
nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES] = 3;
|
||||
nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES] = 3;
|
||||
|
||||
return nsi;
|
||||
}
|
||||
|
|
|
@ -60,8 +60,8 @@ struct vty;
|
|||
struct gprs_ns2_vc_driver;
|
||||
struct gprs_ns2_vc_bind;
|
||||
|
||||
#define NS_TIMERS_COUNT 10
|
||||
#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries)"
|
||||
#define NS_TIMERS_COUNT 11
|
||||
#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries|tsns-procedures-retries)"
|
||||
#define NS_TIMERS_HELP \
|
||||
"(un)blocking Timer (Tns-block) timeout\n" \
|
||||
"(un)blocking Timer (Tns-block) number of retries\n" \
|
||||
|
@ -73,6 +73,7 @@ struct gprs_ns2_vc_bind;
|
|||
"SNS Provision Timer (Tsns-prov) timeout\n" \
|
||||
"SNS Size number of retries\n" \
|
||||
"SNS Config number of retries\n" \
|
||||
"SNS Procedures number of retries\n" \
|
||||
|
||||
/* Educated guess - LLC user payload is 1500 bytes plus possible headers */
|
||||
#define NS_ALLOC_SIZE 3072
|
||||
|
@ -89,6 +90,7 @@ enum ns2_timeout {
|
|||
NS_TOUT_TSNS_PROV,
|
||||
NS_TOUT_TSNS_SIZE_RETRIES,
|
||||
NS_TOUT_TSNS_CONFIG_RETRIES,
|
||||
NS_TOUT_TSNS_PROCEDURES_RETRIES,
|
||||
};
|
||||
|
||||
enum nsvc_timer_mode {
|
||||
|
@ -336,6 +338,7 @@ enum ns2_sns_event {
|
|||
NS2_SNS_EV_REQ_NSVC_ALIVE, /*!< a NS-VC became alive */
|
||||
NS2_SNS_EV_REQ_ADD_BIND, /*!< add a new local bind to this NSE */
|
||||
NS2_SNS_EV_REQ_DELETE_BIND, /*!< remove a local bind from this NSE */
|
||||
NS2_SNS_EV_REQ_CHANGE_WEIGHT, /*!< a bind changed its weight */
|
||||
};
|
||||
|
||||
enum ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
|
||||
|
|
|
@ -70,6 +70,7 @@ enum gprs_sns_bss_state {
|
|||
GPRS_SNS_ST_CONFIGURED,
|
||||
GPRS_SNS_ST_SGSN_WAIT_CONFIG, /* !< SGSN role: Wait for CONFIG from BSS */
|
||||
GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, /* !< SGSN role: Wait for CONFIG-ACK from BSS */
|
||||
GPRS_SNS_ST_LOCAL_PROCEDURE, /*!< in process of a ADD/DEL/CHANGE procedure towards SGSN (BSS->SGSN) */
|
||||
};
|
||||
|
||||
static const struct value_string gprs_sns_event_names[] = {
|
||||
|
@ -88,9 +89,17 @@ static const struct value_string gprs_sns_event_names[] = {
|
|||
{ NS2_SNS_EV_REQ_NSVC_ALIVE, "REQ_NSVC_ALIVE"},
|
||||
{ NS2_SNS_EV_REQ_ADD_BIND, "REQ_ADD_BIND"},
|
||||
{ NS2_SNS_EV_REQ_DELETE_BIND, "REQ_DELETE_BIND"},
|
||||
{ NS2_SNS_EV_REQ_CHANGE_WEIGHT, "REQ_CHANGE_WEIGHT"},
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum sns_procedure {
|
||||
SNS_PROC_NONE, /*!< used as invalid/idle value */
|
||||
SNS_PROC_ADD,
|
||||
SNS_PROC_DEL,
|
||||
SNS_PROC_CHANGE_WEIGHT,
|
||||
};
|
||||
|
||||
struct sns_endpoint {
|
||||
struct llist_head list;
|
||||
struct osmo_sockaddr saddr;
|
||||
|
@ -99,6 +108,21 @@ struct sns_endpoint {
|
|||
struct ns2_sns_bind {
|
||||
struct llist_head list;
|
||||
struct gprs_ns2_vc_bind *bind;
|
||||
uint8_t change_weight_state;
|
||||
};
|
||||
|
||||
struct ns2_sns_procedure {
|
||||
struct llist_head list;
|
||||
struct ns2_sns_bind *sbind;
|
||||
uint16_t sig_weight;
|
||||
uint16_t data_weight;
|
||||
/* copy entry to protect against changes of gss->local */
|
||||
struct gprs_ns_ie_ip4_elem ip4;
|
||||
struct gprs_ns_ie_ip6_elem ip6;
|
||||
enum sns_procedure procedure;
|
||||
uint8_t trans_id;
|
||||
/* is the procedure in process */
|
||||
bool running;
|
||||
};
|
||||
|
||||
struct ns2_sns_elems {
|
||||
|
@ -139,6 +163,9 @@ struct ns2_sns_state {
|
|||
/* local configuration to send to the remote end */
|
||||
struct ns2_sns_elems local;
|
||||
|
||||
/* local configuration after all local procedures applied */
|
||||
struct ns2_sns_elems local_procedure;
|
||||
|
||||
/* remote configuration as received */
|
||||
struct ns2_sns_elems remote;
|
||||
|
||||
|
@ -147,6 +174,10 @@ struct ns2_sns_state {
|
|||
size_t num_max_nsvcs;
|
||||
size_t num_max_ip4_remote;
|
||||
size_t num_max_ip6_remote;
|
||||
|
||||
struct llist_head procedures;
|
||||
struct ns2_sns_procedure *current_procedure;
|
||||
uint8_t trans_id;
|
||||
};
|
||||
|
||||
static inline struct gprs_ns2_nse *nse_inst_from_fi(struct osmo_fsm_inst *fi)
|
||||
|
@ -302,6 +333,16 @@ static void ns2_clear_elems(struct ns2_sns_elems *elems)
|
|||
elems->num_ip6 = 0;
|
||||
}
|
||||
|
||||
static void ns2_clear_procedures(struct ns2_sns_state *gss)
|
||||
{
|
||||
struct ns2_sns_procedure *procedure, *tmp;
|
||||
gss->current_procedure = NULL;
|
||||
llist_for_each_entry_safe(procedure, tmp, &gss->procedures, list) {
|
||||
llist_del(&procedure->list);
|
||||
talloc_free(procedure);
|
||||
}
|
||||
}
|
||||
|
||||
static void ns2_vc_create_ip(struct osmo_fsm_inst *fi, struct gprs_ns2_nse *nse, const struct osmo_sockaddr *remote,
|
||||
uint8_t sig_weight, uint8_t data_weight)
|
||||
{
|
||||
|
@ -777,6 +818,36 @@ static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, int ip_proto)
|
|||
return count;
|
||||
}
|
||||
|
||||
static int ns2_sns_copy_local_endpoints(struct ns2_sns_state *gss)
|
||||
{
|
||||
switch (gss->family) {
|
||||
case AF_INET:
|
||||
gss->local_procedure.ip4 = talloc_realloc(gss, gss->local_procedure.ip4, struct gprs_ns_ie_ip4_elem,
|
||||
gss->local.num_ip4);
|
||||
if (!gss->local_procedure.ip4)
|
||||
return -ENOMEM;
|
||||
|
||||
gss->local_procedure.num_ip4 = gss->local.num_ip4;
|
||||
memcpy(gss->local_procedure.ip4, gss->local.ip4,
|
||||
sizeof(struct gprs_ns_ie_ip4_elem) * gss->local.num_ip4);
|
||||
break;
|
||||
case AF_INET6:
|
||||
gss->local_procedure.ip6 = talloc_realloc(gss, gss->local_procedure.ip6, struct gprs_ns_ie_ip6_elem,
|
||||
gss->local.num_ip6);
|
||||
if (!gss->local_procedure.ip6)
|
||||
return -ENOMEM;
|
||||
|
||||
gss->local_procedure.num_ip6 = gss->local.num_ip6;
|
||||
memcpy(gss->local_procedure.ip6, gss->local.ip6,
|
||||
sizeof(struct gprs_ns_ie_ip6_elem) * gss->local.num_ip6);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
||||
|
@ -878,6 +949,8 @@ static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
|
|||
gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * gss->local.num_ip6, 8);
|
||||
break;
|
||||
}
|
||||
|
||||
ns2_sns_copy_local_endpoints(gss);
|
||||
}
|
||||
|
||||
static void ns2_sns_choose_next_bind(struct ns2_sns_state *gss)
|
||||
|
@ -902,6 +975,7 @@ static void ns2_sns_st_bss_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_s
|
|||
if (old_state != GPRS_SNS_ST_BSS_SIZE)
|
||||
gss->N = 0;
|
||||
|
||||
ns2_clear_procedures(gss);
|
||||
gss->alive = false;
|
||||
|
||||
ns2_sns_compute_local_ep_from_binds(fi);
|
||||
|
@ -1367,7 +1441,135 @@ static void ns2_sns_st_configured_onenter(struct osmo_fsm_inst *fi, uint32_t old
|
|||
if (gss->sns_nsvc->sns_only)
|
||||
gprs_ns2_free_nsvc(gss->sns_nsvc);
|
||||
|
||||
ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);
|
||||
if (old_state != GPRS_SNS_ST_LOCAL_PROCEDURE)
|
||||
ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);
|
||||
|
||||
if (!llist_empty(&gss->procedures)) {
|
||||
osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
|
||||
gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
|
||||
}
|
||||
}
|
||||
|
||||
static void ns2_sns_st_local_procedure_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
||||
{
|
||||
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
||||
|
||||
/* check if resend or not */
|
||||
if (!gss->current_procedure) {
|
||||
/* take next procedure */
|
||||
gss->current_procedure = llist_first_entry_or_null(&gss->procedures,
|
||||
struct ns2_sns_procedure, list);
|
||||
if (!gss->current_procedure) {
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);
|
||||
return;
|
||||
}
|
||||
gss->N = 0;
|
||||
gss->current_procedure->running = true;
|
||||
gss->current_procedure->trans_id = ++gss->trans_id;
|
||||
if (gss->trans_id == 0)
|
||||
gss->trans_id = gss->current_procedure->trans_id = 1;
|
||||
|
||||
}
|
||||
|
||||
/* also takes care of retransmitting */
|
||||
switch (gss->current_procedure->procedure) {
|
||||
case SNS_PROC_CHANGE_WEIGHT:
|
||||
if (gss->family == AF_INET)
|
||||
ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
|
||||
else
|
||||
ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ns2_sns_st_local_procedure(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
||||
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
||||
struct gprs_ns_ie_ip4_elem *ip4, *proc4;
|
||||
struct gprs_ns_ie_ip6_elem *ip6, *proc6;
|
||||
struct tlv_parsed *tp = data;
|
||||
uint8_t trans_id;
|
||||
uint8_t cause;
|
||||
|
||||
switch (event) {
|
||||
case NS2_SNS_EV_RX_ADD:
|
||||
ns2_sns_st_configured_add(fi, gss, tp);
|
||||
break;
|
||||
case NS2_SNS_EV_RX_DELETE:
|
||||
ns2_sns_st_configured_delete(fi, gss, tp);
|
||||
break;
|
||||
case NS2_SNS_EV_RX_CHANGE_WEIGHT:
|
||||
ns2_sns_st_configured_change(fi, gss, tp);
|
||||
break;
|
||||
case NS2_SNS_EV_RX_ACK:
|
||||
/* presence of trans_id is already checked here */
|
||||
trans_id = tlvp_val8(tp, NS_IE_TRANS_ID, 0);
|
||||
if (trans_id != gss->current_procedure->trans_id) {
|
||||
LOGPFSML(fi, LOGL_INFO, "NSEI=%u Rx SNS ACK with invalid transaction id %d. Valid %d\n",
|
||||
nse->nsei, trans_id, gss->current_procedure->trans_id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (TLVP_PRESENT(tp, NS_IE_CAUSE)) {
|
||||
/* what happend on error cause? return to size? */
|
||||
cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
|
||||
LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx SNS ACK trans %d with cause code %d.\n",
|
||||
nse->nsei, trans_id, cause);
|
||||
sns_failed(fi, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (gss->current_procedure->procedure) {
|
||||
case SNS_PROC_CHANGE_WEIGHT:
|
||||
switch (gss->family) {
|
||||
case AF_INET:
|
||||
proc4 = &gss->current_procedure->ip4;
|
||||
for (unsigned int i=0; i<gss->local.num_ip4; i++) {
|
||||
ip4 = &gss->local.ip4[i];
|
||||
if (ip4->ip_addr != proc4->ip_addr ||
|
||||
ip4->udp_port != proc4->udp_port)
|
||||
continue;
|
||||
ip4->sig_weight = proc4->sig_weight;
|
||||
ip4->data_weight = proc4->data_weight;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
proc6 = &gss->current_procedure->ip6;
|
||||
for (unsigned int i=0; i<gss->local.num_ip6; i++) {
|
||||
ip6 = &gss->local.ip6[i];
|
||||
if (memcmp(&ip6->ip_addr, &proc6->ip_addr, sizeof(proc6->ip_addr)) ||
|
||||
ip6->udp_port != proc6->udp_port) {
|
||||
continue;
|
||||
}
|
||||
ip6->sig_weight = proc6->sig_weight;
|
||||
ip6->data_weight = proc6->data_weight;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
llist_del(&gss->current_procedure->list);
|
||||
talloc_free(gss->current_procedure);
|
||||
gss->current_procedure = NULL;
|
||||
|
||||
if (llist_empty(&gss->procedures))
|
||||
osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_CONFIGURED,
|
||||
0, 0);
|
||||
else
|
||||
osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
|
||||
gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state ns2_sns_bss_states[] = {
|
||||
|
@ -1414,11 +1616,27 @@ static const struct osmo_fsm_state ns2_sns_bss_states[] = {
|
|||
S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
|
||||
S(NS2_SNS_EV_REQ_NSVC_ALIVE),
|
||||
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
||||
S(GPRS_SNS_ST_BSS_SIZE),
|
||||
S(GPRS_SNS_ST_BSS_SIZE) |
|
||||
S(GPRS_SNS_ST_LOCAL_PROCEDURE),
|
||||
.name = "CONFIGURED",
|
||||
.action = ns2_sns_st_configured,
|
||||
.onenter = ns2_sns_st_configured_onenter,
|
||||
},
|
||||
[GPRS_SNS_ST_LOCAL_PROCEDURE] = {
|
||||
.in_event_mask = S(NS2_SNS_EV_RX_ADD) |
|
||||
S(NS2_SNS_EV_RX_DELETE) |
|
||||
S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
|
||||
S(NS2_SNS_EV_RX_ACK) |
|
||||
S(NS2_SNS_EV_REQ_NSVC_ALIVE),
|
||||
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
||||
S(GPRS_SNS_ST_BSS_SIZE) |
|
||||
S(GPRS_SNS_ST_CONFIGURED) |
|
||||
S(GPRS_SNS_ST_LOCAL_PROCEDURE),
|
||||
.name = "LOCAL_PROCEDURE",
|
||||
.action = ns2_sns_st_local_procedure,
|
||||
.onenter = ns2_sns_st_local_procedure_onenter,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi)
|
||||
|
@ -1453,14 +1671,173 @@ static int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi)
|
|||
case 4:
|
||||
sns_failed(fi, "Config succeeded but no NS-VC came online. Selecting next IP-SNS endpoint.");
|
||||
break;
|
||||
case 5:
|
||||
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
|
||||
sns_failed(fi, "SNS Procedure retries failed.");
|
||||
} else {
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV], 5);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gprs_ns_ie_ip4_elem *ns2_get_sbind_ip4_entry(struct ns2_sns_state *gss,
|
||||
struct ns2_sns_bind *sbind,
|
||||
struct ns2_sns_elems *endpoints)
|
||||
{
|
||||
const struct osmo_sockaddr *addr;
|
||||
struct gprs_ns_ie_ip4_elem *ip4;
|
||||
|
||||
if (gss->family != AF_INET)
|
||||
return NULL;
|
||||
|
||||
addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
||||
if (addr->u.sa.sa_family != AF_INET)
|
||||
return NULL;
|
||||
|
||||
for (unsigned int i=0; i<endpoints->num_ip4; i++) {
|
||||
ip4 = &endpoints->ip4[i];
|
||||
if (ip4->ip_addr == addr->u.sin.sin_addr.s_addr &&
|
||||
ip4->udp_port == addr->u.sin.sin_port)
|
||||
return ip4;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct gprs_ns_ie_ip6_elem *ns2_get_sbind_ip6_entry(struct ns2_sns_state *gss,
|
||||
struct ns2_sns_bind *sbind,
|
||||
struct ns2_sns_elems *endpoints)
|
||||
{
|
||||
const struct osmo_sockaddr *addr;
|
||||
struct gprs_ns_ie_ip6_elem *ip6;
|
||||
|
||||
if (gss->family != AF_INET6)
|
||||
return NULL;
|
||||
|
||||
addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
||||
if (addr->u.sa.sa_family != AF_INET6)
|
||||
return NULL;
|
||||
|
||||
for (unsigned int i=0; i<endpoints->num_ip6; i++) {
|
||||
ip6 = &endpoints->ip6[i];
|
||||
if (memcmp(&ip6->ip_addr, &addr->u.sin6.sin6_addr, sizeof(ip6->ip_addr)) ||
|
||||
ip6->udp_port != addr->u.sin6.sin6_port)
|
||||
return ip6;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return != 0 if the resulting weight is invalid. return 1 if sbind doesn't have an entry */
|
||||
static int ns2_update_weight_entry(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
|
||||
struct ns2_sns_elems *endpoints)
|
||||
{
|
||||
struct gprs_ns_ie_ip4_elem *ip4;
|
||||
struct gprs_ns_ie_ip6_elem *ip6;
|
||||
|
||||
switch (gss->family) {
|
||||
case AF_INET:
|
||||
ip4 = ns2_get_sbind_ip4_entry(gss, sbind, endpoints);
|
||||
if (!ip4)
|
||||
return 1;
|
||||
ip4->sig_weight = sbind->bind->sns_sig_weight;
|
||||
ip4->data_weight = sbind->bind->sns_data_weight;
|
||||
return (ip4_weight_sum_sig(endpoints) != 0 && ip4_weight_sum_data(endpoints) != 0);
|
||||
break;
|
||||
case AF_INET6:
|
||||
ip6 = ns2_get_sbind_ip6_entry(gss, sbind, endpoints);
|
||||
if (!ip6)
|
||||
return 1;
|
||||
ip6->sig_weight = sbind->bind->sns_sig_weight;
|
||||
ip6->data_weight = sbind->bind->sns_data_weight;
|
||||
return (ip6_weight_sum_sig(endpoints) != 0 && ip6_weight_sum_data(endpoints) != 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void ns2_add_procedure(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
|
||||
enum sns_procedure procedure_type)
|
||||
{
|
||||
struct ns2_sns_procedure *procedure = NULL;
|
||||
const struct osmo_sockaddr *saddr;
|
||||
saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
||||
|
||||
if (saddr->u.sa.sa_family != gss->family)
|
||||
return;
|
||||
|
||||
switch (procedure_type) {
|
||||
case SNS_PROC_CHANGE_WEIGHT:
|
||||
llist_for_each_entry(procedure, &gss->procedures, list) {
|
||||
if (procedure->sbind == sbind && procedure->procedure == procedure_type &&
|
||||
!procedure->running) {
|
||||
switch(gss->family) {
|
||||
case AF_INET:
|
||||
/* merge it with a previous procedure */
|
||||
procedure->ip4.ip_addr = sbind->bind->sns_sig_weight;
|
||||
procedure->ip4.data_weight = sbind->bind->sns_data_weight;
|
||||
break;
|
||||
case AF_INET6:
|
||||
/* merge it with a previous procedure */
|
||||
procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;
|
||||
procedure->ip6.data_weight = sbind->bind->sns_data_weight;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
procedure = talloc_zero(gss, struct ns2_sns_procedure);
|
||||
if (!procedure)
|
||||
return;
|
||||
|
||||
procedure->sbind = sbind;
|
||||
procedure->procedure = procedure_type;
|
||||
procedure->sig_weight = sbind->bind->sns_sig_weight;
|
||||
procedure->data_weight = sbind->bind->sns_data_weight;
|
||||
|
||||
switch(gss->family) {
|
||||
case AF_INET:
|
||||
|
||||
procedure->ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
|
||||
procedure->ip4.udp_port = saddr->u.sin.sin_port;
|
||||
procedure->ip4.sig_weight = sbind->bind->sns_sig_weight;
|
||||
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;
|
||||
procedure->ip6.data_weight = sbind->bind->sns_data_weight;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
llist_add_tail(&procedure->list, &gss->procedures);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (gss->nse->bss_sns_fi->state == GPRS_SNS_ST_CONFIGURED) {
|
||||
osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
|
||||
gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* common allstate-action for both roles */
|
||||
static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
||||
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
||||
struct ns2_sns_bind *sbind;
|
||||
struct gprs_ns2_vc *nsvc, *nsvc2;
|
||||
|
||||
|
@ -1509,6 +1886,27 @@ static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
/* if this is the last bind, the free_nsvc() will trigger a reselection */
|
||||
talloc_free(sbind);
|
||||
break;
|
||||
case NS2_SNS_EV_REQ_CHANGE_WEIGHT:
|
||||
sbind = data;
|
||||
switch (fi->state) {
|
||||
case GPRS_SNS_ST_UNCONFIGURED:
|
||||
/* select_endpoint will check if this is a valid configuration */
|
||||
if (gss->role == GPRS_SNS_ROLE_BSS)
|
||||
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
||||
break;
|
||||
case GPRS_SNS_ST_BSS_SIZE:
|
||||
/* invalid weight? */
|
||||
if (!ns2_update_weight_entry(gss, sbind, &gss->local))
|
||||
sns_failed(fi, "updating weights results in an invalid configuration.");
|
||||
break;
|
||||
default:
|
||||
if (!ns2_update_weight_entry(gss, sbind, &gss->local_procedure)) {
|
||||
sns_failed(fi, "updating weights results in an invalid configuration.");
|
||||
break;
|
||||
}
|
||||
ns2_add_procedure(gss, sbind, SNS_PROC_CHANGE_WEIGHT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1618,6 +2016,7 @@ static struct osmo_fsm gprs_ns2_sns_bss_fsm = {
|
|||
S(NS2_SNS_EV_REQ_FREE_NSVCS) |
|
||||
S(NS2_SNS_EV_REQ_SELECT_ENDPOINT) |
|
||||
S(NS2_SNS_EV_REQ_ADD_BIND) |
|
||||
S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
|
||||
S(NS2_SNS_EV_REQ_DELETE_BIND),
|
||||
.allstate_action = ns2_sns_st_all_action_bss,
|
||||
.cleanup = NULL,
|
||||
|
@ -1653,6 +2052,7 @@ struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,
|
|||
gss->num_max_ip6_remote = 8192;
|
||||
INIT_LLIST_HEAD(&gss->sns_endpoints);
|
||||
INIT_LLIST_HEAD(&gss->binds);
|
||||
INIT_LLIST_HEAD(&gss->procedures);
|
||||
|
||||
return fi;
|
||||
err:
|
||||
|
@ -1952,7 +2352,7 @@ void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bo
|
|||
return;
|
||||
|
||||
gss = nse->bss_sns_fi->priv;
|
||||
if(nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED)
|
||||
if (nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED && nse->bss_sns_fi->state != GPRS_SNS_ST_LOCAL_PROCEDURE)
|
||||
return;
|
||||
|
||||
if (alive == gss->alive)
|
||||
|
@ -2052,7 +2452,26 @@ int gprs_ns2_sns_del_bind(struct gprs_ns2_nse *nse,
|
|||
*/
|
||||
void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind)
|
||||
{
|
||||
/* TODO: implement weights after binds per sns implemented */
|
||||
struct ns2_sns_bind *sbind;
|
||||
struct gprs_ns2_nse *nse;
|
||||
struct ns2_sns_state *gss;
|
||||
const struct osmo_sockaddr *addr = gprs_ns2_ip_bind_sockaddr(bind);
|
||||
|
||||
llist_for_each_entry(nse, &bind->nsi->nse, list) {
|
||||
if (!nse->bss_sns_fi)
|
||||
continue;
|
||||
|
||||
gss = nse->bss_sns_fi->priv;
|
||||
if (addr->u.sa.sa_family != gss->family)
|
||||
return;
|
||||
|
||||
llist_for_each_entry(sbind, &gss->binds, list) {
|
||||
if (sbind->bind == bind) {
|
||||
osmo_fsm_inst_dispatch(gss->nse->bss_sns_fi, NS2_SNS_EV_REQ_CHANGE_WEIGHT, sbind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2067,6 +2486,7 @@ static void ns2_clear_sgsn(struct ns2_sns_state *gss, struct gprs_ns2_vc *size_n
|
|||
{
|
||||
struct gprs_ns2_vc *nsvc, *nsvc2;
|
||||
|
||||
ns2_clear_procedures(gss);
|
||||
ns2_clear_elems(&gss->local);
|
||||
ns2_clear_elems(&gss->remote);
|
||||
llist_for_each_entry_safe(nsvc, nsvc2, &gss->nse->nsvc, list) {
|
||||
|
@ -2207,12 +2627,28 @@ static const struct osmo_fsm_state ns2_sns_sgsn_states[] = {
|
|||
S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
|
||||
S(NS2_SNS_EV_REQ_NSVC_ALIVE),
|
||||
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
||||
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG),
|
||||
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
|
||||
S(GPRS_SNS_ST_LOCAL_PROCEDURE),
|
||||
.name = "CONFIGURED",
|
||||
/* shared with BSS side; once configured there's no difference */
|
||||
.action = ns2_sns_st_configured,
|
||||
.onenter = ns2_sns_st_configured_onenter,
|
||||
},
|
||||
[GPRS_SNS_ST_LOCAL_PROCEDURE] = {
|
||||
.in_event_mask = S(NS2_SNS_EV_RX_ADD) |
|
||||
S(NS2_SNS_EV_RX_DELETE) |
|
||||
S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
|
||||
S(NS2_SNS_EV_RX_ACK) |
|
||||
S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
|
||||
S(NS2_SNS_EV_REQ_NSVC_ALIVE),
|
||||
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
||||
S(GPRS_SNS_ST_CONFIGURED) |
|
||||
S(GPRS_SNS_ST_LOCAL_PROCEDURE),
|
||||
.name = "LOCAL_PROCEDURE",
|
||||
/* shared with BSS side; once configured there's no difference */
|
||||
.action = ns2_sns_st_local_procedure,
|
||||
.onenter = ns2_sns_st_local_procedure_onenter,
|
||||
},
|
||||
};
|
||||
|
||||
static int ns2_sns_fsm_sgsn_timer_cb(struct osmo_fsm_inst *fi)
|
||||
|
@ -2234,11 +2670,19 @@ static int ns2_sns_fsm_sgsn_timer_cb(struct osmo_fsm_inst *fi)
|
|||
case 4:
|
||||
LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online.\n", nse->nsei);
|
||||
break;
|
||||
case 5:
|
||||
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES]) {
|
||||
sns_failed(fi, "SNS Procedure retries failed.");
|
||||
} else {
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV],
|
||||
fi->T);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* allstate-action for SGSN role */
|
||||
static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
|
@ -2349,6 +2793,7 @@ static struct osmo_fsm gprs_ns2_sns_sgsn_fsm = {
|
|||
S(NS2_SNS_EV_REQ_NO_NSVC) |
|
||||
S(NS2_SNS_EV_REQ_FREE_NSVCS) |
|
||||
S(NS2_SNS_EV_REQ_ADD_BIND) |
|
||||
S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
|
||||
S(NS2_SNS_EV_REQ_DELETE_BIND),
|
||||
.allstate_action = ns2_sns_st_all_action_sgsn,
|
||||
.cleanup = NULL,
|
||||
|
@ -2380,6 +2825,7 @@ struct osmo_fsm_inst *ns2_sns_sgsn_fsm_alloc(struct gprs_ns2_nse *nse, const cha
|
|||
gss->role = GPRS_SNS_ROLE_SGSN;
|
||||
INIT_LLIST_HEAD(&gss->sns_endpoints);
|
||||
INIT_LLIST_HEAD(&gss->binds);
|
||||
INIT_LLIST_HEAD(&gss->procedures);
|
||||
|
||||
return fi;
|
||||
err:
|
||||
|
|
|
@ -101,6 +101,7 @@ static const struct value_string gprs_ns_timer_strs[] = {
|
|||
{ 7, "tsns-prov" },
|
||||
{ 8, "tsns-size-retries" },
|
||||
{ 9, "tsns-config-retries" },
|
||||
{10, "tsns-procedures-retries" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ OsmoNSdummy(config)# list
|
|||
OsmoNSdummy(config)# ns
|
||||
OsmoNSdummy(config-ns)# list
|
||||
...
|
||||
timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries) <0-65535>
|
||||
timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries|tsns-procedures-retries) <0-65535>
|
||||
nse <0-65535> [ip-sns-role-sgsn]
|
||||
no nse <0-65535>
|
||||
bind (fr|udp) ID
|
||||
|
|
Loading…
Reference in New Issue