From 4f1128fcbd462fc5b7fe900d2daa09576e308736 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Wed, 20 Jan 2021 17:42:48 +0100 Subject: gprs_ns2: inform the NS user (BSSGP) about the MTU of a NSE The BSSGP layer needs to know the MTU of the NS UNIDATA payload. The MTU can be 0 if the NSE doesn't contain any NSVC. Every status indication will contain the mtu value. The MTU in the status indication contains the maximum transfer unit of a BSSGP message. From NS side the maximum SDU. Related: OS#4889 Change-Id: I5016b295db6185ec131d83089cf6c806e34ef1b6 --- include/osmocom/gprs/frame_relay.h | 5 +++ include/osmocom/gprs/gprs_ns2.h | 3 ++ src/gb/gprs_ns2.c | 41 ++++++++++++++++++++++--- src/gb/gprs_ns2_fr.c | 1 + src/gb/gprs_ns2_frgre.c | 4 +++ src/gb/gprs_ns2_internal.h | 7 +++++ src/gb/gprs_ns2_udp.c | 4 +++ tests/gb/gprs_ns2_test.c | 62 ++++++++++++++++++++++++++++++++++++++ tests/gb/gprs_ns2_test.ok | 7 +++++ 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 -- cgit v1.2.3