aboutsummaryrefslogtreecommitdiffstats
path: root/src/gb/gprs_ns2_sns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gb/gprs_ns2_sns.c')
-rw-r--r--src/gb/gprs_ns2_sns.c169
1 files changed, 143 insertions, 26 deletions
diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c
index ab8415df..dfa9afe2 100644
--- a/src/gb/gprs_ns2_sns.c
+++ b/src/gb/gprs_ns2_sns.c
@@ -81,6 +81,8 @@ enum gprs_sns_event {
GPRS_SNS_EV_CHANGE_WEIGHT,
GPRS_SNS_EV_NO_NSVC,
GPRS_SNS_EV_NSVC_ALIVE, /*!< a NS-VC became alive */
+ GPRS_SNS_EV_REQ_ADD_BIND,
+ GPRS_SNS_EV_REQ_DELETE_BIND,
};
static const struct value_string gprs_sns_event_names[] = {
@@ -95,6 +97,8 @@ static const struct value_string gprs_sns_event_names[] = {
{ GPRS_SNS_EV_CHANGE_WEIGHT, "CHANGE_WEIGHT" },
{ GPRS_SNS_EV_NO_NSVC, "NO_NSVC" },
{ GPRS_SNS_EV_NSVC_ALIVE, "NSVC_ALIVE"},
+ { GPRS_SNS_EV_REQ_ADD_BIND, "ADD_BIND"},
+ { GPRS_SNS_EV_REQ_DELETE_BIND, "DELETE_BIND"},
{ 0, NULL }
};
@@ -103,6 +107,11 @@ struct sns_endpoint {
struct osmo_sockaddr saddr;
};
+struct ns2_sns_bind {
+ struct llist_head list;
+ struct gprs_ns2_vc_bind *bind;
+};
+
struct ns2_sns_state {
struct gprs_ns2_nse *nse;
@@ -110,6 +119,10 @@ struct ns2_sns_state {
/* holds the list of initial SNS endpoints */
struct llist_head sns_endpoints;
+ /* list of used struct ns2_sns_bind */
+ struct llist_head binds;
+ /* pointer to the bind which was used to initiate the SNS connection */
+ struct ns2_sns_bind *initial_bind;
/* prevent recursive reselection */
bool reselection_running;
@@ -120,8 +133,6 @@ struct ns2_sns_state {
struct sns_endpoint *initial;
/* all SNS PDU will be sent over this nsvc */
struct gprs_ns2_vc *sns_nsvc;
- /* iterate over the binds after all remote has been tested */
- int bind_offset;
/* timer N */
int N;
/* true if at least one nsvc is alive */
@@ -718,7 +729,7 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
struct gprs_ns_ie_ip4_elem *ip4_elems;
struct gprs_ns_ie_ip6_elem *ip6_elems;
struct gprs_ns2_vc_bind *bind;
- struct gprs_ns2_inst *nsi = gss->nse->nsi;
+ struct ns2_sns_bind *sbind;
struct osmo_sockaddr *remote;
const struct osmo_sockaddr *sa;
struct osmo_sockaddr local;
@@ -741,23 +752,25 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
remote = &gss->initial->saddr;
/* count how many bindings are available (only UDP binds) */
- count = ns2_ip_count_bind(nsi, remote);
+ count = llist_count(&gss->binds);
if (count == 0) {
/* TODO: logging */
return;
}
- bind = ns2_ip_get_bind_by_index(nsi, remote, gss->bind_offset);
- if (!bind) {
- if (gss->bind_offset) {
- gss->bind_offset = 0;
- bind = ns2_ip_get_bind_by_index(nsi, remote, gss->bind_offset);
+ /* take the first bind or take the next bind */
+ if (!gss->initial_bind) {
+ gss->initial_bind = llist_first_entry(&gss->binds, struct ns2_sns_bind, list);
+ } else {
+ if (gss->initial_bind->list.next != &gss->binds) {
+ gss->initial_bind = llist_entry(gss->initial_bind->list.next, struct ns2_sns_bind, list);
+ } else {
+ gss->initial_bind = llist_first_entry(&gss->binds, struct ns2_sns_bind, list);
}
-
- if (!bind)
- return;
}
+ bind = gss->initial_bind->bind;
+
/* setup the NSVC */
if (!gss->sns_nsvc) {
gss->sns_nsvc = ns2_ip_bind_connect(bind, gss->nse, remote);
@@ -773,11 +786,8 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
return;
gss->ip4_local = ip4_elems;
-
- llist_for_each_entry(bind, &nsi->binding, list) {
- if (!gprs_ns2_is_ip_bind(bind))
- continue;
-
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ bind = sbind->bind;
sa = gprs_ns2_ip_bind_sockaddr(bind);
if (!sa)
continue;
@@ -813,10 +823,8 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
gss->ip6_local = ip6_elems;
- llist_for_each_entry(bind, &nsi->binding, list) {
- if (!gprs_ns2_is_ip_bind(bind))
- continue;
-
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ bind = sbind->bind;
sa = gprs_ns2_ip_bind_sockaddr(bind);
if (!sa)
continue;
@@ -1409,6 +1417,8 @@ 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 gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct ns2_sns_bind *sbind;
+ struct gprs_ns2_vc *nsvc, *nsvc2;
/* reset when receiving GPRS_SNS_EV_NO_NSVC */
switch (event) {
@@ -1428,19 +1438,16 @@ static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void
ns2_clear_ipv46_entries(gss);
/* Choose the next sns endpoint. */
- if (llist_empty(&gss->sns_endpoints)) {
+ if (llist_empty(&gss->sns_endpoints) || llist_empty(&gss->binds)) {
gss->initial = NULL;
ns2_prim_status_ind(gss->nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_NO_ENDPOINTS);
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 3);
return;
} else if (!gss->initial) {
gss->initial = llist_first_entry(&gss->sns_endpoints, struct sns_endpoint, list);
- gss->bind_offset = 0;
} else if (gss->initial->list.next == &gss->sns_endpoints) {
/* last entry, continue with first */
gss->initial = llist_first_entry(&gss->sns_endpoints, struct sns_endpoint, list);
- gss->bind_offset++;
- gss->bind_offset %= ns2_ip_count_bind(nse->nsi, &gss->initial->saddr);
} else {
/* next element is an entry */
gss->initial = llist_entry(gss->initial->list.next, struct sns_endpoint, list);
@@ -1449,6 +1456,50 @@ static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void
gss->reselection_running = false;
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SIZE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 1);
break;
+ case GPRS_SNS_EV_REQ_ADD_BIND:
+ sbind = data;
+ switch (fi->state) {
+ case GPRS_SNS_ST_UNCONFIGURED:
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_SELECT_ENDPOINT, NULL);
+ break;
+ case GPRS_SNS_ST_SIZE:
+ /* TODO: add the ip4 element to the list */
+ break;
+ case GPRS_SNS_ST_CONFIG_BSS:
+ case GPRS_SNS_ST_CONFIG_SGSN:
+ case GPRS_SNS_ST_CONFIGURED:
+ /* TODO: add to SNS-IP procedure queue & add nsvc() */
+ break;
+ }
+ break;
+ case GPRS_SNS_EV_REQ_DELETE_BIND:
+ sbind = data;
+ switch (fi->state) {
+ case GPRS_SNS_ST_UNCONFIGURED:
+ break;
+ case GPRS_SNS_ST_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);
+ }
+ }
+ break;
+ case GPRS_SNS_ST_CONFIG_BSS:
+ case GPRS_SNS_ST_CONFIG_SGSN:
+ case GPRS_SNS_ST_CONFIGURED:
+ /* TODO: do an delete SNS-IP procedure */
+ /* TODO: remove the ip4 element to the list */
+ llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) {
+ if (nsvc->bind == sbind->bind) {
+ gprs_ns2_free_nsvc(nsvc);
+ }
+ }
+ break;
+ }
+ /* if this is the last bind, the free_nsvc() will trigger a reselection */
+ talloc_free(sbind);
+ break;
}
}
@@ -1457,7 +1508,9 @@ static struct osmo_fsm gprs_ns2_sns_bss_fsm = {
.states = ns2_sns_bss_states,
.num_states = ARRAY_SIZE(ns2_sns_bss_states),
.allstate_event_mask = S(GPRS_SNS_EV_NO_NSVC) |
- S(GPRS_SNS_EV_SELECT_ENDPOINT),
+ S(GPRS_SNS_EV_SELECT_ENDPOINT) |
+ S(GPRS_SNS_EV_REQ_ADD_BIND) |
+ S(GPRS_SNS_EV_REQ_DELETE_BIND),
.allstate_action = ns2_sns_st_all_action,
.cleanup = NULL,
.timer_cb = ns2_sns_fsm_bss_timer_cb,
@@ -1488,6 +1541,7 @@ struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,
fi->priv = gss;
gss->nse = nse;
INIT_LLIST_HEAD(&gss->sns_endpoints);
+ INIT_LLIST_HEAD(&gss->binds);
return fi;
err:
@@ -1810,6 +1864,69 @@ void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bo
}
}
+int gprs_ns2_sns_add_bind(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc_bind *bind)
+{
+ struct ns2_sns_state *gss;
+ struct ns2_sns_bind *tmp;
+
+ OSMO_ASSERT(nse->bss_sns_fi);
+ gss = nse->bss_sns_fi->priv;
+
+ if (!gprs_ns2_is_ip_bind(bind)) {
+ return -EINVAL;
+ }
+
+ if (!llist_empty(&gss->binds)) {
+ llist_for_each_entry(tmp, &gss->binds, list) {
+ if (tmp->bind == bind)
+ return -EALREADY;
+ }
+ }
+
+ tmp = talloc_zero(gss, struct ns2_sns_bind);
+ if (!tmp)
+ return -ENOMEM;
+ tmp->bind = bind;
+ llist_add_tail(&tmp->list, &gss->binds);
+
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_ADD_BIND, tmp);
+ return 0;
+}
+
+/* Remove a bind from the SNS. All assosiated NSVC must be removed. */
+int gprs_ns2_sns_del_bind(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc_bind *bind)
+{
+ struct ns2_sns_state *gss;
+ struct ns2_sns_bind *tmp, *tmp2;
+ bool found = false;
+
+ if (!nse->bss_sns_fi)
+ return -EINVAL;
+
+ gss = nse->bss_sns_fi->priv;
+ if (gss->initial_bind && gss->initial_bind->bind == bind) {
+ if (gss->initial_bind->list.prev == &gss->binds)
+ gss->initial_bind = NULL;
+ else
+ gss->initial_bind = llist_entry(gss->initial_bind->list.prev, struct ns2_sns_bind, list);
+ }
+
+ llist_for_each_entry_safe(tmp, tmp2, &gss->binds, list) {
+ if (tmp->bind == bind) {
+ llist_del(&tmp->list);
+ found = true;
+ }
+ }
+
+ if (!found)
+ return -ENOENT;
+
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_DELETE_BIND, tmp);
+ return 0;
+}
+
/* Update SNS weights
* \param[in] nsvc the NSVC which should be updated
*/