pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-mgw/+/33312 )
Change subject: mgw: Allow auditing speciall 'null' endpoint ......................................................................
mgw: Allow auditing speciall 'null' endpoint
This is a special endpoint which can always be audited. This is useful for clients who wish to submit requests to osmo-mgw periodically to find out whether the MGW is still reachable. This endpoint will be used by libomso-mgcp-client as default target endpoint to implement such feature. This "null" Endpoint is osmo-mgw specific, not described in MGCP specs.
Related: SYS#6481 Change-Id: Ia409b16e9211e6261e2e0f21288544289d6f3733 --- M doc/manuals/chapters/mgcp_endpoints.adoc M include/osmocom/mgcp/mgcp_endp.h M src/libosmo-mgcp/mgcp_endp.c M src/libosmo-mgcp/mgcp_protocol.c M tests/mgcp/mgcp_test.c M tests/mgcp/mgcp_test.ok 6 files changed, 131 insertions(+), 8 deletions(-)
Approvals: Jenkins Builder: Verified fixeria: Looks good to me, but someone else must approve laforge: Looks good to me, approved
diff --git a/doc/manuals/chapters/mgcp_endpoints.adoc b/doc/manuals/chapters/mgcp_endpoints.adoc index 797fdea..2fb6e16 100644 --- a/doc/manuals/chapters/mgcp_endpoints.adoc +++ b/doc/manuals/chapters/mgcp_endpoints.adoc @@ -91,4 +91,10 @@ not yet useable.
NOTE: the VTY command "show mgcp" can be used to get a list of all available -endpoints (including identifiers) \ No newline at end of file +endpoints (including identifiers) + +=== The `null` endpoint + +OsmoMGW offers a special `null@<domain>` endpoint which can be audited at all times. +This is useful for MGCP clients who wish to submit requests to OsmoMGW +periodically to find out whether it is still reachable and in a working state. \ No newline at end of file diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h index 08b966f..7bf034b 100644 --- a/include/osmocom/mgcp/mgcp_endp.h +++ b/include/osmocom/mgcp/mgcp_endp.h @@ -133,6 +133,7 @@ int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid); void mgcp_endp_update(struct mgcp_endpoint *endp); bool mgcp_endp_is_wildcarded(const char *epname); +bool mgcp_endp_is_null(const char *epname); struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname, const struct mgcp_trunk *trunk); struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname, diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c index 2bf15e0..20088b7 100644 --- a/src/libosmo-mgcp/mgcp_endp.c +++ b/src/libosmo-mgcp/mgcp_endp.c @@ -229,6 +229,17 @@ return false; }
+/*! Check if the given epname refers to a "null" endpoint. + * \param[in] epname endpoint name to check + * \returns true if epname refers to "null"" endpoint, else false. */ +bool mgcp_endp_is_null(const char *epname) +{ + if (strncasecmp(epname, "null@", 5) == 0) + return true; + + return false; +} + /*! Find an endpoint by its name on a specified trunk. * \param[out] cause pointer to store cause code, can be NULL. * \param[in] epname endpoint name to lookup. diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c index 1fa345b..80e0f8a 100644 --- a/src/libosmo-mgcp/mgcp_protocol.c +++ b/src/libosmo-mgcp/mgcp_protocol.c @@ -86,6 +86,9 @@ /* set to true when the request has been classified as wildcarded */ bool wildcarded;
+ /* Set to true when the request is targeted at the "null" endpoint */ + bool null_endp; + /* contains cause code in case of problems during endp/trunk resolution */ int mgcp_cause; }; @@ -390,7 +393,10 @@ /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */ rq.pdata = &pdata; rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname); - rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg); + if (!rq.wildcarded) + rq.null_endp = mgcp_endp_is_null(pdata.epname); + if (!rq.null_endp) + rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg); rq.mgcp_cause = rc; if (!rq.endp) { rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT)); @@ -407,14 +413,14 @@ rq.name, pdata.epname); return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans); } - } else { + } else if (!rq.null_endp) { /* If the endpoint name suggests that the request refers to a specific endpoint, then the * request cannot be handled and we must stop early. */ LOGP(DLMGCP, LOGL_NOTICE, "%s: cannot find endpoint "%s", cause=%d -- abort\n", rq.name, pdata.epname, -rq.mgcp_cause); return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans); - } + } /* else: Handle special "null" endpoint below (with rq.endp=NULL, rq.trunk=NULL) */ } else { osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name)); rq.trunk = rq.endp->trunk; @@ -460,6 +466,11 @@ static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq) { LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n"); + + /* Auditing "null" endpoint is allowed for keepalive purposes. There's no rq->endp nor rq->trunk in this case. */ + if (rq->null_endp) + return create_ok_response(rq->pdata->cfg, NULL, 200, "AUEP", rq->pdata->trans); + if (!rq->endp || !mgcp_endp_avail(rq->endp)) { LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n"); return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans); @@ -855,7 +866,7 @@ struct mgcp_parse_data *pdata = rq->pdata; struct mgcp_trunk *trunk = rq->trunk; struct mgcp_endpoint *endp = rq->endp; - struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group; + struct rate_ctr_group *rate_ctrs; int error_code = 400; const char *local_options = NULL; const char *callid = NULL; @@ -869,6 +880,14 @@
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
+ if (rq->null_endp) { + /* trunk not available so rate_ctr aren't available either. */ + LOGP(DLMGCP, LOGL_ERROR, "CRCX: Not allowed in 'null' endpoint!\n"); + return create_err_response(pdata->cfg, NULL, 502, "CRCX", pdata->trans); + } + + rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group; + /* we must have a free ep */ if (!endp) { rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL)); @@ -1134,7 +1153,7 @@ struct mgcp_parse_data *pdata = rq->pdata; struct mgcp_trunk *trunk = rq->trunk; struct mgcp_endpoint *endp = rq->endp; - struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group; + struct rate_ctr_group *rate_ctrs; char new_local_addr[INET6_ADDRSTRLEN]; int error_code = 500; int silent = 0; @@ -1149,6 +1168,14 @@
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
+ if (rq->null_endp) { + /* trunk not available so rate_ctr aren't available either. */ + LOGP(DLMGCP, LOGL_ERROR, "MDCX: Not allowed in 'null' endpoint!\n"); + return create_err_response(pdata->cfg, NULL, 502, "MDCX", pdata->trans); + } + + rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group; + /* Prohibit wildcarded requests */ if (rq->wildcarded) { LOGPENDP(endp, DLMGCP, LOGL_ERROR, @@ -1360,7 +1387,7 @@ struct mgcp_parse_data *pdata = rq->pdata; struct mgcp_trunk *trunk = rq->trunk; struct mgcp_endpoint *endp = rq->endp; - struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group; + struct rate_ctr_group *rate_ctrs; int error_code = 400; int silent = 0; char *line; @@ -1370,10 +1397,18 @@ unsigned int i;
/* NOTE: In this handler we can not take it for granted that the endp - * pointer will be populated, however a trunk is always guaranteed. */ + * pointer will be populated, however a trunk is always guaranteed (except for 'null' endp). + */
LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
+ if (rq->null_endp) { + /* trunk not available so rate_ctr aren't available either. */ + LOGP(DLMGCP, LOGL_ERROR, "DLCX: Not allowed in 'null' endpoint!\n"); + return create_err_response(pdata->cfg, NULL, 502, "DLCX", pdata->trans); + } + + rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group; if (endp && !mgcp_endp_avail(endp)) { rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL)); LOGPENDP(endp, DLMGCP, LOGL_ERROR, @@ -1524,6 +1559,12 @@
LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
+ if (rq->null_endp) { + /* trunk not available so rate_ctr aren't available either. */ + LOGP(DLMGCP, LOGL_ERROR, "RSIP: Not allowed in 'null' endpoint!\n"); + return create_err_response(rq->pdata->cfg, NULL, 502, "RSIP", rq->pdata->trans); + } + if (rq->pdata->cfg->reset_cb) rq->pdata->cfg->reset_cb(rq->endp->trunk); return NULL; @@ -1549,6 +1590,12 @@
LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
+ if (rq->null_endp) { + /* trunk not available so rate_ctr aren't available either. */ + LOGP(DLMGCP, LOGL_ERROR, "RQNT: Not allowed in 'null' endpoint!\n"); + return create_err_response(rq->pdata->cfg, NULL, 502, "RQNT", rq->pdata->trans); + } + for_each_line(line, rq->pdata->save) { switch (toupper(line[0])) { case 'S': diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c index 2374e91..e37bb57 100644 --- a/tests/mgcp/mgcp_test.c +++ b/tests/mgcp/mgcp_test.c @@ -77,6 +77,8 @@ #define AUEP1_RET "500 158663169 FAIL\r\n" #define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n" #define AUEP2_RET "500 18983213 FAIL\r\n" +#define AUEP_NULL "AUEP 18983215 null@mgw MGCP 1.0\r\n" +#define AUEP_NULL_RET "200 18983215 OK\r\n" #define EMPTY "\r\n" #define EMPTY_RET NULL #define SHORT "CRCX \r\n" @@ -274,6 +276,12 @@
#define MDCX_TOO_LONG_CI_RET "510 18983224 FAIL\r\n"
+#define MDCX_NULL \ + "MDCX 9 null@mgw MGCP 1.0\r\n" \ + "I: %s\n" + +#define MDCX_NULL_RET "502 9 FAIL\r\n" + #define SHORT2 "CRCX 1" #define SHORT2_RET "510 000000 FAIL\r\n" #define SHORT3 "CRCX 1 1@mgw" @@ -392,6 +400,13 @@ #define DLCX_RET_OSMUX DLCX_RET \ "X-Osmo-CP: EC TI=0, TO=0\r\n"
+#define DLCX_NULL \ + "DLCX 8 null@mgw MGCP 1.0\r\n" \ + "I: %s\r\n" \ + "C: 2\r\n" + +#define DLCX_NULL_RET "502 8 FAIL\r\n" + #define RQNT \ "RQNT 186908780 1@mgw MGCP 1.0\r\n" \ "X: B244F267488\r\n" \ @@ -405,6 +420,13 @@ #define RQNT1_RET "200 186908780 OK\r\n" #define RQNT2_RET "200 186908781 OK\r\n"
+#define RQNT_NULL \ + "RQNT 186908782 null@mgw MGCP 1.0\r\n" \ + "X: B244F267488\r\n" \ + "S: D/9\r\n" + +#define RQNT_NULL_RET "502 186908782 FAIL\r\n" + #define PTYPE_IGNORE 0 /* == default initializer */ #define PTYPE_NONE 128 #define PTYPE_NYI PTYPE_NONE @@ -545,6 +567,20 @@ "m=audio 16008 RTP/AVP 0\r\n" \ "a=ptime:20\r\n"
+#define CRCX_NULL \ + "CRCX 2 null@mgw MGCP 1.0\r\n" \ + "m: recvonly\r\n" \ + "C: 2\r\n" \ + "L: p:20\r\n" \ + "\r\n" \ + "v=0\r\n" \ + "c=IN IP4 123.12.12.123\r\n" \ + "m=audio 5904 RTP/AVP 97\r\n" \ + "a=rtpmap:97 GSM-EFR/8000\r\n" \ + "a=ptime:40\r\n" + +#define CRCX_NULL_RET "502 2 FAIL\r\n" + struct mgcp_test { const char *name; const char *req; @@ -586,6 +622,11 @@ {"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97}, {"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET}, {"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET}, + {"AUEP_NULL", AUEP_NULL, AUEP_NULL_RET}, + {"CRCX_NULL", CRCX_NULL, CRCX_NULL_RET}, + {"MDCX_NULL", MDCX_NULL, MDCX_NULL_RET}, + {"DLCX_NULL", DLCX_NULL, DLCX_NULL_RET}, + {"RQNT_NULL", RQNT_NULL, RQNT_NULL_RET}, };
static const struct mgcp_test retransmit[] = { diff --git a/tests/mgcp/mgcp_test.ok b/tests/mgcp/mgcp_test.ok index 08df5d4..d76e455 100644 --- a/tests/mgcp/mgcp_test.ok +++ b/tests/mgcp/mgcp_test.ok Binary files differ