Skip to content

Commit

Permalink
registrar/mid-registrar: Add the "expires_max_deviation" modparam
Browse files Browse the repository at this point in the history
This new modparam could help mitigate the effects of post-restart
"registration storms", when all TCP connections go down and a
significant portion of devices re-REGISTER on the spot.

By randomizing the returned expiry intervals, the REGISTER storm will no
longer repeat at regular intervals following a restart, e.g. every 3600
seconds.
  • Loading branch information
liviuchircu committed Mar 25, 2022
1 parent 9451d6c commit f301667
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 9 deletions.
4 changes: 2 additions & 2 deletions lib/reg/ci.c
Expand Up @@ -132,8 +132,8 @@ ucontact_info_t *pack_ci(struct sip_msg* _m, contact_t* _c, unsigned int _e,
goto error;
}

/* set expire time */
ci.expires = _e;
/* set expire time, with an optional random deviation */
ci.expires = randomize_expires(_e);

if (pn_enable && _reg_flags & REG_SAVE__PN_ON_FLAG) {
ci.flags |= FL_PN_ON;
Expand Down
15 changes: 14 additions & 1 deletion lib/reg/common.c
Expand Up @@ -20,7 +20,8 @@

#include "common.h"

/* modparams */
/* common registrar modparams */
int expires_max_deviation; /*!< +/- max deviation for contact lifetimes */
int max_contacts = 0; /*!< Maximum number of contacts per AOR
(0 == no checking) */
int max_username_len = USERNAME_MAX_SIZE;
Expand All @@ -38,6 +39,18 @@ int reg_init_globals(void)
realm_prefix.len = strlen(realm_prefix.s);
rcv_param.len = strlen(rcv_param.s);

if (expires_max_deviation < 0) {
expires_max_deviation = -expires_max_deviation;
LM_ERR("'expires_max_deviation' cannot be negative, fixing to %d\n",
expires_max_deviation);
}

if (expires_max_deviation > RAND_MAX/2) {
expires_max_deviation = RAND_MAX/2;
LM_ERR("'expires_max_deviation' is too large, fixing to %d\n",
expires_max_deviation);
}

if (max_expires && max_expires < min_expires) {
LM_ERR("max_expires (%d) < min_expires (%d), "
"bumping max_expires up to %d\n", max_expires,
Expand Down
38 changes: 33 additions & 5 deletions lib/reg/common.h
Expand Up @@ -47,20 +47,48 @@ extern usrloc_api_t ul;
extern struct tm_binds tmb;

/* common registrar modparams */
extern int expires_max_deviation;
extern int max_contacts;
extern int max_username_len;
extern int max_domain_len;
extern int max_aor_len;
extern int max_contact_len;

#define reg_modparams \
{"max_contacts", INT_PARAM, &max_contacts}, \
{"max_username_len", INT_PARAM, &max_username_len}, \
{"max_domain_len", INT_PARAM, &max_domain_len}, \
{"max_aor_len", INT_PARAM, &max_aor_len}, \
{"max_contact_len", INT_PARAM, &max_contact_len}
{"max_contacts", INT_PARAM, &max_contacts}, \
{"max_username_len", INT_PARAM, &max_username_len}, \
{"max_domain_len", INT_PARAM, &max_domain_len}, \
{"max_aor_len", INT_PARAM, &max_aor_len}, \
{"max_contact_len", INT_PARAM, &max_contact_len}, \
{"expires_max_deviation", INT_PARAM, &expires_max_deviation}

/* common registrar init code */
int reg_init_globals(void);

static inline time_t randomize_expires(unsigned int expires_ts)
{
time_t ret;

if (!expires_max_deviation)
return expires_ts;

int expires_dur = expires_ts - get_act_time();
int expires_adj = rand() % (expires_max_deviation * 2 + 1)
- expires_max_deviation;

expires_dur += expires_adj;
if (expires_dur < min_expires)
expires_dur = min_expires;

if (max_expires && expires_dur > max_expires)
expires_dur = max_expires;

ret = expires_dur + get_act_time();
LM_DBG("randomized expiry ts from %u to %lu (adj: %d/%d, "
"max_deviation: %d)\n", expires_ts, ret, expires_adj,
(int)ret - (int)expires_ts, expires_max_deviation);

return ret;
}

#endif /* __LIB_REG_COMMON_H__ */
34 changes: 34 additions & 0 deletions lib/reg/doc/reg_modparams.xml
@@ -1,5 +1,39 @@
<!-- Common registrar modparams-->

<section id="param_expires_max_deviation" xreflabel="expires_max_deviation">
<title><varname>expires_max_deviation</varname> (integer)</title>
<para>
Set this parameter in order to add a random +/- deviation up to
and including the given value to the expiration interval of a
newly registered contact. For example, if this parameter is set to
<emphasis>100</emphasis> and a phone registers for 1800 sec, the final
expiry will be random number in the [1700, 1900] interval.
<para>
By randomizing the registration lifetimes of the contacts, the
server is better equipped to deal with a post-restart <emphasis>registration
storm</emphasis>, when all TCP connections are lost and a significant portion of
UAs will re-register at the same time. Thanks to the contact lifetime
randomization, the registration storm will only happen once rather
than, e.g., every 1800 seconds following the restart.
</para>
</para>
<para>
<emphasis>
Default value is 0 (no deviation).
</emphasis>
</para>
<example>
<title>Setting the <varname>expires_max_deviation</varname> parameter</title>
<programlisting format="linespecific">
...
# add a random +/- 0-100 seconds to each registration lifetime
modparam("&reg_module;", "expires_max_deviation", 100)
...
</programlisting>
</example>
</section>


<section id="param_max_contacts" xreflabel="max_contacts">
<title><varname>max_contacts</varname> (integer)</title>
<para>
Expand Down
2 changes: 1 addition & 1 deletion modules/mid_registrar/save.c
Expand Up @@ -1186,7 +1186,7 @@ struct ucontact_info *mid_reg_pack_ci(struct sip_msg *req, struct sip_msg *rpl,
ci.last_modified = get_act_time();
ci.flags = mri->ul_flags;
ci.cflags = mri->cflags;
ci.expires = ctmap->expires + get_act_time();
ci.expires = randomize_expires(ctmap->expires + get_act_time());
ci.shtag = mri->ownership_tag;

ci.q = ctmap->q;
Expand Down

0 comments on commit f301667

Please sign in to comment.