Esempio n. 1
0
int ds_select_dst_impl(struct sip_msg *msg, char *set_, char *alg_, int set_new)
{
	extern int *ds_activelist;
	extern char ***ds_setp_a, ***ds_setp_b;
	extern int *ds_setlen_a, *ds_setlen_b;

	int set, alg;
	unsigned int hash;

	str uri;

	if(msg==NULL)
    {
        LOG(L_ERR, "DISPATCHER:ds_select_dst: bad parameters\n");
        return -1;
    }

	if ( get_int_fparam(&set, msg, (fparam_t*)set_) < 0 ) {
	  LOG(L_ERR, "DISPATCHER:ds_select_dst: bad set value (%d)\n", set);
	  return -1;
	}

	if ( get_int_fparam(&alg, msg, (fparam_t*)alg_) < 0 ) {
	  LOG(L_ERR, "DISPATCHER:ds_select_dst: bad algorithm (%d)\n", alg);
	  return -1;
	}

    if ((set < 0) || (set >= DS_MAX_SETS)) {
        LOG(L_ERR, "DISPATCHER:ds_select_dst: bad set offset (%d)\n", set);
        return -1;
    }
    if ((alg < 0) || (alg > 4)) {
        LOG(L_ERR, "DISPATCHER:ds_select_dst: invalid algorithm\n");
        return -1;
    }

	if (((*ds_activelist == 0) ?  ds_setlen_a[set] : ds_setlen_b[set]) <= 0) {
        LOG(L_ERR, "DISPATCHER:ds_select_dst: empty destination set\n");
        return -1;
    }
    if (msg->dst_uri.s != NULL || msg->dst_uri.len > 0) {
		if (msg->dst_uri.s)
            pkg_free(msg->dst_uri.s);
        msg->dst_uri.s = NULL;
        msg->dst_uri.len = 0;
    }
    /* get hash */
    hash = 0;
    switch (alg) {
		/* see bottom for case '0' */
        case 1: /* hash from uri */
            if (ds_hash_fromuri(msg, &hash) != 0) {
                if (ds_hash_callid(msg, &hash) != 0) {
                    LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot determine from uri hash\n");
                    return -1;
                }
            }
            break;
        case 2: /* hash to uri */
            if (ds_hash_touri(msg, &hash) != 0) {
                if (ds_hash_callid(msg, &hash) != 0) {
                    LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot determine from uri hash\n");
                    return -1;
                }
            }
            break;
        case 3: /* hash Request uri */
            if (ds_hash_ruri(msg, &hash) != 0) {
                if (ds_hash_callid(msg, &hash) != 0) {
                    LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot determine from uri hash\n");
                    return -1;
                }
            }
            break;
		case 4:	/* Call ID hash, shifted right once to skip low bit
				 * This should allow for even distribution when using
				 * Call ID hash twice (i.e. fe + be)
				 */
            if (ds_hash_callid(msg, &hash) != 0) {
                LOG(L_ERR,
                    "DISPATCHER:ds_select_dst: cannot determine callid hash\n");
                hash = 0; /* bad default, just to be sure */
                return -1;
            }
			hash = hash >> 4; /* should be enough for even more backends */
			break;
        case 0: /* hash call id */ /* fall-through */
        default:
            if (ds_hash_callid(msg, &hash) != 0) {
                LOG(L_ERR,
                    "DISPATCHER:ds_select_dst: cannot determine callid hash\n");
                hash = 0; /* bad default, just to be sure */
                return -1;
            }
			break; /* make gcc happy */
    }

    DBG("DISPATCHER:ds_select_dst: hash: [%u]\n", hash);
    /* node list offset from hash */
    if (*ds_activelist == 0) {
        hash = hash % ds_setlen_a[set];
        uri.s = ds_setp_a[set][hash];
        uri.len = strlen(ds_setp_a[set][hash]);
    } else {
        hash = hash % ds_setlen_b[set];
        uri.s = ds_setp_b[set][hash];
        uri.len = strlen(ds_setp_b[set][hash]);
    }

	if (!set_new) {
		if (set_dst_uri(msg, &uri) < 0) {
            LOG(L_ERR,
				"DISPATCHER:dst_select_dst: Error while setting dst_uri\n");
            return -1;
        }
		/* dst_uri changed, so it makes sense to re-use the current uri for
			forking */
		ruri_mark_new(); /* re-use uri for serial forking */
    	DBG("DISPATCHER:ds_select_dst: selected [%d-%d-%d] <%.*s>\n",
        	alg, set, hash, msg->dst_uri.len, msg->dst_uri.s);
	} else {
		if (set_new_uri(msg, &uri) < 0) {
            LOG(L_ERR,
				"DISPATCHER:dst_select_dst: Error while setting new_uri\n");
            return -1;
        }
    	DBG("DISPATCHER:ds_select_dst: selected [%d-%d-%d] <%.*s>\n",
        	alg, set, hash, msg->new_uri.len, msg->dst_uri.s);
    }

    return 1;
}
Esempio n. 2
0
int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode, int max_results)
{
	int i, cnt, i_unwrapped;
	unsigned int ds_hash;
	int_str avp_val;
	int ds_id;
	ds_set_p idx = NULL;
	int inactive_dst_count = 0;
	int destination_entries_to_skip = 0;
	/* used to sort the destinations for LB algo */
	ds_dest_p dest = NULL;
	ds_dest_p selected = NULL;
	static ds_dest_p *sorted_set = NULL;

	if(msg==NULL)
	{
		LM_ERR("bad parameters\n");
		return -1;
	}
	
	if(_ds_list==NULL || _ds_list_nr<=0)
	{
		LM_DBG("empty destination set\n");
		return -1;
	}

	if((mode==0) && (ds_force_dst==0)
			&& (msg->dst_uri.s!=NULL || msg->dst_uri.len>0))
	{
		LM_ERR("destination already set [%.*s]\n", msg->dst_uri.len,
				msg->dst_uri.s);
		return -1;
	}
	

	/* get the index of the set */
	if(ds_get_index(set, &idx)!=0)
	{
		LM_ERR("destination set [%d] not found\n", set);
		return -1;
	}
	
	LM_DBG("set [%d]\n", set);

	ds_hash = 0;
	ds_id = -1;
	switch(alg)
	{
		case 0:
			if(ds_hash_callid(msg, &ds_hash)!=0)
			{
				LM_ERR("can't get callid hash\n");
				return -1;
			}
		break;
		case 1:
			if(ds_hash_fromuri(msg, &ds_hash)!=0)
			{
				LM_ERR("can't get From uri hash\n");
				return -1;
			}
		break;
		case 2:
			if(ds_hash_touri(msg, &ds_hash)!=0)
			{
				LM_ERR("can't get To uri hash\n");
				return -1;
			}
		break;
		case 3:
			if (ds_hash_ruri(msg, &ds_hash)!=0)
			{
				LM_ERR("can't get ruri hash\n");
				return -1;
			}
		break;
		case 4:
			ds_id = idx->last;
			idx->last = (idx->last+1) % idx->nr;
		break;
		case 5:
			i = ds_hash_authusername(msg, &ds_hash);
			switch (i)
			{
				case 0:
					/* Authorization-Header found: Nothing to be done here */
				break;
				case 1:
					/* No Authorization found: Use round robin */
					ds_id = idx->last;
					idx->last = (idx->last+1) % idx->nr;
				break;
				default:
					LM_ERR("can't get authorization hash\n");
					return -1;
				break;
			}
		break;
		case 6:
			ds_hash = rand();
		break;
		case 7:
			if (ds_hash_pvar(msg, &ds_hash)!=0)
			{
				LM_ERR("can't get PV hash\n");
				return -1;
			}
		break;
		case 8:
			ds_id = 0;
		break;
		case 9:
			if (!ds_has_pattern && ds_pattern_suffix.len == 0 ) {
				LM_WARN("no pattern specified - using first entry...\n");
				alg = 8;
				break;
			}
			if ((ds_id = ds_pvar_algo(msg, idx, &sorted_set)) <= 0)
			{
				LM_ERR("can't get destination index\n");
				return -1;
			}
			ds_id = 0;
		break;
		default:
			LM_WARN("algo %d not implemented - using first entry...\n", alg);
			ds_id = 0;
	}

	if (ds_id==-1) {
		/* no destination yet actually selected -> do it based on hash */
		if (idx->weight_sum==0) {
			ds_id = ds_hash % idx->nr;
		} else {
			ds_hash = ds_hash%idx->weight_sum;
			/* get the ds id based on weights */
			for( ds_id=0 ; ds_id<idx->nr ; ds_id++ )
				if (ds_hash<idx->dlist[ds_id].weight)
					break;
		}
	}

	LM_DBG("alg hash [%u], id [%u]\n", ds_hash, ds_id);
	cnt = 0;

	if (alg != 9) {
		i=ds_id;
		while ( idx->dlist[i].flags&(DS_INACTIVE_DST|DS_PROBING_DST) )
		{
			if(ds_use_default!=0) {
				if (idx->nr>1)
					i = (i+1)%(idx->nr-1);
			} else {
				i = (i+1)%idx->nr;
			}
			if(i==ds_id)
			{
				if(ds_use_default!=0)
				{
					i = idx->nr-1;
					if (idx->dlist[i].flags&(DS_INACTIVE_DST|DS_PROBING_DST))
						return -1;
					break;
				} else {
					return -1;
				}
			}
		}
		ds_id = i;
		selected = &idx->dlist[ds_id];
	} else {
		selected = sorted_set[0];
	}

	if(ds_update_dst(msg, &selected->uri, selected->sock, mode)!=0)
	{
		LM_ERR("cannot set dst addr\n");
		return -1;
	}
	/* if alg is round-robin then update the shortcut to next to be used */
	if(alg==4)
		idx->last = (ds_id+1) % idx->nr;
	
	LM_DBG("selected [%d-%d/%d] <%.*s>\n", alg, set, ds_id,
			selected->uri.len, selected->uri.s);

	if(!(ds_flags&DS_FAILOVER_ON))
		goto done;

	/* do some AVP cleanup before start populating new ones */
	destroy_avps( 0 /*all types*/, dst_avp_name, 1 /*all*/);
	destroy_avps( 0 /*all types*/, grp_avp_name, 1 /*all*/);
	destroy_avps( 0 /*all types*/, cnt_avp_name, 1 /*all*/);
	destroy_avps( 0 /*all types*/,sock_avp_name, 1 /*all*/);
	if (attrs_avp_name>0)
		destroy_avps( 0 /*all types*/,attrs_avp_name, 1 /*all*/);


	if(ds_use_default!=0 && ds_id!=idx->nr-1)
	{
		if (push_ds_2_avps( &idx->dlist[idx->nr-1] ) != 0 )
			return -1;
		cnt++;
	}

	inactive_dst_count = count_inactive_destinations(idx);
	/* don't count inactive and default entries into total */
	destination_entries_to_skip = idx->nr - inactive_dst_count - (ds_use_default!=0);
	destination_entries_to_skip -= max_results;

	/* add to avp */

	for(i_unwrapped = ds_id-1+idx->nr; i_unwrapped>ds_id; i_unwrapped--) {
		i = i_unwrapped % idx->nr;
		dest = (alg == 9 ? sorted_set[i] : &idx->dlist[i]);

		if((dest->flags & DS_INACTIVE_DST)
				|| (ds_use_default!=0 && i==(idx->nr-1)))
			continue;
		if(destination_entries_to_skip > 0) {
			LM_DBG("skipped entry [%d/%d] (would create more than %i results)\n", set, i, max_results);
			destination_entries_to_skip--;
			continue;
		}

		LM_DBG("using entry [%d/%d]\n", set, i);
		if (push_ds_2_avps( dest ) != 0 )
			return -1;
		cnt++;
	}

	/* add to avp the first used dst */
	avp_val.s = selected->uri;
	if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
		return -1;
	cnt++;

done:
	if (attrs_avp_name>0) {
		avp_val.s = selected->attrs;
		if(add_avp(AVP_VAL_STR|attrs_avp_type,attrs_avp_name,avp_val)!=0)
			return -1;
	}

	/* add to avp the group id */
	avp_val.n = set;
	if(add_avp(grp_avp_type, grp_avp_name, avp_val)!=0)
		return -1;

	/* add to avp the number of dst */
	avp_val.n = cnt;
	if(add_avp(cnt_avp_type, cnt_avp_name, avp_val)!=0)
		return -1;

	return 1;
}
int ds_select_dst_impl(struct sip_msg *msg, char *set, char *alg, int set_new)
{
	int a, s, idx;
	ds_setidx_p si = NULL;
	unsigned int hash;

	if(msg==NULL)
	{
		LOG(L_ERR, "DISPATCHER:ds_select_dst: bad parameters\n");
		return -1;
	}
	
	if(_ds_list==NULL || _ds_index==NULL)
	{
		LOG(L_ERR, "DISPATCHER:ds_select_dst: no destination sets\n");
		return -1;
	}

	if((force_dst==0) && (msg->dst_uri.s!=NULL || msg->dst_uri.len>0))
	{
		LOG(L_ERR,
			"DISPATCHER:ds_select_dst: destination already set [%.*s]\n",
			msg->dst_uri.len, msg->dst_uri.s);
		return -1;
	}
	
	get_int_fparam(&s, msg, (fparam_t*)set);
	get_int_fparam(&a, msg, (fparam_t*)alg);

	/* get the index of the set */
	si = _ds_index;
	while(si)
	{
		if(si->id == s)
		{
			idx = si->index;
			break;
		}
		si = si->next;
	}

	if(si==NULL)
	{
		LOG(L_ERR,
			"DISPATCHER:ds_select_dst: destination set [%d] not found\n",s);
		return -1;
	}

	DBG("DISPATCHER:ds_select_dst: set index [%d]\n", idx);

	hash = 0;
	switch(a)
	{
		case 0:
			if(ds_hash_callid(msg, &hash)!=0)
			{
				LOG(L_ERR,
					"DISPATCHER:ds_select_dst: can't get callid hash\n");
				return -1;
			}
		break;
		case 1:
			if(ds_hash_fromuri(msg, &hash)!=0)
			{
				LOG(L_ERR,
					"DISPATCHER:ds_select_dst: can't get From uri hash\n");
				return -1;
			}
		break;
		case 2:
			if(ds_hash_touri(msg, &hash)!=0)
			{
				LOG(L_ERR,
					"DISPATCHER:ds_select_dst: can't get To uri hash\n");
				return -1;
			}
		break;
		case 3:
			if (ds_hash_ruri(msg, &hash)!=0)
			{
				LOG(L_ERR,	
					"DISPATCHER:ds_select_dst: can't get ruri hash\n");
				return -1;
			}
		break;
		default:
			LOG(L_WARN,
					"WARNING: ds_select_dst: algo %d not implemented"
					" using callid hashing instead...\n", a);
			hash = 0;
	}

	DBG("DISPATCHER:ds_select_dst: alg hash [%u]\n", hash);

	hash = hash%_ds_list[idx].nr;

	if (!set_new) {
		if (set_dst_uri(msg, &_ds_list[idx].dlist[hash].uri) < 0) {
			LOG(L_ERR, "DISPATCHER:dst_select_dst: Error while setting dst_uri\n");
			return -1;
		}

		DBG("DISPATCHER:ds_select_dst: selected [%d-%d/%d/%d] <%.*s>\n",
				a, s, idx, hash, msg->dst_uri.len, msg->dst_uri.s);
	}
	else {
		if (set_new_uri(msg, &_ds_list[idx].dlist[hash].uri) < 0) {
			LOG(L_ERR, "DISPATCHER:dst_select_dst: Error while setting new_uri\n");
			return -1;
		}
		DBG("DISPATCHER:ds_select_new: selected [%d-%d/%d/%d] <%.*s>\n",
				a, s, idx, hash, msg->new_uri.len, msg->new_uri.s);
	}
	
	return 1;
}
Esempio n. 4
0
int ds_select_dst(struct sip_msg *msg, char *set, char *alg, int mode)
{
	int a, s, idx;
	ds_setidx_p si = NULL;
	unsigned int hash;
	struct action act;

	if(msg==NULL)
	{
		LOG(L_ERR, "DISPATCHER:ds_select_dst: bad parameters\n");
		return -1;
	}
	
	if(_ds_list==NULL || _ds_index==NULL)
	{
		LOG(L_ERR, "DISPATCHER:ds_select_dst: no destination sets\n");
		return -1;
	}

	if((mode==0) && (force_dst==0)
			&& (msg->dst_uri.s!=NULL || msg->dst_uri.len>0))
	{
		LOG(L_ERR,
			"DISPATCHER:ds_select_dst: destination already set [%.*s]\n",
			msg->dst_uri.len, msg->dst_uri.s);
		return -1;
	}
	
	s = (int)(long)set;
	a = (int)(long)alg;

	/* get the index of the set */
	si = _ds_index;
	while(si)
	{
		if(si->id == s)
		{
			idx = si->index;
			break;
		}
		si = si->next;
	}

	if(si==NULL)
	{
		LOG(L_ERR,
			"DISPATCHER:ds_select_dst: destination set [%d] not found\n",s);
		return -1;
	}

	DBG("DISPATCHER:ds_select_dst: set index [%d]\n", idx);

	hash = 0;
	switch(a)
	{
		case 0:
			if(ds_hash_callid(msg, &hash)!=0)
			{
				LOG(L_ERR,
					"DISPATCHER:ds_select_dst: can't get callid hash\n");
				return -1;
			}
		break;
		case 1:
			if(ds_hash_fromuri(msg, &hash)!=0)
			{
				LOG(L_ERR,
					"DISPATCHER:ds_select_dst: can't get From uri hash\n");
				return -1;
			}
		break;
		case 2:
			if(ds_hash_touri(msg, &hash)!=0)
			{
				LOG(L_ERR,
					"DISPATCHER:ds_select_dst: can't get To uri hash\n");
				return -1;
			}
		break;
		case 3:
			if (ds_hash_ruri(msg, &hash)!=0)
			{
				LOG(L_ERR,	
					"DISPATCHER:ds_select_dst: can't get ruri hash\n");
				return -1;
			}
		break;
		case 4:
			hash = _ds_list[idx].last;
			_ds_list[idx].last = (_ds_list[idx].last+1) % _ds_list[idx].nr;
		break;
		default:
			LOG(L_WARN,
					"WARNING: ds_select_dst: algo %d not implemented"
					" - using first entry...\n", a);
			hash = 0;
	}

	DBG("DISPATCHER:ds_select_dst: alg hash [%u]\n", hash);

	hash = hash%_ds_list[idx].nr;
	switch(mode)
	{
		case 1:
			act.type = SET_HOSTPORT_T;
			act.p1_type = STRING_ST;
			if(_ds_list[idx].dlist[hash].uri.len>4 
					&& strncasecmp(_ds_list[idx].dlist[hash].uri.s,"sip:",4)==0)
				act.p1.string = _ds_list[idx].dlist[hash].uri.s+4;
			else
				act.p1.string = _ds_list[idx].dlist[hash].uri.s;
			act.next = 0;
	
			if (do_action(&act, msg) < 0) {
				LOG(L_ERR,
					"DISPATCHER:dst_select_dst: Error while setting host\n");
				return -1;
			}
		break;
		default:
			if (set_dst_uri(msg, &_ds_list[idx].dlist[hash].uri) < 0) {
				LOG(L_ERR,
					"DISPATCHER:dst_select_dst: Error while setting dst_uri\n");
				return -1;
			}
			DBG("DISPATCHER:ds_select_dst: selected [%d-%d/%d/%d] <%.*s>\n",
				a, s, idx, hash, msg->dst_uri.len, msg->dst_uri.s);
	
		break;
	}

	return 1;
}