Просмотр исходного кода

modules/ims_usrloc_pcscf: added new hash storage based on IP:PORT. backwards compatible with original scheme using new modparam
- new modparam is hashing_type - see module documentation

Jason Penton 11 лет назад
Родитель
Сommit
5b7255d886

+ 50 - 0
modules/ims_usrloc_pcscf/doc/ims_usrloc_pcscf_admin.xml

@@ -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 ([email protected]: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
+      [email protected]: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>

+ 12 - 1
modules/ims_usrloc_pcscf/pcontact.c

@@ -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;
 

+ 3 - 0
modules/ims_usrloc_pcscf/pcontact.h

@@ -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

+ 51 - 4
modules/ims_usrloc_pcscf/udomain.c

@@ -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 */

+ 3 - 1
modules/ims_usrloc_pcscf/ul_mod.c

@@ -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}
 };
 

+ 61 - 0
modules/ims_usrloc_pcscf/usrloc.c

@@ -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;
+}

+ 2 - 0
modules/ims_usrloc_pcscf/usrloc.h

@@ -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