aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2021-03-04 19:49:38 +0100
committerHarald Welte <laforge@osmocom.org>2021-03-24 00:30:23 +0100
commit01fa6a3ef33082b0c177ea2da822633c2fe343eb (patch)
treec7a9c25a015bcac5b85de043893bbdb534a80598 /src
parent24f4df5c460e70a21c9296391f9550dd492dcf75 (diff)
gprs_ns2_sns: Implement checks during processing of inbound SNS-SIZE
As per 3GPP TS 48.016 section 6.2.4.1, we need to perform some consistency checks during the SNS-SIZE procedure. Let's implement them. Change-Id: I1638f04ba45fef3ba0b237948dff6022267141fb Related: OS#3373
Diffstat (limited to 'src')
-rw-r--r--src/gb/gprs_ns2_sns.c60
1 files changed, 59 insertions, 1 deletions
diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c
index 1af96319..11c5a8ea 100644
--- a/src/gb/gprs_ns2_sns.c
+++ b/src/gb/gprs_ns2_sns.c
@@ -750,6 +750,31 @@ static void ns2_sns_st_bss_size(struct osmo_fsm_inst *fi, uint32_t event, void *
}
}
+static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, enum ns2_sns_type stype)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct ns2_sns_bind *sbind;
+ int count = 0;
+
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ const struct osmo_sockaddr *sa = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ if (!sa)
+ continue;
+
+ switch (stype) {
+ case IPv4:
+ if (sa->u.sas.ss_family == AF_INET)
+ count++;
+ break;
+ case IPv6:
+ if (sa->u.sas.ss_family == AF_INET6)
+ count++;
+ break;
+ }
+ }
+ return count;
+}
+
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;
@@ -2019,7 +2044,6 @@ static void ns2_sns_st_sgsn_wait_config_ack_onenter(struct osmo_fsm_inst *fi, ui
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
- ns2_sns_compute_local_ep_from_binds(fi);
/* transmit SGSN-oriented SNS-CONFIG */
ns2_tx_sns_config(gss->sns_nsvc, true, gss->ip4_local, gss->num_ip4_local,
gss->ip6_local, gss->num_ip6_local);
@@ -2123,6 +2147,7 @@ static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event,
{
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
struct tlv_parsed *tp = NULL;
+ size_t num_local_eps, num_remote_eps;
uint8_t flag;
uint8_t cause;
@@ -2144,6 +2169,39 @@ static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event,
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
break;
}
+ if (TLVP_PRES_LEN(tp, NS_IE_IPv4_EP_NR, 2))
+ gss->num_max_ip4_remote = tlvp_val16be(tp, NS_IE_IPv4_EP_NR);
+ if (TLVP_PRES_LEN(tp, NS_IE_IPv6_EP_NR, 2))
+ gss->num_max_ip6_remote = tlvp_val16be(tp, NS_IE_IPv6_EP_NR);
+ /* decide if we go for IPv4 or IPv6 */
+ if (gss->num_max_ip6_remote && ns2_sns_count_num_local_ep(fi, IPv6)) {
+ gss->ip = IPv6;
+ num_local_eps = gss->num_ip6_local;
+ num_remote_eps = gss->num_max_ip6_remote;
+ } else if (gss->num_max_ip4_remote && ns2_sns_count_num_local_ep(fi, IPv4)) {
+ gss->ip = IPv4;
+ num_local_eps = gss->num_ip4_local;
+ num_remote_eps = gss->num_max_ip4_remote;
+ } else {
+ if (gss->num_ip4_local && !gss->num_max_ip4_remote)
+ cause = NS_CAUSE_INVAL_NR_IPv4_EP;
+ else
+ cause = NS_CAUSE_INVAL_NR_IPv6_EP;
+ ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ break;
+ }
+ ns2_sns_compute_local_ep_from_binds(fi);
+ /* ensure number of NS-VCs is sufficient for full mesh */
+ gss->num_max_nsvcs = tlvp_val16be(tp, NS_IE_MAX_NR_NSVC);
+ if (gss->num_max_nsvcs < num_remote_eps * num_local_eps) {
+ LOGPFSML(fi, LOGL_ERROR, "%zu local and %zu remote EPs, requires %zu NS-VC, "
+ "but BSS supports only %zu maximum NS-VCs\n", num_local_eps,
+ num_remote_eps, num_local_eps * num_remote_eps, gss->num_max_nsvcs);
+ cause = NS_CAUSE_INVAL_NR_NS_VC;
+ ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ break;
+ }
+ /* perform state reset, if requested */
flag = *TLVP_VAL(tp, NS_IE_RESET_FLAG);
if (flag & 1) {
struct gprs_ns2_vc *nsvc, *nsvc2;