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