aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/gprs/frame_relay.h5
-rw-r--r--include/osmocom/gprs/gprs_ns2.h3
-rw-r--r--src/gb/gprs_ns2.c41
-rw-r--r--src/gb/gprs_ns2_fr.c1
-rw-r--r--src/gb/gprs_ns2_frgre.c4
-rw-r--r--src/gb/gprs_ns2_internal.h7
-rw-r--r--src/gb/gprs_ns2_udp.c4
-rw-r--r--tests/gb/gprs_ns2_test.c62
-rw-r--r--tests/gb/gprs_ns2_test.ok7
9 files changed, 130 insertions, 4 deletions
diff --git a/include/osmocom/gprs/frame_relay.h b/include/osmocom/gprs/frame_relay.h
index 1be37eec..e3167070 100644
--- a/include/osmocom/gprs/frame_relay.h
+++ b/include/osmocom/gprs/frame_relay.h
@@ -39,6 +39,11 @@ enum osmo_fr_role {
FR_ROLE_NETWORK_EQUIPMENT,
};
+/* 48.016 ยง 6.1.4.2 default maximum information field size of 1600 octets */
+#define FRAME_RELAY_MTU 1600
+/* FR DLC header is 2 byte */
+#define FRAME_RELAY_SDU (FRAME_RELAY_MTU - 2)
+
extern const struct value_string osmo_fr_role_names[];
static inline const char *osmo_fr_role_str(enum osmo_fr_role role) {
diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h
index 07e6c451..b406bb0d 100644
--- a/include/osmocom/gprs/gprs_ns2.h
+++ b/include/osmocom/gprs/gprs_ns2.h
@@ -84,6 +84,7 @@ enum gprs_ns2_affecting_cause {
GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED,
GPRS_NS2_AFF_CAUSE_SNS_FAILURE,
GPRS_NS2_AFF_CAUSE_SNS_NO_ENDPOINTS,
+ GPRS_NS2_AFF_CAUSE_MTU_CHANGE,
};
extern const struct value_string gprs_ns2_aff_cause_prim_strs[];
@@ -135,6 +136,8 @@ struct osmo_gprs_ns2_prim {
/* Only true on the first time it's available.
* Allow the BSSGP layer to reset persistent NSE */
bool first;
+ /* MTU of a NS SDU. It's the lowest MTU of all (alive & dead) NSVCs */
+ uint16_t mtu;
} status;
} u;
};
diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c
index 99a7415c..9302a16f 100644
--- a/src/gb/gprs_ns2.c
+++ b/src/gb/gprs_ns2.c
@@ -217,6 +217,7 @@ const struct value_string gprs_ns2_aff_cause_prim_strs[] = {
{ GPRS_NS2_AFF_CAUSE_RECOVERY, "NSE recovery" },
{ GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED, "NSE SNS configured" },
{ GPRS_NS2_AFF_CAUSE_SNS_FAILURE, "NSE SNS failure" },
+ { GPRS_NS2_AFF_CAUSE_MTU_CHANGE, "NSE MTU changed" },
{ 0, NULL }
};
@@ -496,15 +497,20 @@ void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
nsp.u.status.transfer = ns2_count_transfer_cap(nse, bvci);
nsp.u.status.first = nse->first;
nsp.u.status.persistent = nse->persistent;
+ if (nse->mtu < 4)
+ nsp.u.status.mtu = 0;
+ else
+ nsp.u.status.mtu = nse->mtu - 4; /* 1 Byte NS PDU type, 1 Byte NS SDU control, 2 Byte BVCI */
+
if (nsvc) {
nsp.u.status.nsvc = gprs_ns2_ll_str_buf(nsvc_str, sizeof(nsvc_str), nsvc);
- LOGNSVC(nsvc, LOGL_NOTICE, "NS-STATUS.ind(bvci=%05u): cause=%s, transfer=%d, first=%d\n",
+ LOGNSVC(nsvc, LOGL_NOTICE, "NS-STATUS.ind(bvci=%05u): cause=%s, transfer=%d, first=%d, mtu=%d\n",
nsp.bvci, gprs_ns2_aff_cause_prim_str(nsp.u.status.cause),
- nsp.u.status.transfer, nsp.u.status.first);
+ nsp.u.status.transfer, nsp.u.status.first, nse->mtu);
} else {
- LOGNSE(nse, LOGL_NOTICE, "NS-STATUS.ind(bvci=%05u): cause=%s, transfer=%d, first=%d\n",
+ LOGNSE(nse, LOGL_NOTICE, "NS-STATUS.ind(bvci=%05u): cause=%s, transfer=%d, first=%d, mtu=%d\n",
nsp.bvci, gprs_ns2_aff_cause_prim_str(nsp.u.status.cause),
- nsp.u.status.transfer, nsp.u.status.first);
+ nsp.u.status.transfer, nsp.u.status.first, nse->mtu);
}
osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_STATUS, PRIM_OP_INDICATION, NULL);
@@ -548,6 +554,7 @@ struct gprs_ns2_vc *ns2_vc_alloc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_
llist_add(&nsvc->list, &nse->nsvc);
llist_add(&nsvc->blist, &bind->nsvc);
+ ns2_nse_update_mtu(nse);
return nsvc;
@@ -746,6 +753,7 @@ struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nse
nse->nsei = nsei;
nse->nsi = nsi;
nse->first = true;
+ nse->mtu = 0;
llist_add(&nse->list, &nsi->nse);
INIT_LLIST_HEAD(&nse->nsvc);
@@ -1334,6 +1342,31 @@ static void add_bind_array(struct gprs_ns2_vc_bind **array,
array[i] = bind;
}
+void ns2_nse_update_mtu(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc;
+ int mtu = 0;
+
+ if (llist_empty(&nse->nsvc)) {
+ nse->mtu = 0;
+ return;
+ }
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (mtu == 0)
+ mtu = nsvc->bind->mtu;
+ else if (mtu > nsvc->bind->mtu)
+ mtu = nsvc->bind->mtu;
+ }
+
+ if (nse->mtu == mtu)
+ return;
+
+ nse->mtu = mtu;
+ if (nse->alive)
+ ns2_prim_status_ind(nsvc->nse, NULL, 0, GPRS_NS2_AFF_CAUSE_MTU_CHANGE);
+}
+
/*! calculate the transfer capabilities for a nse
* \param nse the nse to count the transfer capability
* \param bvci a bvci - unused
diff --git a/src/gb/gprs_ns2_fr.c b/src/gb/gprs_ns2_fr.c
index 7e6db2a8..445f00ac 100644
--- a/src/gb/gprs_ns2_fr.c
+++ b/src/gb/gprs_ns2_fr.c
@@ -801,6 +801,7 @@ int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
bind->send_vc = fr_vc_sendmsg;
bind->free_vc = free_vc;
bind->dump_vty = dump_vty;
+ bind->mtu = FRAME_RELAY_SDU;
priv = bind->priv = talloc_zero(bind, struct priv_bind);
if (!priv) {
rc = -ENOMEM;
diff --git a/src/gb/gprs_ns2_frgre.c b/src/gb/gprs_ns2_frgre.c
index 177aeb20..62d87a41 100644
--- a/src/gb/gprs_ns2_frgre.c
+++ b/src/gb/gprs_ns2_frgre.c
@@ -572,6 +572,10 @@ int gprs_ns2_frgre_bind(struct gprs_ns2_inst *nsi,
bind->send_vc = frgre_vc_sendmsg;
bind->free_vc = free_vc;
bind->nsi = nsi;
+ /* TODO: allow to set the MTU via vty. It can not be automatic detected because it goes over an
+ * ethernet device and the MTU here must match the FR side of the FR-to-GRE gateway.
+ */
+ bind->mtu = FRAME_RELAY_SDU;
priv = bind->priv = talloc_zero(bind, struct priv_bind);
if (!priv) {
diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h
index d4764f65..7e235be9 100644
--- a/src/gb/gprs_ns2_internal.h
+++ b/src/gb/gprs_ns2_internal.h
@@ -179,6 +179,9 @@ struct gprs_ns2_nse {
/*! sum of all the signalling weight of _alive_ NS-VCs */
uint32_t sum_sig_weight;
+
+ /*! MTU of a NS PDU. This is the lowest MTU of all NSVCs */
+ uint16_t mtu;
};
/*! Structure representing a single NS-VC */
@@ -244,6 +247,9 @@ struct gprs_ns2_vc_bind {
/*! transfer capability in mbit */
int transfer_capability;
+ /*! MTU of a NS PDU on this bind. */
+ uint16_t mtu;
+
/*! which link-layer are we based on? */
enum gprs_ns2_ll ll;
@@ -298,6 +304,7 @@ void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
uint16_t bvci,
enum gprs_ns2_affecting_cause cause);
void ns2_nse_notify_alive(struct gprs_ns2_vc *nsvc, bool alive);
+void ns2_nse_update_mtu(struct gprs_ns2_nse *nse);
/* message */
int ns2_validate(struct gprs_ns2_vc *nsvc,
diff --git a/src/gb/gprs_ns2_udp.c b/src/gb/gprs_ns2_udp.c
index 0f224583..36f6a97e 100644
--- a/src/gb/gprs_ns2_udp.c
+++ b/src/gb/gprs_ns2_udp.c
@@ -368,6 +368,10 @@ int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
dscp, rc, errno);
}
+ /* IPv4: max fragmented payload can be (13 bit) * 8 byte => 65535.
+ * IPv6: max payload can be 65535 (RFC 2460).
+ * UDP header = 8 byte */
+ bind->mtu = 65535 - 8;
if (result)
*result = bind;
diff --git a/tests/gb/gprs_ns2_test.c b/tests/gb/gprs_ns2_test.c
index 2027e638..6d71a8c6 100644
--- a/tests/gb/gprs_ns2_test.c
+++ b/tests/gb/gprs_ns2_test.c
@@ -40,10 +40,13 @@ int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
static struct log_info info = {};
static struct osmo_wqueue *unitdata = NULL;
+static struct osmo_gprs_ns2_prim last_nse_recovery = {};
static int ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
+ struct osmo_gprs_ns2_prim *nsp;
OSMO_ASSERT(oph->sap == SAP_NS);
+ nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
if (oph->msg) {
if (oph->primitive == GPRS_NS2_PRIM_UNIT_DATA) {
osmo_wqueue_enqueue(unitdata, oph->msg);
@@ -51,6 +54,9 @@ static int ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
msgb_free(oph->msg);
}
}
+ if (oph->primitive == GPRS_NS2_PRIM_STATUS && nsp->u.status.cause == GPRS_NS2_AFF_CAUSE_RECOVERY) {
+ last_nse_recovery = *nsp;
+ }
return 0;
}
@@ -113,6 +119,7 @@ static struct gprs_ns2_vc_bind *dummy_bind(struct gprs_ns2_inst *nsi, const char
bind->transfer_capability = 42;
bind->send_vc = vc_sendmsg;
bind->priv = talloc_zero(bind, struct osmo_wqueue);
+ bind->mtu = 123;
struct osmo_wqueue *queue = bind->priv;
osmo_wqueue_init(queue, 100);
@@ -154,6 +161,7 @@ static struct gprs_ns2_vc_bind *loopback_bind(struct gprs_ns2_inst *nsi, const c
bind->ll = GPRS_NS2_LL_UDP;
bind->transfer_capability = 99;
bind->send_vc = loopback_sendmsg;
+ bind->mtu = 123;
return bind;
}
@@ -362,6 +370,59 @@ void test_unitdata(void *ctx)
printf("--- Finish unitdata test\n");
}
+void test_mtu(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_vc_bind *loopbind;
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[2];
+ struct gprs_ns2_vc *loop[2];
+
+ struct msgb *msg, *other;
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing mtu test\n");
+ osmo_wqueue_clear(unitdata);
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ loopbind = loopback_bind(nsi, "loopback");
+ nse = gprs_ns2_create_nse(nsi, 1004, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ OSMO_ASSERT(nse);
+
+ for (i=0; i<2; i++) {
+ printf("---- Create NSVC[%d]\n", i);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ loop[i] = loopback_nsvc(loopbind, nsvc[i]);
+ OSMO_ASSERT(nsvc[i]);
+ ns2_vc_fsm_start(nsvc[i]);
+ OSMO_ASSERT(!ns2_vc_is_unblocked(nsvc[i]));
+ ns2_tx_reset(loop[i], NS_CAUSE_OM_INTERVENTION);
+ ns2_tx_unblock(loop[i]);
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ }
+
+ /* both nsvcs are unblocked and alive */
+ printf("---- Send a small UNITDATA to NSVC[0]\n");
+ msg = generate_unitdata("test_unitdata");
+ ns2_recv_vc(nsvc[0], msg);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(msg == other);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(NULL == other);
+ msgb_free(msg);
+
+ printf("---- Check if got mtu reported\n");
+ /* 1b NS PDU type, 1b NS SDU control, 2b BVCI */
+ OSMO_ASSERT(last_nse_recovery.u.status.mtu == 123 - 4);
+
+ gprs_ns2_free(nsi);
+ printf("--- Finish unitdata test\n");
+}
int main(int argc, char **argv)
{
@@ -379,6 +440,7 @@ int main(int argc, char **argv)
test_nse_transfer_cap(ctx);
test_block_unblock_nsvc(ctx);
test_unitdata(ctx);
+ test_mtu(ctx);
printf("===== NS2 protocol test END\n\n");
talloc_free(ctx);
diff --git a/tests/gb/gprs_ns2_test.ok b/tests/gb/gprs_ns2_test.ok
index 7d123255..f40579f3 100644
--- a/tests/gb/gprs_ns2_test.ok
+++ b/tests/gb/gprs_ns2_test.ok
@@ -20,5 +20,12 @@
---- Try to receive over blocked NSVC[0]
---- Receive over NSVC[1]
--- Finish unitdata test
+--- Testing mtu test
+---- Create NSE + Binds
+---- Create NSVC[0]
+---- Create NSVC[1]
+---- Send a small UNITDATA to NSVC[0]
+---- Check if got mtu reported
+--- Finish unitdata test
===== NS2 protocol test END