int ds_next_dst(struct sip_msg *msg, int mode) { struct socket_info *sock; struct usr_avp *avp; struct usr_avp *tmp_avp; struct usr_avp *attr_avp; int_str avp_value; int_str sock_avp_value; if(!(ds_flags&DS_FAILOVER_ON) || dst_avp_name < 0) { LM_WARN("failover support disabled\n"); return -1; } tmp_avp = search_first_avp(dst_avp_type, dst_avp_name, NULL, 0); if(tmp_avp==NULL) return -1; /* used avp deleted -- strange */ /* get AVP with next destination URI */ avp = search_next_avp(tmp_avp, &avp_value); destroy_avp(tmp_avp); /* remove old attribute AVP (from prev destination) */ if (attrs_avp_name >= 0) { attr_avp = search_first_avp(attrs_avp_type, attrs_avp_name, NULL, 0); if (attr_avp) destroy_avp(attr_avp); } if(avp==NULL || !(avp->flags&AVP_VAL_STR)) return -1; /* no more avps or value is int */ /* get AVP with next destination socket */ tmp_avp = search_first_avp(sock_avp_type, sock_avp_name, &sock_avp_value, 0); if (tmp_avp) { /* this shuold not happen, it is a bogus state */ sock = NULL; } else { if (sscanf( sock_avp_value.s.s, "%p", (void**)&sock ) != 1) sock = NULL; } if(ds_update_dst(msg, &avp_value.s, sock, mode)!=0) { LM_ERR("cannot set dst addr\n"); return -1; } LM_DBG("using [%.*s]\n", avp_value.s.len, avp_value.s.s); return 1; }
static int w_ds_select(struct sip_msg* msg, char* part_set, char* alg, char* max_results_flags, int mode) { int ret = -1; int _ret; int run_prev_ds_select = 0; ds_select_ctl_t prev_ds_select_ctl, ds_select_ctl; char selected_dst_sock_buf[PTR_STRING_SIZE]; /* a hexa string */ ds_selected_dst selected_dst; struct socket_info *sock = NULL; if(msg==NULL) return -1; ds_select_ctl.mode = mode; ds_select_ctl.max_results = 1000; ds_select_ctl.reset_AVP = 1; ds_select_ctl.set_destination = 1; ds_select_ctl.ds_flags = 0; memset(&selected_dst, 0, sizeof(ds_selected_dst)); selected_dst.socket.s = selected_dst_sock_buf; /* Retrieve dispatcher set */ ds_param_t *part_set_param = (ds_param_t*)part_set; if (fixup_get_partition(msg, &part_set_param->partition, &ds_select_ctl.partition) != 0 ||ds_select_ctl.partition == NULL) { LM_ERR("unknown partition\n"); return -1; } int_list_t *set_list = part_set_param->sets; int_list_t *set_list_exp_start = NULL, *set_list_exp_end = NULL; /* Retrieve dispatcher algorithm */ int_list_t *alg_list = (int_list_t *)alg; int_list_t *alg_list_exp_start = NULL, *alg_list_exp_end = NULL; /* In case this parameter is not specified */ max_list_param_p max_param = (max_list_param_p)max_results_flags; str max_list_str; int_list_t *max_list=NULL, *max_list_free; if (max_param && max_param->type == MAX_LIST_TYPE_STR) { max_list = (int_list_t*)max_param->lst.list; } else if (max_param && max_param->type == MAX_LIST_TYPE_PV) { if (pv_printf_s(msg, max_param->lst.elem, &max_list_str) != 0) { LM_ERR("cannot get max list from pv\n"); return -1; } if (set_list_from_string(max_list_str, &max_list) != 0 || max_list == NULL) return -1; } /* Avoid compiler warning */ memset(&prev_ds_select_ctl, 0, sizeof(ds_select_ctl_t)); ds_select_ctl.set_destination = 0; /* Parse the params in reverse order. * We need to runt the first entry last to properly populate ds_select_dst * AVPs. * On the first ds_select_dst run we need to reset AVPs. * On the last ds_select_dst run we need to set destination. */ do { CHECK_AND_EXPAND_LIST(set_list); ds_select_ctl.set = set_list->v.ival; CHECK_AND_EXPAND_LIST(alg_list); ds_select_ctl.alg = alg_list->v.ival; if (max_results_flags) { ds_select_ctl.max_results = max_list->v.ival; ds_select_ctl.ds_flags = max_list->flags; } if (run_prev_ds_select) { LM_DBG("ds_select: %d %d %d %d %d\n", prev_ds_select_ctl.set, prev_ds_select_ctl.alg, prev_ds_select_ctl.max_results, prev_ds_select_ctl.reset_AVP, prev_ds_select_ctl.set_destination); _ret = ds_select_dst(msg, &prev_ds_select_ctl, &selected_dst, prev_ds_select_ctl.ds_flags); if (_ret>=0) ret = _ret; /* stop resetting AVPs. */ ds_select_ctl.reset_AVP = 0; } else { /* Enable running ds_select_dst on next loop. */ run_prev_ds_select = 1; } prev_ds_select_ctl = ds_select_ctl; set_list = set_list->next; alg_list = alg_list->next; if (max_results_flags) { max_list_free = max_list; max_list = max_list->next; if (max_param->type == MAX_LIST_TYPE_PV) pkg_free(max_list_free); } TRY_FREE_EXPANDED_LIST(set_list); TRY_FREE_EXPANDED_LIST(alg_list); } while (set_list && alg_list && (max_results_flags ? max_list : set_list)); if (max_results_flags && max_list != NULL) { LM_ERR("extra max slot(s) and/or flag(s)\n"); ret = -2; goto error; } if (set_list != NULL) { LM_ERR("extra set(s)\n"); ret = -2; goto error; } if (alg_list != NULL) { LM_ERR("extra algorithm(s)\n"); ret = -2; goto error; } /* last ds_select_dst run: setting destination. */ ds_select_ctl.set_destination = 1; LM_DBG("ds_select: %d %d %d %d %d\n", ds_select_ctl.set, ds_select_ctl.alg, ds_select_ctl.max_results, ds_select_ctl.reset_AVP, ds_select_ctl.set_destination); _ret = ds_select_dst(msg, &ds_select_ctl, &selected_dst, ds_select_ctl.ds_flags); if (_ret>=0) { ret = _ret; } else { if (selected_dst.uri.s != NULL) { if (selected_dst.socket.len != 0) { if (sscanf( selected_dst.socket.s, "%p", (void**)&sock ) != 1){ LM_ERR("unable to read forced destination socket\n"); ret = -4; goto error; } } if (ds_update_dst(msg, &selected_dst.uri, sock, ds_select_ctl.mode) != 0) { LM_ERR("cannot set dst addr\n"); ret = -3; goto error; } } else { ret = -1; goto error; } } error: if (selected_dst.uri.s != NULL) pkg_free(selected_dst.uri.s); return ret; }
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; }