modules/ims_usrloc_pcscf: added new hash storage based on IP:PORT. backwards compatib...
authorJason Penton <jason.penton@gmail.com>
Tue, 11 Feb 2014 05:52:50 +0000 (07:52 +0200)
committerJason Penton <jason.penton@gmail.com>
Tue, 11 Feb 2014 05:52:50 +0000 (07:52 +0200)
- new modparam is hashing_type - see module documentation

modules/ims_usrloc_pcscf/doc/ims_usrloc_pcscf_admin.xml
modules/ims_usrloc_pcscf/pcontact.c
modules/ims_usrloc_pcscf/pcontact.h
modules/ims_usrloc_pcscf/udomain.c
modules/ims_usrloc_pcscf/ul_mod.c
modules/ims_usrloc_pcscf/usrloc.c
modules/ims_usrloc_pcscf/usrloc.h

index 34ff0a9..43c91f8 100644 (file)
@@ -151,6 +151,56 @@ modparam("ims_usrloc_pcscf", "db_url", "dbdriver://username:password@dbhost/dbna
         <programlisting format="linespecific">...
 modparam("ims_usrloc_pcscf", "db_mode", 1)
 ...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title>hashing_type (int)</title>
+
+      <para>This is used to specify how contacts are stored in the internal
+      memory hashing structures. This is an important parameter, not only for
+      efficiency, but also for functionality. IMS can get rather confusing
+      when it comes to contacts, SIP URIs and Implicitly registered SIP URIs
+      (IMPUs). Originally the hash for storage of contacts was performed over
+      the full contact URI viz (27821234567@10.0.0.10:12345;user=phone). This
+      scheme is useful (from a performance perspective) in circumstances where
+      you have many SIP URIs being registered from the same host/port.
+      However, this causes problems in IMS environments where an implicit
+      registration set of IMPU's is implicitly registered on behalf of a UA
+      when it registers. This is because the implicit contact being used in
+      subsequent requests could use a different SIP URI, for example
+      john.doe@10.0.0.10:12345. In this case the P-CSCF would not be able to
+      retrieve the initial contact as the hash over the different contact
+      would in most cases be different. It was therefore proposed to hash the
+      contact by IP:PORT only, effectively identifying a "device" - assuming a
+      1-1 relationship between an IP:PORT pair. In our example, we would get
+      to the same hash slot using the second SIP URI as we got using the
+      initial registered SIP URI. Within this slot we can now search for the
+      appropriate contact (remember there are still collision possibilities)
+      and then traverse through the linked list if iumplcit IMPUs to find the
+      contact currently being used. Of course if it is not found, then you can
+      deny the request.</para>
+
+      <itemizedlist>
+        <listitem>
+          <para>0 - This uses the original hash over AOR method. By default we
+          are backwards compatible...</para>
+        </listitem>
+
+        <listitem>
+          <para>1 - Use the newer hash over IP:PORT.</para>
+        </listitem>
+      </itemizedlist>
+
+      <para><emphasis>Default value is 0.</emphasis></para>
+
+      <example>
+        <title>Set hashing_type parameter</title>
+
+        <programlisting format="linespecific">...
+modparam("ims_usrloc_pcscf", "hashing_type", 1)
+...
 </programlisting>
       </example>
     </section>
index e2d7340..e5acdfc 100644 (file)
@@ -58,6 +58,7 @@
 #include "usrloc_db.h"
 
 extern int db_mode;
+extern int hashing_type;
 
 /*! retransmission detection interval in seconds */
 int cseq_delay = 20;
@@ -114,6 +115,7 @@ int new_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, s
        int i;
        ppublic_t* ppublic_ptr;
        int is_default = 1;
+       str contact_host_port;
 
        *_c = (pcontact_t*)shm_malloc(sizeof(pcontact_t));
        if (*_c == 0) {
@@ -137,7 +139,16 @@ int new_pcontact(struct udomain* _d, str* _contact, struct pcontact_info* _ci, s
        memcpy((*_c)->aor.s, _contact->s, _contact->len);
        (*_c)->aor.len = _contact->len;
        (*_c)->domain = (str*)_d;
-       (*_c)->aorhash = core_hash(_contact, 0, 0);
+
+
+       if ((hashing_type==0) || aor_to_contact(_contact, &contact_host_port) != 0) {
+               if (hashing_type != 0){
+                       LM_DBG("failed to clean contact to host:port, falling back to full AOR - [%.*s]\n", _contact->len, _contact->s);
+               }
+               (*_c)->aorhash = core_hash(_contact, 0, 0);
+       } else {
+               (*_c)->aorhash = core_hash(&contact_host_port, 0, 0);
+       }
        (*_c)->expires = _ci->expires;
        (*_c)->reg_state = _ci->reg_state;
 
index 4d3ad03..0eb542d 100644 (file)
@@ -69,6 +69,9 @@ void mem_delete_ppublic(pcontact_t* _r/*, ucontact_t* _c*/);
 void timer_pcontact(pcontact_t* _r);
 int delete_ppublic(pcontact_t* _r/*, struct ucontact* _c*/);
 int get_ppublic(pcontact_t* _r);
+int aor_to_contact(str* aor, str* contact);
+unsigned int get_hash_slot(udomain_t* _d, str* _aor);
+unsigned int get_aor_hash(udomain_t* _d, str* _aor);
 
 
 #endif
index da7af05..a93b0e2 100644 (file)
@@ -267,7 +267,8 @@ void mem_timer_udomain(udomain_t* _d)
 void lock_udomain(udomain_t* _d, str* _aor)
 {
        unsigned int sl;
-       sl = core_hash(_aor, 0, _d->size);
+
+       sl = get_hash_slot(_d, _aor);
 
 #ifdef GEN_LOCK_T_PREFERED
        lock_get(_d->table[sl].lock);
@@ -279,7 +280,7 @@ void lock_udomain(udomain_t* _d, str* _aor)
 void unlock_udomain(udomain_t* _d, str* _aor)
 {
        unsigned int sl;
-       sl = core_hash(_aor, 0, _d->size);
+       sl = get_hash_slot(_d, _aor);
 #ifdef GEN_LOCK_T_PREFERED
        lock_release(_d->table[sl].lock);
 #else
@@ -440,21 +441,67 @@ error:
 }
 
 int get_pcontact(udomain_t* _d, str* _contact, struct pcontact** _c) {
-       unsigned int sl, i, aorhash;
+       unsigned int sl, i, aorhash, len, len2;
        struct pcontact* c;
+       char *ptr, *ptr2;
+       ppublic_t* impu;
 
        /* search in cache */
-       aorhash = core_hash(_contact, 0, 0);
+       aorhash = get_aor_hash(_d, _contact);
        sl = aorhash & (_d->size - 1);
        c = _d->table[sl].first;
 
        for (i = 0; i < _d->table[sl].n; i++) {
+               LM_DBG("Searching for contact in P-CSCF usrloc [%.*s]\n",
+                               _contact->len,
+                               _contact->s);
+
                if ((c->aorhash == aorhash) && (c->aor.len == _contact->len)
                                && !memcmp(c->aor.s, _contact->s, _contact->len)) {
                        *_c = c;
                        return 0;
                }
 
+               /* hash is correct, but contacts differ. Let's check if maybe the UA is using a different user part
+                * which was part of his implicit set
+                */
+               ptr2 = ptr = _contact->s;
+
+               if ((c->aorhash == aorhash)) {
+                       len2 = len = _contact->len;
+
+                       /* double check domain part is the same - this is to ensure that we don't false match on a collision that has a similar
+                        * userpart in the list of impus... (very unlikely but safer this way).
+                        */
+                       ptr = memchr(_contact->s, '@', _contact->len);
+                       if (ptr) {
+                               len = (ptr - _contact->s);
+                               ptr2 = ptr + 1;
+                               len2 = _contact->len - (ptr2 - _contact->s);
+                       }
+
+                       ptr = memchr(c->aor.s, '@', c->aor.len);
+                       if (!ptr)
+                               ptr = c->aor.s;
+                       else
+                               ptr = ptr + 1;
+
+                       if ((len2 <= c->aor.len) && (memcmp(ptr2, ptr, len2)==0)) {
+                               impu = c->head;
+                               while (impu) {
+                                       LM_DBG("comparing first %d chars of impu [%.*s] for contact [%.*s]\n",
+                                                       len,
+                                                       impu->public_identity.len, impu->public_identity.s,
+                                                       _contact->len, _contact->s);
+                                       if (memcmp(impu->public_identity.s, _contact->s, len) == 0) {
+                                               //match
+                                               *_c = c;
+                                               return 0;
+                                       }
+                                       impu = impu->next;
+                               }
+                       }
+               }
                c = c->next;
        }
        return 1; /* Nothing found */
index fd6aeb2..5d6e11d 100644 (file)
@@ -82,7 +82,8 @@ int init_flag = 0;
 str db_url          = str_init(DEFAULT_DB_URL);        /*!< Database URL */
 int timer_interval  = 60;                                              /*!< Timer interval in seconds */
 int db_mode         = 0;                                               /*!< Database sync scheme: 0-no db, 1-write through, 2-write back, 3-only db */
-int ul_fetch_rows = 2000;
+int ul_fetch_rows      = 2000;
+int hashing_type       = 0;                                            /*!< has type for storing P-CSCF contacts - 0 - use full contact AOR, 1 - use IP:PORT only */
 
 db1_con_t* ul_dbh = 0;
 db_func_t ul_dbf; 
@@ -107,6 +108,7 @@ static param_export_t params[] = {
        {"db_url",              STR_PARAM, &db_url.s        },
        {"timer_interval",      INT_PARAM, &timer_interval  },
        {"db_mode",             INT_PARAM, &db_mode         },
+       {"hashing_type",                INT_PARAM, &hashing_type        },
        {0, 0, 0}
 };
 
index 5d483b5..4a0cba5 100644 (file)
@@ -51,6 +51,7 @@
 #include "ul_mod.h"
 
 extern unsigned int init_flag;
+extern int hashing_type;
 
 int bind_usrloc(usrloc_api_t* api) {
        if (!api) {
@@ -83,3 +84,63 @@ int bind_usrloc(usrloc_api_t* api) {
 
        return 0;
 }
+
+/* function to convert contact aor to only have data after @ - ie strip user part */
+int aor_to_contact(str* aor, str* contact) {
+       char* p;
+       int ret = 0;    //success
+
+       contact->s = aor->s;
+       contact->len = aor->len;
+       if (memcmp(aor->s, "sip:", 4) == 0) {
+               contact->s = aor->s + 4;
+               contact->len-=4;
+       }
+
+       if ((p=memchr(contact->s, '@', contact->len))) {
+               contact->len -= (p - contact->s + 1);
+               contact->s = p+1;
+       }
+
+       if ((p=memchr(contact->s, ';', contact->len))) {
+               contact->len = p - contact->s;
+       }
+
+       if ((p=memchr(contact->s, '>', contact->len))) {
+               contact->len = p - contact->s;
+       }
+
+       return ret;
+}
+
+/* return the slot id for inserting contacts in the hash */
+unsigned int get_hash_slot(udomain_t* _d, str* _aor){
+       str contact;
+       unsigned int sl;
+
+       if ((hashing_type == 0) /*use full AOR for hash*/ || (aor_to_contact(_aor, &contact) != 0)) {
+               if (hashing_type!=0) {
+                       LM_DBG("Unable to get contact host:port from contact header... falling back to full AOR\n");
+               }
+               sl = core_hash(_aor, 0, _d->size);
+       } else {
+               sl = core_hash(&contact, 0, _d->size);
+       }
+
+       return sl;
+}
+
+unsigned int get_aor_hash(udomain_t* _d, str* _aor) {
+       str contact;
+       unsigned int aorhash;
+
+       if ((hashing_type == 0) || (aor_to_contact(_aor, &contact) != 0)) {
+               if (hashing_type !=0) {
+                       LM_DBG("Unable to get contact host:port from contact header... falling back to full AOR\n");
+               }
+               aorhash = core_hash(_aor, 0, 0);
+       } else {
+               aorhash = core_hash(&contact, 0, 0);
+       }
+       return aorhash;
+}
index 8cc8c08..9816ec9 100644 (file)
@@ -239,4 +239,6 @@ typedef struct usrloc_api {
 /*! usrloc API export bind function */
 typedef int (*bind_usrloc_t)(usrloc_api_t* api);
 
+int aor_to_contact(str* aor, str* contact);
+
 #endif