|
|
|
@ -48,6 +48,12 @@ struct streamhandler {
|
|
|
|
|
const struct streamhandler_io *in;
|
|
|
|
|
const struct streamhandler_io *out;
|
|
|
|
|
};
|
|
|
|
|
struct intf_rr {
|
|
|
|
|
struct logical_intf hash_key;
|
|
|
|
|
mutex_t lock;
|
|
|
|
|
GQueue logical_intfs;
|
|
|
|
|
struct logical_intf *singular; // set iff only one is present in the list - no lock needed
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void determine_handler(struct packet_stream *in, const struct packet_stream *out);
|
|
|
|
@ -74,6 +80,9 @@ static int call_savpf2avp_rtcp(str *s, struct packet_stream *, struct stream_fd
|
|
|
|
|
//static int call_savpf2savp_rtcp(str *s, struct packet_stream *);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct logical_intf *__get_logical_interface(const str *name, sockfamily_t *fam);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -256,13 +265,12 @@ static const struct rtpengine_srtp __res_null = {
|
|
|
|
|
static GQueue *__interface_list_for_family(sockfamily_t *fam);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static GHashTable *__logical_intf_name_family_hash;
|
|
|
|
|
static GHashTable *__intf_spec_addr_type_hash;
|
|
|
|
|
static GHashTable *__local_intf_addr_type_hash; // hash of lists
|
|
|
|
|
static GHashTable *__logical_intf_name_family_hash; // name + family -> struct logical_intf
|
|
|
|
|
static GHashTable *__logical_intf_name_family_rr_hash; // name + family -> struct intf_rr
|
|
|
|
|
static GHashTable *__intf_spec_addr_type_hash; // addr + type -> struct intf_spec
|
|
|
|
|
static GHashTable *__local_intf_addr_type_hash; // addr + type -> GList of struct local_intf
|
|
|
|
|
static GQueue __preferred_lists_for_family[__SF_LAST];
|
|
|
|
|
|
|
|
|
|
static __thread unsigned int selection_index = 0;
|
|
|
|
|
static __thread unsigned int selection_count = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* checks for free no_ports on a local interface */
|
|
|
|
@ -272,7 +280,7 @@ static int has_free_ports_loc(struct local_intf *loc, unsigned int num_ports) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (num_ports > loc->spec->port_pool.free_ports) {
|
|
|
|
|
if (num_ports > g_atomic_int_get(&loc->spec->port_pool.free_ports)) {
|
|
|
|
|
ilog(LOG_ERR, "Didn't found %d ports available for %.*s/%s",
|
|
|
|
|
num_ports, loc->logical->name.len, loc->logical->name.s,
|
|
|
|
|
sockaddr_print_buf(&loc->spec->local_address.addr));
|
|
|
|
@ -332,64 +340,49 @@ static int has_free_ports_log_all(struct logical_intf *log, unsigned int num_por
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* run round-robin-calls algorithm */
|
|
|
|
|
static struct logical_intf* run_round_robin_calls(GQueue *q, unsigned int num_ports) {
|
|
|
|
|
static struct logical_intf* run_round_robin_calls(struct intf_rr *rr, unsigned int num_ports) {
|
|
|
|
|
struct logical_intf *log = NULL;
|
|
|
|
|
volatile unsigned int nr_tries = 0;
|
|
|
|
|
unsigned int nr_logs = 0;
|
|
|
|
|
|
|
|
|
|
nr_logs = g_queue_get_length(q);
|
|
|
|
|
mutex_lock(&rr->lock);
|
|
|
|
|
|
|
|
|
|
select_log:
|
|
|
|
|
// choose the next logical interface
|
|
|
|
|
log = g_queue_peek_nth(q, selection_index);
|
|
|
|
|
if (!log) {
|
|
|
|
|
if (selection_index == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
selection_index = 0;
|
|
|
|
|
goto select_log;
|
|
|
|
|
}
|
|
|
|
|
__C_DBG("Trying %d ports on logical interface %.*s", num_ports, log->name.len, log->name.s);
|
|
|
|
|
unsigned int max_tries = rr->logical_intfs.length;
|
|
|
|
|
unsigned int num_tries = 0;
|
|
|
|
|
|
|
|
|
|
// test for free ports for the logical interface
|
|
|
|
|
if(!has_free_ports_log_all(log, num_ports)) {
|
|
|
|
|
// count the logical interfaces tried
|
|
|
|
|
nr_tries++;
|
|
|
|
|
while (num_tries++ < max_tries) {
|
|
|
|
|
log = g_queue_pop_head(&rr->logical_intfs);
|
|
|
|
|
g_queue_push_tail(&rr->logical_intfs, log);
|
|
|
|
|
|
|
|
|
|
// the logical interface selected has no ports available, try another one
|
|
|
|
|
selection_index ++;
|
|
|
|
|
selection_index = selection_index % nr_logs;
|
|
|
|
|
mutex_unlock(&rr->lock);
|
|
|
|
|
|
|
|
|
|
// all the logical interfaces have no ports available
|
|
|
|
|
if (nr_tries == nr_logs) {
|
|
|
|
|
ilog(LOG_ERR, "No logical interface with free ports found; fallback to default behaviour");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
__C_DBG("Trying %d ports on logical interface " STR_FORMAT, num_ports, STR_FMT(&log->name));
|
|
|
|
|
|
|
|
|
|
if (has_free_ports_log_all(log, num_ports))
|
|
|
|
|
goto done;
|
|
|
|
|
log = NULL;
|
|
|
|
|
|
|
|
|
|
goto select_log;
|
|
|
|
|
mutex_lock(&rr->lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__C_DBG("Round Robin Calls algorithm found logical %.*s; count=%u index=%u", log->name.len, log->name.s, selection_count, selection_index);
|
|
|
|
|
mutex_unlock(&rr->lock);
|
|
|
|
|
|
|
|
|
|
// 1 stream => 2 x get_logical_interface calls at offer
|
|
|
|
|
// 2 streams => 4 x get_logical_interface calls at offer
|
|
|
|
|
selection_count ++;
|
|
|
|
|
if (selection_count % (num_ports / 2) == 0) {
|
|
|
|
|
selection_count = 0;
|
|
|
|
|
selection_index ++;
|
|
|
|
|
selection_index = selection_index % nr_logs;
|
|
|
|
|
done:
|
|
|
|
|
if (!log) {
|
|
|
|
|
ilog(LOG_ERR, "No logical interface with free ports found; fallback to default behaviour");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__C_DBG("Round Robin Calls algorithm found logical " STR_FORMAT, STR_FMT(&log->name));
|
|
|
|
|
return log;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 'fam' may only be NULL if 'name' is also NULL
|
|
|
|
|
struct logical_intf *get_logical_interface(const str *name, sockfamily_t *fam, int num_ports) {
|
|
|
|
|
struct logical_intf d, *log = NULL;
|
|
|
|
|
struct logical_intf *log = NULL;
|
|
|
|
|
|
|
|
|
|
__C_DBG("Get logical interface for %d ports", num_ports);
|
|
|
|
|
|
|
|
|
|
if (G_UNLIKELY(!name || !name->s ||
|
|
|
|
|
str_cmp(name, ALGORITHM_ROUND_ROBIN_CALLS) == 0)) {
|
|
|
|
|
|
|
|
|
|
if (G_UNLIKELY(!name || !name->s)) {
|
|
|
|
|
// trivial case: no interface given. just pick one suitable for the address family.
|
|
|
|
|
// always used for legacy TCP and UDP protocols.
|
|
|
|
|
GQueue *q;
|
|
|
|
|
if (fam)
|
|
|
|
|
q = __interface_list_for_family(fam);
|
|
|
|
@ -404,35 +397,46 @@ got_some:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// round-robin-calls behaviour - return next interface with free ports
|
|
|
|
|
if (name && name->s && str_cmp(name, ALGORITHM_ROUND_ROBIN_CALLS) == 0 && num_ports > 0) {
|
|
|
|
|
log = run_round_robin_calls(q, num_ports);
|
|
|
|
|
if (log) {
|
|
|
|
|
__C_DBG("Choose logical interface %.*s because of direction %.*s",
|
|
|
|
|
log->name.len, log->name.s,
|
|
|
|
|
name->len, name->s);
|
|
|
|
|
} else {
|
|
|
|
|
__C_DBG("Choose logical interface NULL because of direction %.*s",
|
|
|
|
|
name->len, name->s);
|
|
|
|
|
}
|
|
|
|
|
return log;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// default behaviour - return first logical interface
|
|
|
|
|
return q->head ? q->head->data : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if round-robin is desired
|
|
|
|
|
struct logical_intf key;
|
|
|
|
|
key.name = *name;
|
|
|
|
|
key.preferred_family = fam;
|
|
|
|
|
struct intf_rr *rr = g_hash_table_lookup(__logical_intf_name_family_rr_hash, &key);
|
|
|
|
|
if (!rr)
|
|
|
|
|
return __get_logical_interface(name, fam);
|
|
|
|
|
if (rr->singular) {
|
|
|
|
|
__C_DBG("Returning non-RR logical interface '" STR_FORMAT "' based on direction '" \
|
|
|
|
|
STR_FORMAT "'",
|
|
|
|
|
STR_FMT(&rr->singular->name),
|
|
|
|
|
STR_FMT(name));
|
|
|
|
|
return rr->singular;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__C_DBG("Running RR interface selection for direction '" STR_FORMAT "'",
|
|
|
|
|
STR_FMT(name));
|
|
|
|
|
|
|
|
|
|
log = run_round_robin_calls(rr, num_ports);
|
|
|
|
|
if (log)
|
|
|
|
|
return log;
|
|
|
|
|
return __get_logical_interface(name, fam);
|
|
|
|
|
}
|
|
|
|
|
static struct logical_intf *__get_logical_interface(const str *name, sockfamily_t *fam) {
|
|
|
|
|
struct logical_intf d, *log = NULL;
|
|
|
|
|
|
|
|
|
|
d.name = *name;
|
|
|
|
|
d.preferred_family = fam;
|
|
|
|
|
|
|
|
|
|
log = g_hash_table_lookup(__logical_intf_name_family_hash, &d);
|
|
|
|
|
if (log) {
|
|
|
|
|
__C_DBG("Choose logical interface %.*s because of direction %.*s",
|
|
|
|
|
log->name.len, log->name.s,
|
|
|
|
|
name->len, name->s);
|
|
|
|
|
__C_DBG("Choose logical interface " STR_FORMAT " because of direction " STR_FORMAT,
|
|
|
|
|
STR_FMT(&log->name),
|
|
|
|
|
STR_FMT(name));
|
|
|
|
|
} else {
|
|
|
|
|
__C_DBG("Choose logical interface NULL because of direction %.*s",
|
|
|
|
|
name->len, name->s);
|
|
|
|
|
__C_DBG("Choose logical interface NULL because of direction " STR_FORMAT,
|
|
|
|
|
STR_FMT(name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return log;
|
|
|
|
@ -482,26 +486,50 @@ int is_local_endpoint(const struct intf_address *addr, unsigned int port) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// called during single-threaded startup only
|
|
|
|
|
static void __add_intf_rr_1(struct logical_intf *lif, str *name_base, sockfamily_t *fam) {
|
|
|
|
|
struct logical_intf key;
|
|
|
|
|
key.name = *name_base;
|
|
|
|
|
key.preferred_family = fam;
|
|
|
|
|
struct intf_rr *rr = g_hash_table_lookup(__logical_intf_name_family_rr_hash, &key);
|
|
|
|
|
if (!rr) {
|
|
|
|
|
rr = g_slice_alloc0(sizeof(*rr));
|
|
|
|
|
rr->hash_key = key;
|
|
|
|
|
mutex_init(&rr->lock);
|
|
|
|
|
g_hash_table_insert(__logical_intf_name_family_rr_hash, &rr->hash_key, rr);
|
|
|
|
|
}
|
|
|
|
|
g_queue_push_tail(&rr->logical_intfs, lif);
|
|
|
|
|
rr->singular = (rr->logical_intfs.length == 1) ? lif : NULL;
|
|
|
|
|
g_hash_table_insert(lif->rr_specs, &rr->hash_key.name, lif);
|
|
|
|
|
}
|
|
|
|
|
static void __add_intf_rr(struct logical_intf *lif, str *name_base, sockfamily_t *fam) {
|
|
|
|
|
__add_intf_rr_1(lif, name_base, fam);
|
|
|
|
|
static str legacy_rr_str = STR_CONST_INIT("round-robin-calls");
|
|
|
|
|
__add_intf_rr_1(lif, &legacy_rr_str, fam);
|
|
|
|
|
}
|
|
|
|
|
static GQueue *__interface_list_for_family(sockfamily_t *fam) {
|
|
|
|
|
return &__preferred_lists_for_family[fam->idx];
|
|
|
|
|
}
|
|
|
|
|
// called during single-threaded startup only
|
|
|
|
|
static void __interface_append(struct intf_config *ifa, sockfamily_t *fam) {
|
|
|
|
|
struct logical_intf *lif;
|
|
|
|
|
GQueue *q;
|
|
|
|
|
struct local_intf *ifc;
|
|
|
|
|
struct intf_spec *spec;
|
|
|
|
|
|
|
|
|
|
lif = get_logical_interface(&ifa->name, fam, 0);
|
|
|
|
|
lif = __get_logical_interface(&ifa->name, fam);
|
|
|
|
|
|
|
|
|
|
if (!lif) {
|
|
|
|
|
lif = g_slice_alloc0(sizeof(*lif));
|
|
|
|
|
lif->name = ifa->name;
|
|
|
|
|
lif->preferred_family = fam;
|
|
|
|
|
lif->addr_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq);
|
|
|
|
|
lif->rr_specs = g_hash_table_new(str_hash, str_equal);
|
|
|
|
|
g_hash_table_insert(__logical_intf_name_family_hash, lif, lif);
|
|
|
|
|
if (ifa->local_address.addr.family == fam) {
|
|
|
|
|
q = __interface_list_for_family(fam);
|
|
|
|
|
g_queue_push_tail(q, lif);
|
|
|
|
|
__add_intf_rr(lif, &ifa->name_base, fam);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -527,6 +555,7 @@ static void __interface_append(struct intf_config *ifa, sockfamily_t *fam) {
|
|
|
|
|
__insert_local_intf_addr_type(&ifc->advertised_address, ifc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// called during single-threaded startup only
|
|
|
|
|
void interfaces_init(GQueue *interfaces) {
|
|
|
|
|
int i;
|
|
|
|
|
GList *l;
|
|
|
|
@ -535,6 +564,7 @@ void interfaces_init(GQueue *interfaces) {
|
|
|
|
|
|
|
|
|
|
/* init everything */
|
|
|
|
|
__logical_intf_name_family_hash = g_hash_table_new(__name_family_hash, __name_family_eq);
|
|
|
|
|
__logical_intf_name_family_rr_hash = g_hash_table_new(__name_family_hash, __name_family_eq);
|
|
|
|
|
__intf_spec_addr_type_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq);
|
|
|
|
|
__local_intf_addr_type_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq);
|
|
|
|
|
|
|
|
|
|