Change in libosmocore[master]: WIP: gprs_ns2: sns: implement local procedure change weight

lynxis lazus gerrit-no-reply at lists.osmocom.org
Tue Mar 2 09:15:18 UTC 2021


lynxis lazus has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/23187 )


Change subject: WIP: gprs_ns2: sns: implement local procedure change weight
......................................................................

WIP: gprs_ns2: sns: implement local procedure change weight

Change-Id: Icec4dabb46bc198f68f91bfe09ba279fbe68d454
---
M src/gb/gprs_ns2.c
M src/gb/gprs_ns2_internal.h
M src/gb/gprs_ns2_sns.c
3 files changed, 422 insertions(+), 19 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/87/23187/1

diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c
index c56b0b5..e3d3298 100644
--- a/src/gb/gprs_ns2.c
+++ b/src/gb/gprs_ns2.c
@@ -1289,6 +1289,7 @@
 	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;
 }
diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h
index 1bef753..35933aa 100644
--- a/src/gb/gprs_ns2_internal.h
+++ b/src/gb/gprs_ns2_internal.h
@@ -33,8 +33,8 @@
 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"	\
@@ -46,6 +46,7 @@
 	"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
@@ -62,6 +63,7 @@
 	NS_TOUT_TSNS_PROV,
 	NS_TOUT_TSNS_SIZE_RETRIES,
 	NS_TOUT_TSNS_CONFIG_RETRIES,
+	NS_TOUT_TSNS_PROCEDURES_RETRIES,
 };
 
 enum nsvc_timer_mode {
diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c
index 86b4fdf..3517378 100644
--- a/src/gb/gprs_ns2_sns.c
+++ b/src/gb/gprs_ns2_sns.c
@@ -35,6 +35,8 @@
  * - No concurrent dual stack. It supports either IPv4 or IPv6, but not both at the same time.
  * - SNS Add/Change/Delete: Doesn't answer on the same NSVC as received SNS ADD/CHANGE/DELETE PDUs.
  * - SNS Add/Change/Delete: Doesn't communicated the failed IPv4/IPv6 entries on the SNS_ACK.
+ *
+ * -
  */
 
 #include <errno.h>
@@ -67,6 +69,7 @@
 	GPRS_SNS_ST_CONFIG_BSS,		/*!< SNS-CONFIG procedure (BSS->SGSN) ongoing */
 	GPRS_SNS_ST_CONFIG_SGSN,	/*!< SNS-CONFIG procedure (SGSN->BSS) ongoing */
 	GPRS_SNS_ST_CONFIGURED,
+	GPRS_SNS_ST_LOCAL_PROCEDURE,	/*!< in process of a ADD/DEL/UPDATE procedure towards SGSN (BSS->SGSN) */
 };
 
 enum gprs_sns_event {
@@ -79,10 +82,12 @@
 	GPRS_SNS_EV_RX_ADD,
 	GPRS_SNS_EV_RX_DELETE,
 	GPRS_SNS_EV_RX_CHANGE_WEIGHT,
+	GPRS_SNS_EV_RX_ACK,
 	GPRS_SNS_EV_REQ_NO_NSVC,
 	GPRS_SNS_EV_REQ_NSVC_ALIVE,		/*!< a NS-VC became alive */
 	GPRS_SNS_EV_REQ_ADD_BIND,
 	GPRS_SNS_EV_REQ_DELETE_BIND,
+	GPRS_SNS_EV_REQ_CHANGE_WEIGHT,		/*!< a bind changed its weight */
 };
 
 static const struct value_string gprs_sns_event_names[] = {
@@ -95,13 +100,21 @@
 	{ GPRS_SNS_EV_RX_ADD,	    		"RX_ADD" },
 	{ GPRS_SNS_EV_RX_DELETE,		"RX_DELETE" },
 	{ GPRS_SNS_EV_RX_CHANGE_WEIGHT,		"RX_CHANGE_WEIGHT" },
+	{ GPRS_SNS_EV_RX_ACK,			"RX_ACK" },
 	{ GPRS_SNS_EV_REQ_NO_NSVC,		"REQ_NO_NSVC" },
 	{ GPRS_SNS_EV_REQ_NSVC_ALIVE,		"REQ_NSVC_ALIVE"},
 	{ GPRS_SNS_EV_REQ_ADD_BIND,		"REQ_ADD_BIND"},
 	{ GPRS_SNS_EV_REQ_DELETE_BIND,		"REQ_DELETE_BIND"},
+	{ GPRS_SNS_EV_REQ_CHANGE_WEIGHT,	"REQ_UPDATE_WEIGHT"},
 	{ 0, NULL }
 };
 
+enum sns_procedure {
+	SNS_ADD,
+	SNS_DEL,
+	SNS_CHANGE_WEIGHT,
+};
+
 struct sns_endpoint {
 	struct llist_head list;
 	struct osmo_sockaddr saddr;
@@ -112,6 +125,13 @@
 	struct gprs_ns2_vc_bind *bind;
 };
 
+struct ns2_sns_procedure {
+	struct llist_head list;
+	struct gprs_ns2_vc_bind *bind;
+	void *endpoint;
+	enum sns_procedure procedure;
+};
+
 struct ns2_sns_state {
 	struct gprs_ns2_nse *nse;
 
@@ -133,12 +153,20 @@
 	struct sns_endpoint *initial;
 	/* all SNS PDU will be sent over this nsvc */
 	struct gprs_ns2_vc *sns_nsvc;
+
+	/* all pending local procedures*/
+	struct llist_head procedures;
+	/* all current procedure */
+	struct ns2_sns_procedure *current;
+
 	/* timer N */
 	int N;
 	/* true if at least one nsvc is alive */
 	bool alive;
 	/* alive timeout, to ensure at least one NSVC comes after X online */
 	struct osmo_timer_list alive_timeout;
+	/* valid transaction id to use for our procedures */
+	uint8_t trans_id;
 
 	/* local configuration to send to the remote end */
 	struct gprs_ns_ie_ip4_elem *ip4_local;
@@ -163,12 +191,42 @@
 	unsigned int num_ip6_remote;
 };
 
+static void add_local_procedure(struct ns2_sns_state *gss, enum sns_procedure procedure, void *endpoint);
+
 static inline struct gprs_ns2_nse *nse_inst_from_fi(struct osmo_fsm_inst *fi)
 {
 	struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
 	return gss->nse;
 }
 
+static struct gprs_ns_ie_ip4_elem *ip4_elem_by_saddr(struct gprs_ns_ie_ip4_elem *ip4, size_t num, const struct osmo_sockaddr *saddr)
+{
+	if (saddr->u.sa.sa_family != AF_INET)
+		return NULL;
+
+	for (size_t i = 0; i < num; i++) {
+		if (ip4[i].ip_addr == saddr->u.sin.sin_addr.s_addr &&
+				ip4->udp_port == saddr->u.sin.sin_port)
+			return &ip4[i];
+	}
+
+	return NULL;
+}
+
+static struct gprs_ns_ie_ip6_elem *ip6_elem_by_saddr(struct gprs_ns_ie_ip6_elem *ip6, size_t num, const struct osmo_sockaddr *saddr)
+{
+	if (saddr->u.sa.sa_family != AF_INET6)
+		return NULL;
+
+	for (size_t i = 0; i < num; i++) {
+		if (memcmp(&ip6[i].ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr)) == 0 &&
+				ip6->udp_port == saddr->u.sin6.sin6_port)
+			return &ip6[i];
+	}
+
+	return NULL;
+}
+
 /* helper function to compute the sum of all (data or signaling) weights */
 static int ip4_weight_sum(const struct gprs_ns_ie_ip4_elem *ip4, unsigned int num,
 			  bool data_weight)
@@ -283,6 +341,8 @@
 
 static void ns2_clear_ipv46_entries(struct ns2_sns_state *gss)
 {
+	struct ns2_sns_procedure *procedure, *tmp;
+
 	TALLOC_FREE(gss->ip4_local);
 	TALLOC_FREE(gss->ip4_remote);
 	TALLOC_FREE(gss->ip6_local);
@@ -292,6 +352,15 @@
 	gss->num_ip4_remote = 0;
 	gss->num_ip6_local = 0;
 	gss->num_ip6_remote = 0;
+
+	if (gss->current)
+		talloc_free(gss->current);
+	gss->current = 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,
@@ -433,6 +502,47 @@
 	return 0;
 }
 
+/* Add a given local IPv4 element to gprs_sns_state */
+static int add_local_ip4_elem(struct ns2_sns_state *gss, uint32_t ip_addr, uint16_t port)
+{
+	unsigned int i;
+
+	if (gss->num_ip4_local + gss->num_ip4_remote >= gss->num_max_nsvcs)
+		return -NS_CAUSE_INVAL_NR_NS_VC;
+
+	/* check for duplicates */
+	for (i = 0; i < gss->num_ip4_local; i++) {
+		if (memcmp(&gss->ip4_local[i], ip4, sizeof(*ip4)))
+			continue;
+		/* TODO: log message duplicate */
+		/* TODO: check if this is the correct cause code */
+		return -NS_CAUSE_PROTO_ERR_UNSPEC;
+	}
+
+	gss->ip4_local = talloc_realloc(gss, gss->ip4_local, struct gprs_ns_ie_ip4_elem,
+					 gss->num_ip4_local+1);
+	gss->ip4_local[gss->num_ip4_local] = *ip4;
+	gss->num_ip4_local += 1;
+	return 0;
+}
+
+/* Remove a given local IPv4 element from gprs_sns_state */
+static int remove_local_ip4_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip4_elem *ip4)
+{
+	unsigned int i;
+
+	for (i = 0; i < gss->num_ip4_local; i++) {
+		if (memcmp(&gss->ip4_local[i], ip4, sizeof(*ip4)))
+			continue;
+		/* all array elements < i remain as they are; all > i are shifted left by one */
+		memmove(&gss->ip4_local[i], &gss->ip4_local[i+1], gss->num_ip4_local-i-1);
+		gss->num_ip4_local -= 1;
+		return 0;
+	}
+	return -1;
+}
+
+
 /* Add a given remote IPv4 element to gprs_sns_state */
 static int add_remote_ip4_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip4_elem *ip4)
 {
@@ -743,7 +853,7 @@
 	if (old_state != GPRS_SNS_ST_SIZE)
 		gss->N = 0;
 
-
+	gss->trans_id = 0;
 	gss->alive = false;
 	ns2_clear_ipv46_entries(gss);
 
@@ -1149,19 +1259,19 @@
 		if (TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
 			v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
 			num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
-			for ( i = 0; i < num_v4; i++) {
+			for (i = 0; i < num_v4; i++) {
 				rc = do_sns_delete(fi, &v4_list[i], NULL);
 				if (rc < 0) {
 					cause = -rc;
 					/* continue to delete others */
 				}
 			}
+
 			if (cause != 0xff) {
 				/* TODO: create list of not-deleted and return it */
 				ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
 				return;
 			}
-
 		} else if (TLVP_PRESENT(tp, NS_IE_IP_ADDR) && TLVP_LEN(tp, NS_IE_IP_ADDR) == 5) {
 			/* delete all NS-VCs for given IPv4 address */
 			const uint8_t *ie = TLVP_VAL(tp, NS_IE_IP_ADDR);
@@ -1302,6 +1412,7 @@
 static void ns2_sns_st_configured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 {
 	struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+	struct gprs_ns2_inst *nsi = nse_inst_from_fi(fi)->nsi;
 	struct tlv_parsed *tp = data;
 
 	switch (event) {
@@ -1317,13 +1428,103 @@
 	case GPRS_SNS_EV_REQ_NSVC_ALIVE:
 		osmo_timer_del(&gss->alive_timeout);
 		break;
+	case GPRS_SNS_EV_REQ_CHANGE_WEIGHT:
+		osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV], GPRS_SNS_ST_LOCAL_PROCEDURE);
+		break;
 	}
 }
 
 static void ns2_sns_st_configured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
 {
 	struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
-	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);
+}
+
+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;
+	struct ns2_sns_procedure *current;
+
+	if (!gss->current) {
+		gss->current = llist_first_entry_or_null(&gss->procedures, struct ns2_sns_procedure, list);
+		if (!gss->current) {
+			osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);
+			return;
+		}
+		llist_del(&gss->current->list);
+	}
+
+	current = gss->current;
+	switch (current->procedure) {
+	case SNS_ADD:
+		if (gss->ip == IPv4)
+			ns2_tx_sns_add(gss->sns_nsvc, gss->trans_id, current->endpoint, 1, NULL, 0);
+		else
+			ns2_tx_sns_add(gss->sns_nsvc, gss->trans_id, NULL, 0, current->endpoint, 1);
+		break;
+	case SNS_CHANGE_WEIGHT:
+		if (gss->ip == IPv4)
+			ns2_tx_sns_change_weight(gss->sns_nsvc, gss->trans_id, current->endpoint, 1, NULL, 0);
+		else
+			ns2_tx_sns_change_weight(gss->sns_nsvc, gss->trans_id, NULL, 0, current->endpoint, 1);
+		break;
+	case SNS_DEL:
+		if (gss->ip == IPv4)
+			ns2_tx_sns_del(gss->sns_nsvc, gss->trans_id, current->endpoint, 1, NULL, 0);
+		else
+			ns2_tx_sns_del(gss->sns_nsvc, gss->trans_id, NULL, 0, current->endpoint, 1);
+		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_ns2_inst *nsi = nse->nsi;
+	struct tlv_parsed *tp = data;
+	uint8_t trans_id;
+
+	switch (event) {
+	case GPRS_SNS_EV_RX_ADD:
+		ns2_sns_st_configured_add(fi, gss, tp);
+		break;
+	case GPRS_SNS_EV_RX_DELETE:
+		ns2_sns_st_configured_delete(fi, gss, tp);
+		break;
+	case GPRS_SNS_EV_RX_CHANGE_WEIGHT:
+		ns2_sns_st_configured_change(fi, gss, tp);
+		break;
+	case GPRS_SNS_EV_REQ_NSVC_ALIVE:
+		osmo_timer_del(&gss->alive_timeout);
+		break;
+	case GPRS_SNS_EV_RX_ACK:
+		/* the transaction id has been already checked */
+		trans_id = tlvp_val8(tp, NS_IE_TRANS_ID, 0);
+		if (trans_id != gss->trans_id) {
+			LOGPFSML(fi, LOGL_DEBUG, "NSEI=%u Rx SNS ACK with invalid transaction id %d. Valid %d\n",
+				 nse->nsei, trans_id, gss->trans_id);
+			break;
+		}
+
+		if (!TLVP_PRESENT(tp, NS_IE_CAUSE)) {
+			/* everything ok */
+			talloc_free(gss->current);
+			gss->current = NULL;
+			gss->trans_id++;
+			if (llist_empty(&gss->procedures)) {
+				osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);
+			} else {
+				osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
+							nsi->timeout[NS_TOUT_TSNS_PROV], GPRS_SNS_ST_LOCAL_PROCEDURE);
+			}
+			return;
+		}
+
+		/* what happened, when rejected? remove the bind? */
+		break;
+	}
 }
 
 static const struct osmo_fsm_state ns2_sns_bss_states[] = {
@@ -1356,9 +1557,10 @@
 		.in_event_mask = S(GPRS_SNS_EV_RX_CONFIG) |
 				 S(GPRS_SNS_EV_RX_CONFIG_END),
 		.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+				  S(GPRS_SNS_ST_SIZE) |
 				  S(GPRS_SNS_ST_CONFIG_SGSN) |
 				  S(GPRS_SNS_ST_CONFIGURED) |
-				  S(GPRS_SNS_ST_SIZE),
+				  S(GPRS_SNS_ST_LOCAL_PROCEDURE),
 		.name = "CONFIG_SGSN",
 		.action = ns2_sns_st_config_sgsn,
 		.onenter = ns2_sns_st_config_sgsn_onenter,
@@ -1367,13 +1569,30 @@
 		.in_event_mask = S(GPRS_SNS_EV_RX_ADD) |
 				 S(GPRS_SNS_EV_RX_DELETE) |
 				 S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) |
+				 S(GPRS_SNS_EV_REQ_CHANGE_WEIGHT) |
 				 S(GPRS_SNS_EV_REQ_NSVC_ALIVE),
 		.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
-				  S(GPRS_SNS_ST_SIZE),
+				  S(GPRS_SNS_ST_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(GPRS_SNS_EV_RX_ADD) |
+				 S(GPRS_SNS_EV_RX_DELETE) |
+				 S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) |
+				 S(GPRS_SNS_EV_RX_ACK) |
+				 S(GPRS_SNS_EV_REQ_CHANGE_WEIGHT) |
+				 S(GPRS_SNS_EV_REQ_NSVC_ALIVE),
+		.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+				  S(GPRS_SNS_ST_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,
+	},
 };
 
 /* timer to ensure the SNS doesn't configure NSVC, but then no NSVC come alive afterwards */
@@ -1419,16 +1638,93 @@
 			osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIG_SGSN, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
 		}
 		break;
+	case GPRS_SNS_ST_LOCAL_PROCEDURE:
+		if (gss->N >= nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES]) {
+			LOGPFSML(fi, LOGL_ERROR, "NSE %d: SNS Procedure retries failed. Selecting next IP-SNS endpoint.\n", nse->nsei);
+			osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+		} else {
+			osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV],
+						fi->T);
+		}
 	}
 	return 0;
 }
 
+/* create a local endpoint from sns_bind */
+static void *ns2_create_local_endpoint(struct ns2_sns_state *gss, struct ns2_sns_bind *sns_bind)
+{
+	struct osmo_sockaddr *local = NULL, *remote = NULL;
+	const struct osmo_sockaddr *sa;
+	struct gprs_ns_ie_ip4_elem *ip4;
+	struct gprs_ns_ie_ip6_elem *ip6;
+
+	sa = gprs_ns2_ip_bind_sockaddr(sns_bind->bind);
+	if (!sa)
+		return NULL;
+
+	switch (sa->u.sa.sa_family) {
+	case AF_INET:
+		if (sa->u.sin.sin_addr.s_addr == 0) {
+			if (osmo_sockaddr_local_ip(local, &gss->initial->saddr))
+				return NULL;
+			sa = local;
+		}
+
+		ip4 = ip4_elem_by_saddr(gss->ip4_local, gss->num_ip4_local, sa);
+		if (ip4) {
+			return NULL;
+		} else {
+			// TODO: use memmov/realloc
+			add_local_ip4_elem()
+			ip4 = talloc_zero(gss, struct gprs_ns_ie_ip4_elem);
+			if (!ip4)
+				return NULL;
+		}
+
+		ip4->ip_addr = sa->u.sin.sin_addr.s_addr;
+		ip4->udp_port = sa->u.sin.sin_port;
+		ip4->data_weight = sns_bind->bind->sns_data_weight;
+		ip4->sig_weight = sns_bind->bind->sns_sig_weight;
+		// TODO: add to local endpoints
+		return ip4;
+	case AF_INET6:
+		if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sin6.sin6_addr)) {
+			if (osmo_sockaddr_local_ip(local, remote))
+				return NULL;
+			sa = local;
+		}
+
+		ip6 = ip6_elem_by_saddr(gss->ip6_local, gss->num_ip6_local, sa);
+		if (ip6) {
+			return NULL;
+		} else {
+			ip6 = talloc_zero(gss, struct gprs_ns_ie_ip6_elem);
+			if (!ip6)
+				return NULL;
+		}
+
+		memcpy(&ip6->ip_addr, &sa->u.sin6.sin6_addr, sizeof(struct in6_addr));
+		ip6->udp_port = sa->u.sin6.sin6_port;
+		ip6->data_weight = sns_bind->bind->sns_data_weight;
+		ip6->sig_weight = sns_bind->bind->sns_sig_weight;
+		// TODO: add to local endpoints
+		return ip6;
+	default:
+		OSMO_ASSERT(false);
+		break;
+	}
+
+	return NULL;
+}
+
 static void ns2_sns_st_all_action(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 ns2_sns_bind *sbind;
 	struct gprs_ns2_vc *nsvc, *nsvc2;
+	const struct osmo_sockaddr *sa;
+	void *endpoint = NULL;
 
 	/* reset when receiving GPRS_SNS_EV_REQ_NO_NSVC */
 	switch (event) {
@@ -1468,17 +1764,30 @@
 		break;
 	case GPRS_SNS_EV_REQ_ADD_BIND:
 		sbind = data;
+		// free sbind or llist_add_tail(&tmp->list, &gss->binds);
+		// bind to remote
 		switch (fi->state) {
 		case GPRS_SNS_ST_UNCONFIGURED:
+			llist_add_tail(&sbind->list, &gss->binds);
 			osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
 			break;
 		case GPRS_SNS_ST_SIZE:
-			/* TODO: add the ip4 element to the list */
+			endpoint = ns2_create_local_endpoint(gss, sbind);
+			if (!endpoint) {
+				talloc_free(sbind);
+				return;
+			}
+			// TODO add to endpoints
 			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() */
+			endpoint = ns2_create_local_endpoint(gss, sbind);
+			if (!endpoint) {
+				talloc_free(sbind);
+				return;
+			}
+			add_local_procedure(gss, SNS_ADD, endpoint);
 			break;
 		}
 		break;
@@ -1552,6 +1861,7 @@
 	gss->nse = nse;
 	INIT_LLIST_HEAD(&gss->sns_endpoints);
 	INIT_LLIST_HEAD(&gss->binds);
+	INIT_LLIST_HEAD(&gss->procedures);
 	osmo_timer_setup(&gss->alive_timeout, ns2_sns_timeout_alive_cb, fi);
 
 	return fi;
@@ -1610,8 +1920,7 @@
 		osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CHANGE_WEIGHT, tp);
 		break;
 	case SNS_PDUT_ACK:
-		LOGPFSML(fi, LOGL_NOTICE, "NSEI=%u Rx unsupported SNS PDU type %s\n", nsei,
-			 get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
+		osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_ACK, tp);
 		break;
 	default:
 		LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx unknown SNS PDU type %s\n", nsei,
@@ -1844,7 +2153,7 @@
 		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)
@@ -1875,6 +2184,28 @@
 	}
 }
 
+static void add_local_procedure(struct ns2_sns_state *gss, enum sns_procedure procedure, void *endpoint)
+{
+	struct ns2_sns_procedure *sns_proc;
+
+	switch (gss->nse->bss_sns_fi->state) {
+	case GPRS_SNS_ST_UNCONFIGURED:
+	case GPRS_SNS_ST_SIZE:
+		/* we can ignore the procedure as nothing yet configured */
+		return;
+	default:
+		break;
+	}
+
+	sns_proc = talloc_zero(gss, struct ns2_sns_procedure);
+	if (!sns_proc)
+		return;
+
+	sns_proc->endpoint = endpoint;
+	sns_proc->procedure = procedure;
+	llist_add(&sns_proc->list, &gss->procedures);
+}
+
 int gprs_ns2_sns_add_bind(struct gprs_ns2_nse *nse,
 			  struct gprs_ns2_vc_bind *bind)
 {
@@ -1899,15 +2230,13 @@
 	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 gprs_ns2_vc_bind *bind)
 {
 	struct ns2_sns_state *gss;
 	struct ns2_sns_bind *tmp, *tmp2;
@@ -1938,12 +2267,83 @@
 	return 0;
 }
 
-/* Update SNS weights
- * \param[in] nsvc the NSVC which should be updated
+
+/* Update SNS weights for a bind (local endpoint). */
+static void _ns2_sns_update_weights(struct ns2_sns_state *gss, struct ns2_sns_bind *sns_bind) {
+	// find the ip_local entry by using the initial connection for 0.0.0.0
+	struct osmo_sockaddr *local = NULL, *remote = NULL;
+	const struct osmo_sockaddr *sa;
+	struct gprs_ns_ie_ip4_elem *ip4;
+	struct gprs_ns_ie_ip6_elem *ip6;
+
+	sa = gprs_ns2_ip_bind_sockaddr(sns_bind->bind);
+	if (!sa)
+		return;
+
+	switch (sa->u.sa.sa_family) {
+	case AF_INET:
+		if (sa->u.sin.sin_addr.s_addr == 0) {
+			if (osmo_sockaddr_local_ip(local, &gss->initial->saddr))
+				return;
+			ip4 = ip4_elem_by_saddr(gss->ip4_local, gss->num_ip4_local, local);
+		} else {
+			ip4 = ip4_elem_by_saddr(gss->ip4_local, gss->num_ip4_local, sa);
+		}
+
+		/* TODO: check if this bind is about to be added */
+		if (!ip4)
+			return;
+
+		ip4->data_weight = sns_bind->bind->sns_data_weight;
+		ip4->sig_weight = sns_bind->bind->sns_sig_weight;
+		add_local_procedure(gss, SNS_CHANGE_WEIGHT, ip4);
+		osmo_fsm_inst_dispatch(gss->nse->bss_sns_fi, GPRS_SNS_EV_REQ_CHANGE_WEIGHT, ip4);
+		break;
+	case AF_INET6:
+		// in6addr_any
+		if (memcmp(&sa->u.sin6.sin6_addr, &in6addr_any, sizeof(struct in6_addr))) {
+			if (osmo_sockaddr_local_ip(local, remote))
+				return;
+			ip6 = ip6_elem_by_saddr(gss->ip6_local, gss->num_ip6_local, local);
+		} else {
+			ip6 = ip6_elem_by_saddr(gss->ip6_local, gss->num_ip6_local, sa);
+		}
+
+		/* TODO: check if this bind is about to be added */
+		if (!ip6)
+			return;
+
+		ip6->data_weight = sns_bind->bind->sns_data_weight;
+		ip6->sig_weight = sns_bind->bind->sns_sig_weight;
+		add_local_procedure(gss, SNS_CHANGE_WEIGHT, ip6);
+		osmo_fsm_inst_dispatch(gss->nse->bss_sns_fi, GPRS_SNS_EV_REQ_CHANGE_WEIGHT, ip6);
+		break;
+	default:
+		OSMO_ASSERT(false);
+		break;
+	}
+}
+
+/* Update SNS weights for a bind (local endpoint).
+ * \param[in] bind the bind which has been updated
  */
 void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind)
 {
-	/* TODO: implement weights after binds per sns implemented */
+	struct ns2_sns_bind *sns_bind;
+	struct gprs_ns2_nse *nse;
+	struct ns2_sns_state *gss;
+	llist_for_each_entry(nse, &bind->nsi->nse, list) {
+		if (!nse->bss_sns_fi)
+			continue;
+
+		gss = nse->bss_sns_fi->priv;
+		llist_for_each_entry(sns_bind, &gss->binds, list) {
+			if (sns_bind->bind == bind) {
+				_ns2_sns_update_weights(gss, sns_bind);
+				break;
+			}
+		}
+	}
 }
 
 /* initialize osmo_ctx on main tread */

-- 
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/23187
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: Icec4dabb46bc198f68f91bfe09ba279fbe68d454
Gerrit-Change-Number: 23187
Gerrit-PatchSet: 1
Gerrit-Owner: lynxis lazus <lynxis at fe80.eu>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210302/a921d34f/attachment-0001.htm>


More information about the gerrit-log mailing list