we are working on adding high-availability to our voice application by deploying 2x VE onprem platforms and 2 copies of our apps. Both VE units participate in a single, 2-legged SIP trunk on the fronting SBC:
The SBC uses OPTIONS keepalive to notice if any of the VE platforms are gone due to network/hardware/etc issues. This works fine.
However, our APP is also connecting to backend services via network segments we can’t control, and that can be disrupted (public net). When that happens, we are looking for manners to be able to instruct the connected VE to ‘instruct’ the SBC to not consider that VE for inbound calls, as our app won’t be able to treat it properly.
The way we envisioned doing it dynamically from code is to unregister all inbound numbers from VE, hoping that upon realizing it can’t handle any calls, it would either stop responding to OPTIONS (thus forcing the SBC to remove it from the list of online SIP proxies, achieving our goal), or that it would at least send a failure code without the 180/183 preliminary messages.
Though it does eventually send a 488 Not Acceptable Here, it precedes this with a 180 and 183. Our SBC is fully able to perform alternate routing based on any response code (e.g. for 488) to route it to the other VE, however it can only do so if there are no 180/183 messages preceding 488.
So we seem to be at an impasse. I am happy to open a support case with our SBC vendor too, however their documentation is very explicit:
“If due to an INVITE message the device receives from the proxy a SIP 18x response (e.g., 180 or 183) followed by any failure response (e.g., 400 Not Found), the device does not do alternative routing, but instead terminates the call. This occurs even if the failure response is configured in the associated Alternative Reasons Set.”
Which I assume is probably rooted in some SIP RFC standards (but I don’t know). Anyhow, I assume they will just close my ticket with a “works as designed” comment unless I can come up with any further arguments.
Would there be an alternate way within VE’s realm to achieve our goal?
I can think of a few ideas for you and at least one of these should get you out of the woods.
The first option is to set this in the HmpElementsServer.exe.config file:
<setting name="SessionProgressControlledByApp" serializeAs="String">
This tells HmpElements not to automatically send the “Session Progress” message until explicitly told to do so by the application layer.
By default (without the setting), HmpElements always sends the SESSION PROGRESS for you after it sends a TRYING.
To send the “Session Progress” message by way of your application add this code:
If (ChannelResource is SipChannel sc)
This is usually done at the point that you ‘know’ you want to answer the call BUT IS NOT REQUIRED. If you don’t accept the call, but you answer it instead, the flow would be:
If you do accept the call, the flow would be:
You can also reject the call:
ChannelResource.Disconnect(503); // Send a 503 instead of 488
In this case the call flow would be:
INVITE-TRYING-488 NOT ACCEPTABLE-ACK
In lieu of unregistering your phone numbers with VoiceElements, you may consider this alternative way to alert the SBC that this server is temporarily out of service:
You can programmatically change the HmpElementsServer.exe.config file. The setting I would recommend you altering is the OptionsMaintResponse and you should set it to 486 or 488 or 503 depending on what your SBC wants to see.
In the config there are a few settings related to OPTIONS:
<setting name="OptionsMaintResponse" serializeAs="String">
<setting name="OptionsBusyThreshold" serializeAs="String">
<setting name="OptionsBusyResponse" serializeAs="String">
<setting name="ServerBusyResponse" serializeAs="String">
The OptionsMaintResponse is a way to send back a code for ALL options requests. Until it is set back to 0, the server will always respond with this code (488, 502 or 603 are good examples).
The OptionsBusyThreshold is a way to tell the requesting system that we are full or nearly full of calls. To better explain the OptionsBusyThreshold, the value would be how many trunks/channels need to be available for it to send back a 200. IE if you have 100 ports and you have this set to 10, then at least 10 ports need to be available to send back a 200 OK, otherwise the system sends back the 486 (default) or the “OptionsBusyResponse”.
The OptionsBusyResponse is the response code you want to send back when you are full/or close to full. This allows you to override the default 486.
The SeverBusyResponse is the code to send when an INVITE is presented and there are no more ports available to take a call. (I’m including this just for thoroughness.)
When an OPTIONS request is presented to HmpElements this is the pseudo-logic:
int response = 200; // This is the default 200 OK
if (OptionsMaintResponse > 0)
response = OptionsMaintResponse;
if (OptionsBusyThreshold > 0)
if (OptionsBusyThreshold > AvailableHmpPorts.Count)
response = 486;
if (OptionsBusyResponse > 0)
response = OptionsBusyResponse;
You can use the XDocument class to update the HMPElementsServer.exe.config file. This is the constructor for a class that I use to manipulate the config:
public AppSettingsConfigurator(string fileName, string targetSection)
bool bFound = false;
m_fileName = fileName;
m_document = XDocument.Load(m_fileName);
// Locate the applicationSettings
foreach (XElement config in m_document.Descendants("configuration"))
foreach (XElement apps in config.Descendants("applicationSettings"))
foreach (XElement target in apps.Descendants(targetSection))
bFound = true;
m_targetSection = target;
if (bFound) break;
throw new ApplicationException("Specified file is not recognized as a .config file, " +
"or it has invalid XML or the requested section was not found.");
You can then get the element, change it, and then save the file:
m_targetSection.Descendants("setting").Descendants("OptionsMaintResponse").FirstOrDefault() as XElement;
HMPElements checks for changes in the .config file about every 15 seconds, so it should take hold fairly quickly. Then when you are ready to take calls again, just set this back to ‘0’ (zero).