Pjsip session timers=forced / always not being sent

running 16.8 cert 3. the session timers setting in pjsip is being ignored by the server and the server isn’t sending INVITE so I set timers=forced but Asterisk doesn’t send the INVITE. Also tried timers=always but still no go. Is this only a problem with tls? I figure the server isn’t working correctly but thought the forced setting should fix.

Unless you have a support contract with Sangoma, try 16.19.1. Cert versions are only for people with paid support, and do not support all features

Only SIP clients can sent INVITE (or re-INVITE)! Are you referring to a Re-INVITE on an incoming call? If so who does the initial client propose as the refresher, and what does Asterisk say in its 200 OK?

Session timers won’t prevent a SIP client sending an initial INVITE.

Generally it would help if you provide “pjsip set logger on” output and the configuration of your endpoint.

No. This is an outbound call with Session-Expires: 600. After 5 minutes Asterisk should send re-invite or update and the server should respond to refresh the timer. After 5 minutes nothing is sent by Asterisk. I haven’t tried changing to udp but I’m pretty sure udp works normally, at least with a different server.

I can’t find primary documentation for the options you mention, but


may help,

I’d still suggest providing logs to make things clear.

OK. I confirmed that the server does not support session timers at all. I correctly get 420 [not supported] when I set “required”. Also, the link you provide says “Always” behaves like “no” [indeed, it does]. So explains why the update / re-invite is never sent. Is there any way to report this to the pjsip group? I’m running the bundled v2.9 but didn’t see anything about this in 2.10.

updated please see sip session timers do not work when timers=always · Issue #2792 · pjsip/pjproject · GitHub and Jira ASTERISK-24910.

I am unable to find the instance where Asterisk sets the pjsip use-timer variable as specified on GitHub

“use-timer” is not an API thing. The configuration code sets extension flags[1]. These are then passed to PJSIP INVITE session[2][3].

[1] asterisk/pjsip_configuration.c at master · asterisk/asterisk · GitHub
[2] asterisk/res_pjsip_session.c at master · asterisk/asterisk · GitHub
[3] asterisk/res_pjsip_session.c at master · asterisk/asterisk · GitHub

Thank you.
Looking at the code then it follows that line 3363 should be the one executed and cause set-timer to ultimately be set, correct? Is there an easy way for me to tell if line 3363 is being executed? Wouldn’t that tell us if pjsip is at fault?

The lines in res_pjsip_session have to be executed, or else calling would not be possible. It’s how you set up a session for calling with the INVITE session code. Without calling those, no calls. It could be Asterisk, if there’s something else required of us to do but I don’t know what that is.

Could someone look at the value of extensions.flags when “timers=always” and timers_handler is complete? Should it be 128 or 130? Jira ASTERISK-24910 seems to elude the value may not be correct [comment by Richard Mudgett]. See sip_inv.h File Reference (2.10) (pjsip.org).

I’m about out of ideas also.

I don’t know off the top of my head what it should be, and I’m not actively investigating this issue.

Just thought I would update in case someone wants to fix the master. I modified the source “always” line in timers_handler to read

endpoint->extensions.flags |= (PJSIP_INV_ALWAYS_USE_TIMER | PJSIP_INV_SUPPORT_TIMER);

and now “always” works as expected.

I would suggest filing an issue for this.

Shouldn’t it read like this (makes more sense to me).timers.diff.txt (1023 Bytes)

hmmm… Your code sets the “support” bit for both “required” and “always”. I’m not sure whether or not that is correct for “required”. I know just the single “required” bit works because I tried it but …
After mulling it over, it makes sense that the support bit be set for both the “required” and the “always” cases. Perhaps the “yes” case should be more correctly called “optional” because [as I understand], in the “yes” case pjsip takes no action if the proxy does not run a timer. Then it follows that in the case of “require”, pjsip would operate normally in the uas mode if the proxy runs timers & return a 420 error if it does not. In that case your code is elegant. :smiley:

I don’t profess to be a “C” programmer but it seems to me that we don’t need the line for “no” and the return -1 would be below the closing bracket?

static int timers_handler(const struct a
 	/* set only the "support" flag [timers=yes] and then check for required or always*/
 	if (ast_true(var->value)) {
 		endpoint->extensions.flags |= PJSIP_INV_SUPPORT_TIMER;
		if (!strcasecmp(var->value, "required")) {
			endpoint->extensions.flags |= PJSIP_INV_REQUIRE_TIMER;
		} else if (!strcasecmp(var->value, "always") || !strcasecmp(var->value, "forced")) {
			endpoint->extensions.flags |= PJSIP_INV_ALWAYS_USE_TIMER;
return -1;  /*note the change*/

The code for required/always/forced would never be executed in that, because ast_true would never return true. The ast_true function takes in common ways of indicating yes (such as “yes”, “1”, “true”) and returns 1 if those are present.

My bust. I left out that line. Note that I changed the return to zero per the C spec.

static int timers_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
struct ast_sip_endpoint *endpoint = obj;
	/* clear all */
    if (ast_true(var->value)) {
        /* timers != no so set only the "support" bit for now] */
		endpoint->extensions.flags |= PJSIP_INV_SUPPORT_TIMER;
		if (!strcasecmp(var->value, "required")) {
			endpoint->extensions.flags |= PJSIP_INV_REQUIRE_TIMER;
		} else if (!strcasecmp(var->value, "always") || !strcasecmp(var->value, "forced")) {
			endpoint->extensions.flags |= PJSIP_INV_ALWAYS_USE_TIMER;
	return 0

Does that actually work? Because it shouldn’t per my previous comment. You’ve now also made it so that invalid option values are now just silently accepted, and no error or warning is provided, which is not good.

I get what you’re saying about the code accepting invalid values. Good catch. Oh well, I’ll leave my original code alone for now. Thanks